Subversion Repositories fastphp

Rev

Rev 56 | Rev 58 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. unit EditorMain;
  2.  
  3. {$Include 'FastPHP.inc'}
  4.  
  5. (*
  6.   This program requires
  7.   - Microsoft Internet Controls (TWebBrowser)
  8.     If you are using Delphi 10.1 Starter Edition, please import the ActiveX TLB
  9.     "Microsoft Internet Controls"
  10.   - SynEdit
  11.     You can obtain SynEdit via Embarcadero GetIt
  12. *)
  13.  
  14. // TODO: localize
  15. // TODO: wieso geht copy paste im twebbrowser nicht???
  16. // TODO: Wieso dauert webbrowser1 erste kompilierung so lange???
  17. // TODO: wieso kommt syntax fehler zweimal? einmal stderr einmal stdout?
  18. // TODO: Browser titlebar (link preview)
  19. // TODO: "jump to next/prev todo" buttons/shortcuts
  20. // TODO: "increase/decrease indent" buttons/shortcuts
  21.  
  22. // Future ideas
  23. // - code insight
  24. // - verschiedene php versionen?
  25. // - webbrowser1 nur laden, wenn man den tab anwählt?
  26. // - doppelklick auf tab soll diesen schließen
  27. // - Onlinehelp (www) aufrufen
  28. // - Let all colors be adjustable
  29. // - code in bildschirmmitte (horizontal)?
  30.  
  31. interface
  32.  
  33. uses
  34.   // TODO: "{$IFDEF USE_SHDOCVW_TLB}_TLB{$ENDIF}" does not work with Delphi 10.2
  35.   Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  36.   Dialogs, StdCtrls, OleCtrls, ComCtrls, ExtCtrls, ToolWin, IniFiles,
  37.   SynEditHighlighter, SynHighlighterPHP, SynEdit, ShDocVw_TLB, FindReplace,
  38.   ActnList, SynEditMiscClasses, SynEditSearch, RunPHP, ImgList, SynUnicode,
  39.   System.ImageList, System.Actions, Vcl.Menus, SHDocVw;
  40.  
  41. {.$DEFINE OnlineHelp}
  42.  
  43. type
  44.   TForm1 = class(TForm)
  45.     PageControl1: TPageControl;
  46.     PlaintextTabSheet: TTabSheet;
  47.     HtmlTabSheet: TTabSheet;
  48.     Memo2: TMemo;
  49.     WebBrowser1: TWebBrowser;
  50.     Splitter1: TSplitter;
  51.     PageControl2: TPageControl;
  52.     CodeTabsheet: TTabSheet;
  53.     HelpTabsheet: TTabSheet;
  54.     WebBrowser2: TWebBrowser;
  55.     OpenDialog1: TOpenDialog;
  56.     Panel1: TPanel;
  57.     OpenDialog3: TOpenDialog;
  58.     SynEdit1: TSynEdit;
  59.     SynPHPSyn1: TSynPHPSyn;
  60.     Panel2: TPanel;
  61.     SynEditFocusTimer: TTimer;
  62.     Button1: TButton;
  63.     Button2: TButton;
  64.     Button3: TButton;
  65.     Button4: TButton;
  66.     Button5: TButton;
  67.     Button6: TButton;
  68.     ActionList: TActionList;
  69.     ActionFind: TAction;
  70.     ActionReplace: TAction;
  71.     ActionFindNext: TAction;
  72.     ActionGoto: TAction;
  73.     ActionSave: TAction;
  74.     ActionHelp: TAction;
  75.     ActionRun: TAction;
  76.     ActionESC: TAction;
  77.     Button7: TButton;
  78.     ActionOpen: TAction;
  79.     Button8: TButton;
  80.     Button9: TButton;
  81.     ActionFindPrev: TAction;
  82.     Timer1: TTimer;
  83.     ActionSpaceToTab: TAction;
  84.     Button11: TButton;
  85.     SynEditSearch1: TSynEditSearch;
  86.     TreeView1: TTreeView;
  87.     Splitter2: TSplitter;
  88.     btnLint: TButton;
  89.     ActionLint: TAction;
  90.     ImageList1: TImageList;
  91.     RunPopup: TPopupMenu;
  92.     OpeninIDE1: TMenuItem;
  93.     ActionRunConsole: TAction;
  94.     Runinconsole1: TMenuItem;
  95.     SavePopup: TPopupMenu;
  96.     Saveas1: TMenuItem;
  97.     Save1: TMenuItem;
  98.     SaveDialog1: TSaveDialog;
  99.     BtnSpecialChars: TImage;
  100.     BtnSpecialCharsOff: TImage;
  101.     BtnSpecialCharsOn: TImage;
  102.     procedure Run(Sender: TObject);
  103.     procedure RunConsole(Sender: TObject);
  104.     procedure FormShow(Sender: TObject);
  105.     procedure FormCreate(Sender: TObject);
  106.     procedure FormDestroy(Sender: TObject);
  107.     procedure FormClose(Sender: TObject; var Action: TCloseAction);
  108.     procedure PageControl2Changing(Sender: TObject; var AllowChange: Boolean);
  109.     procedure Memo2DblClick(Sender: TObject);
  110.     (*
  111.     {$IFDEF USE_SHDOCVW_TLB}
  112.     *)
  113.     procedure WebBrowser1BeforeNavigate2(ASender: TObject;
  114.       const pDisp: IDispatch; const URL, Flags, TargetFrameName, PostData,
  115.       Headers: OleVariant; var Cancel: WordBool);
  116.     (*
  117.     {$ELSE}
  118.     procedure WebBrowser1BeforeNavigate2(ASender: TObject;
  119.       const pDisp: IDispatch; var URL, Flags, TargetFrameName, PostData,
  120.       Headers: OleVariant; var Cancel: WordBool);
  121.     {$ENDIF}
  122.     *)
  123.     procedure BeforeNavigate(const URL: OleVariant; var Cancel: WordBool);
  124.     procedure SynEditFocusTimerTimer(Sender: TObject);
  125.     procedure ActionFindExecute(Sender: TObject);
  126.     procedure ActionReplaceExecute(Sender: TObject);
  127.     procedure ActionFindNextExecute(Sender: TObject);
  128.     procedure ActionGotoExecute(Sender: TObject);
  129.     procedure ActionSaveExecute(Sender: TObject);
  130.     procedure ActionHelpExecute(Sender: TObject);
  131.     procedure ActionRunExecute(Sender: TObject);
  132.     procedure ActionESCExecute(Sender: TObject);
  133.     procedure SynEdit1MouseWheelDown(Sender: TObject; Shift: TShiftState;
  134.       MousePos: TPoint; var Handled: Boolean);
  135.     procedure SynEdit1MouseWheelUp(Sender: TObject; Shift: TShiftState;
  136.       MousePos: TPoint; var Handled: Boolean);
  137.     procedure ActionOpenExecute(Sender: TObject);
  138.     procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
  139.     procedure Memo2KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  140.     procedure ActionFindPrevExecute(Sender: TObject);
  141.     procedure SynEdit1MouseCursor(Sender: TObject;
  142.       const aLineCharPos: TBufferCoord; var aCursor: TCursor);
  143.     procedure Timer1Timer(Sender: TObject);
  144.     procedure ActionSpaceToTabExecute(Sender: TObject);
  145.     procedure TreeView1DblClick(Sender: TObject);
  146.     procedure SynEdit1GutterClick(Sender: TObject; Button: TMouseButton; X, Y,
  147.       Line: Integer; Mark: TSynEditMark);
  148.     procedure SynEdit1PaintTransient(Sender: TObject; Canvas: TCanvas;
  149.       TransientType: TTransientType);
  150.     procedure ActionLintExecute(Sender: TObject);
  151.     procedure ActionRunConsoleExecute(Sender: TObject);
  152.     procedure SynEdit1Change(Sender: TObject);
  153.     procedure Saveas1Click(Sender: TObject);
  154.     procedure Save1Click(Sender: TObject);
  155.     procedure BtnSpecialCharsClick(Sender: TObject);
  156.   private
  157.     CurSearchTerm: string;
  158.     HlpPrevPageIndex: integer;
  159.     SrcRep: TSynEditFindReplace;
  160.     {$IFDEF OnlineHelp}
  161.     gOnlineHelpWord: string;
  162.     {$ENDIF}
  163.     procedure Help;
  164.     function MarkUpLineReference(cont: string): string;
  165.     function InputRequestCallback(var data: AnsiString): boolean;
  166.     function OutputNotifyCallback(const data: AnsiString): boolean;
  167.   protected
  168.     ChmIndex: TMemIniFile;
  169.     FScrapFile: string;
  170.     FSaveAsFilename: string;
  171.     codeExplorer: TRunCodeExplorer;
  172.     procedure GotoLineNo(LineNo: integer);
  173.     function GetScrapFile: string;
  174.     procedure StartCodeExplorer;
  175.     procedure RefreshModifySign;
  176.   end;
  177.  
  178. var
  179.   Form1: TForm1;
  180.  
  181. implementation
  182.  
  183. {$R *.dfm}
  184.  
  185. {$R Cursors.res}
  186.  
  187. uses
  188.   Functions, StrUtils, WebBrowserUtils, FastPHPUtils, Math, ShellAPI, RichEdit,
  189.   FastPHPTreeView, ImageListEx, FastPHPConfig;
  190.  
  191. const
  192.   crMouseGutter = 1;
  193.  
  194. procedure TForm1.RefreshModifySign;
  195. var
  196.   tmp: string;
  197. begin
  198.   tmp := Caption;
  199.  
  200.   tmp := StringReplace(tmp, '*', '', [rfReplaceAll]);
  201.   if SynEdit1.Modified then tmp := tmp + '*';
  202.  
  203.   if Caption <> tmp then Caption := tmp;
  204. end;
  205.  
  206. procedure TForm1.ActionFindNextExecute(Sender: TObject);
  207. begin
  208.   SrcRep.FindNext;
  209. end;
  210.  
  211. procedure TForm1.ActionFindPrevExecute(Sender: TObject);
  212. begin
  213.   SrcRep.FindPrev;
  214. end;
  215.  
  216. procedure TForm1.ActionGotoExecute(Sender: TObject);
  217. var
  218.   val: string;
  219.   lineno: integer;
  220. begin
  221.   // TODO: VK_LMENU does not work! only works with AltGr but not Alt
  222.   // http://stackoverflow.com/questions/16828250/delphi-xe2-how-to-prevent-the-alt-key-stealing-focus ?
  223.  
  224.   InputQuery('Go to', 'Line number:', val);
  225.   if not TryStrToInt(val, lineno) then
  226.   begin
  227.     if SynEdit1.CanFocus then SynEdit1.SetFocus;
  228.     exit;
  229.   end;
  230.   GotoLineNo(lineno);
  231. end;
  232.  
  233. procedure TForm1.ActionHelpExecute(Sender: TObject);
  234. begin
  235.   Help;
  236.   if PageControl2.ActivePage = HelpTabsheet then
  237.     WebBrowser2.SetFocus
  238.   else if PageControl2.ActivePage = CodeTabsheet then
  239.     SynEdit1.SetFocus;
  240. end;
  241.  
  242. procedure TForm1.ActionLintExecute(Sender: TObject);
  243. begin
  244.   Run(Sender);
  245.   SynEdit1.SetFocus;
  246. end;
  247.  
  248. procedure TForm1.ActionOpenExecute(Sender: TObject);
  249. begin
  250.   If OpenDialog3.Execute then
  251.   begin
  252.     ShellExecute(0, 'open', PChar(ParamStr(0)), PChar(OpenDialog3.FileName), '', SW_NORMAL);
  253.   end;
  254. end;
  255.  
  256. procedure TForm1.ActionReplaceExecute(Sender: TObject);
  257. begin
  258.   SrcRep.ReplaceExecute;
  259. end;
  260.  
  261. procedure TForm1.ActionRunConsoleExecute(Sender: TObject);
  262. begin
  263.   RunConsole(Sender);
  264.   SynEdit1.SetFocus;
  265. end;
  266.  
  267. procedure TForm1.ActionRunExecute(Sender: TObject);
  268. begin
  269.   Run(Sender);
  270.   SynEdit1.SetFocus;
  271. end;
  272.  
  273. procedure TForm1.ActionSaveExecute(Sender: TObject);
  274. begin
  275.   SynEdit1.Lines.SaveToFile(GetScrapFile);
  276.   SynEdit1.Modified := false;
  277.   RefreshModifySign;
  278. end;
  279.  
  280. procedure TForm1.ActionSpaceToTabExecute(Sender: TObject);
  281.  
  282.     function SpacesAtBeginning(line: string): integer;
  283.     begin
  284.       result := 0;
  285.       if Trim(line) = '' then exit;
  286.       while line[result+1] = ' ' do
  287.       begin
  288.         inc(result);
  289.       end;
  290.     end;
  291.  
  292.     function GuessIndent(lines: {$IFDEF UNICODE}TStrings{$ELSE}TUnicodeStrings{$ENDIF}): integer;
  293.       function _Check(indent: integer): boolean;
  294.       var
  295.         i: integer;
  296.       begin
  297.         result := true;
  298.         for i := 0 to lines.Count-1 do
  299.           if SpacesAtBeginning(lines.Strings[i]) mod indent <> 0 then
  300.           begin
  301.             // ShowMessageFmt('Zeile "%s" nicht durch %d teilbar!', [lines.strings[i], indent]);
  302.             result := false;
  303.             exit;
  304.           end;
  305.       end;
  306.     var
  307.       i: integer;
  308.     begin
  309.       for i := 8 downto 2 do
  310.       begin
  311.         if _Check(i) then
  312.         begin
  313.           result := i;
  314.           exit;
  315.         end;
  316.       end;
  317.       result := -1;
  318.     end;
  319.  
  320.     procedure SpaceToTab(lines: {$IFDEF UNICODE}TStrings{$ELSE}TUnicodeStrings{$ENDIF}; indent: integer);
  321.     var
  322.       i, spaces: integer;
  323.     begin
  324.       for i := 0 to lines.Count-1 do
  325.       begin
  326.         spaces := SpacesAtBeginning(lines.Strings[i]);
  327.         lines.Strings[i] := StringOfChar(#9, spaces div indent) + StringOfChar(' ', spaces mod indent) + Copy(lines.Strings[i], spaces+1, Length(lines.Strings[i])-spaces);
  328.       end;
  329.     end;
  330.  
  331.     function SpacesAvailable(lines: {$IFDEF UNICODE}TStrings{$ELSE}TUnicodeStrings{$ENDIF}): boolean;
  332.     var
  333.       i, spaces: integer;
  334.     begin
  335.       for i := 0 to lines.Count-1 do
  336.       begin
  337.         spaces := SpacesAtBeginning(lines.Strings[i]);
  338.         if spaces > 0 then
  339.         begin
  340.           result := true;
  341.           exit;
  342.         end;
  343.       end;
  344.       result := false;
  345.       exit;
  346.     end;
  347.  
  348. var
  349.   val: string;
  350.   ind: integer;
  351. resourcestring
  352.   SNoLinesAvailable = 'No lines with spaces at the beginning available';
  353. begin
  354.   // TODO: if something is selected, only process the selected part
  355.  
  356.   if not SpacesAvailable(SynEdit1.Lines) then
  357.   begin
  358.     MessageDlg(SNoLinesAvailable, mtInformation, [mbOk], 0);
  359.     exit;
  360.   end;
  361.  
  362.   ind := GuessIndent(SynEdit1.Lines);
  363.   if ind <> -1 then val := IntToStr(ind);
  364.  
  365.   InputQuery('Spaces to tabs', 'Indent:', val); // TODO: handle CANCEL correctly...
  366.   if TryStrToInt(Trim(val), ind) then
  367.   begin
  368.     if ind = 0 then exit;
  369.     SpaceToTab(SynEdit1.Lines, ind);
  370.   end;
  371.  
  372.   if SynEdit1.CanFocus then SynEdit1.SetFocus;
  373. end;
  374.  
  375. procedure TForm1.ActionESCExecute(Sender: TObject);
  376. begin
  377.   if (HlpPrevPageIndex <> -1) and (PageControl2.ActivePage = HelpTabSheet) and
  378.      (HelpTabsheet.TabVisible) then
  379.   begin
  380.     PageControl2.ActivePageIndex := HlpPrevPageIndex;
  381.     HelpTabsheet.TabVisible := false;
  382.   end;
  383.  
  384.   // Dirty hack...
  385.   SrcRep.CloseDialogs;
  386. end;
  387.  
  388. procedure TForm1.ActionFindExecute(Sender: TObject);
  389. begin
  390.   SrcRep.FindExecute;
  391. end;
  392.  
  393. var
  394.   firstTimeBrowserLoad: boolean = true;
  395. procedure TForm1.Run(Sender: TObject);
  396. var
  397.   bakTS: TTabSheet;
  398. begin
  399.   memo2.Lines.Text := '';
  400.  
  401.   if firstTimeBrowserLoad then
  402.   begin
  403.     bakTS := PageControl1.ActivePage;
  404.     try
  405.       PageControl1.ActivePage := HtmlTabSheet; // Required for the first time, otherwise, WebBrowser1.Clear will hang
  406.       Webbrowser1.Clear;
  407.     finally
  408.       PageControl1.ActivePage := bakTS;
  409.     end;
  410.     firstTimeBrowserLoad := false;
  411.   end
  412.   else
  413.     Webbrowser1.Clear;
  414.  
  415.   Screen.Cursor := crHourGlass;
  416.   Application.ProcessMessages;
  417.  
  418.   try
  419.     ActionSave.Execute; // TODO: if it is not the scrap file: do not save the file, since the user did not intended to save... better create a temporary file and run it instead.
  420.  
  421.     memo2.Lines.Text := RunPHPScript(GetScrapFile, Sender=ActionLint, False);
  422.  
  423.     Webbrowser1.LoadHTML(MarkUpLineReference(memo2.Lines.Text), GetScrapFile);
  424.  
  425.     if IsTextHTML(memo2.lines.text) then
  426.       PageControl1.ActivePage := HtmlTabSheet
  427.     else
  428.       PageControl1.ActivePage := PlaintextTabSheet;
  429.   finally
  430.     Screen.Cursor := crDefault;
  431.   end;
  432. end;
  433.  
  434. procedure TForm1.RunConsole(Sender: TObject);
  435. begin
  436.   ActionSave.Execute; // TODO: if it is not the scrap file: do not save the file, since the user did not intended to save... better create a temporary file and run it instead.
  437.   RunPHPScript(GetScrapFile, Sender=ActionLint, True);
  438. end;
  439.  
  440. procedure TForm1.SynEdit1Change(Sender: TObject);
  441. begin
  442.   RefreshModifySign;
  443. end;
  444.  
  445. procedure TForm1.SynEdit1GutterClick(Sender: TObject; Button: TMouseButton; X,
  446.   Y, Line: Integer; Mark: TSynEditMark);
  447. begin
  448.   (*
  449.   TSynEdit(Sender).CaretX := 1;
  450.   TSynEdit(Sender).CaretY := Line;
  451.   TSynEdit(Sender).SelLength := Length(TSynEdit(Sender).LineText);
  452.   *)
  453. end;
  454.  
  455. procedure TForm1.SynEdit1MouseCursor(Sender: TObject; const aLineCharPos: TBufferCoord; var aCursor: TCursor);
  456. {$IFDEF OnlineHelp}
  457. var
  458.   Line: Integer;
  459.   Column: Integer;
  460.   word: string;
  461. begin
  462.   Line  := aLineCharPos.Line-1;
  463.   Column := aLineCharPos.Char-1;
  464.   word := GetWordUnderPos(TSynEdit(Sender), Line, Column);
  465.   if word <> gOnlineHelpWord then
  466.   begin
  467.     gOnlineHelpWord := word;
  468.     Timer1.Enabled := false;
  469.     Timer1.Enabled := true;
  470.   end;
  471. {$ELSE}
  472. begin
  473. {$ENDIF}
  474. end;
  475.  
  476. procedure TForm1.SynEdit1MouseWheelDown(Sender: TObject; Shift: TShiftState;
  477.   MousePos: TPoint; var Handled: Boolean);
  478. begin
  479.   if ssCtrl in Shift then
  480.   begin
  481.     SynEdit1.Font.Size := Max(SynEdit1.Font.Size - 1, 5);
  482.     Handled := true;
  483.   end
  484.   else Handled := false;
  485. end;
  486.  
  487. procedure TForm1.SynEdit1MouseWheelUp(Sender: TObject; Shift: TShiftState;
  488.   MousePos: TPoint; var Handled: Boolean);
  489. begin
  490.   if ssCtrl in Shift then
  491.   begin
  492.     SynEdit1.Font.Size := SynEdit1.Font.Size + 1;
  493.     Handled := true;
  494.   end
  495.   else Handled := false;
  496. end;
  497.  
  498. procedure TForm1.SynEdit1PaintTransient(Sender: TObject; Canvas: TCanvas; TransientType: TTransientType);
  499. var
  500.   Editor: TSynEdit;
  501.   OpenChars: array of WideChar;//[0..2] of WideChar=();
  502.   CloseChars: array of WideChar;//[0..2] of WideChar=();
  503.  
  504.   function IsCharBracket(AChar: WideChar): Boolean;
  505.   begin
  506.     case AChar of
  507.       '{','[','(','<','}',']',')','>':
  508.         Result := True;
  509.       else
  510.         Result := False;
  511.     end;
  512.   end;
  513.  
  514.   function CharToPixels(P: TBufferCoord): TPoint;
  515.   begin
  516.     Result := Editor.RowColumnToPixels(Editor.BufferToDisplayPos(P));
  517.   end;
  518.  
  519. const
  520.   COLOR_FG = clGreen;
  521.   COLOR_BG = clLime;
  522. var
  523.   P: TBufferCoord;
  524.   Pix: TPoint;
  525.   D: TDisplayCoord;
  526.   S: UnicodeString;
  527.   I: Integer;
  528.   Attri: TSynHighlighterAttributes;
  529.   ArrayLength: Integer;
  530.   start: Integer;
  531.   TmpCharA, TmpCharB: WideChar;
  532. begin      
  533.   // Source: https://github.com/SynEdit/SynEdit/blob/master/Demos/OnPaintTransientDemo/Unit1.pas
  534.  
  535.   if TSynEdit(Sender).SelAvail then exit;
  536.   Editor := TSynEdit(Sender);
  537.   ArrayLength:= 3;
  538.  
  539.   (*
  540.   if (Editor.Highlighter = shHTML) or (Editor.Highlighter = shXML) then
  541.     inc(ArrayLength);
  542.   *)
  543.  
  544.   SetLength(OpenChars, ArrayLength);
  545.   SetLength(CloseChars, ArrayLength);
  546.   for i := 0 to ArrayLength - 1 do
  547.   begin
  548.     case i of
  549.       0: begin OpenChars[i] := '('; CloseChars[i] := ')'; end;
  550.       1: begin OpenChars[i] := '{'; CloseChars[i] := '}'; end;
  551.       2: begin OpenChars[i] := '['; CloseChars[i] := ']'; end;
  552.       3: begin OpenChars[i] := '<'; CloseChars[i] := '>'; end;
  553.     end;
  554.   end;
  555.  
  556.   P := Editor.CaretXY;
  557.   D := Editor.DisplayXY;
  558.  
  559.   Start := Editor.SelStart;
  560.  
  561.   if (Start > 0) and (Start <= length(Editor.Text)) then
  562.     TmpCharA := Editor.Text[Start]
  563.   else
  564.     TmpCharA := #0;
  565.  
  566.   if (Start > 0){Added by VTS} and (Start < length(Editor.Text)) then
  567.     TmpCharB := Editor.Text[Start + 1]
  568.   else
  569.     TmpCharB := #0;
  570.  
  571.   if not IsCharBracket(TmpCharA) and not IsCharBracket(TmpCharB) then exit;
  572.   S := TmpCharB;
  573.   if not IsCharBracket(TmpCharB) then
  574.   begin
  575.     P.Char := P.Char - 1;
  576.     S := TmpCharA;
  577.   end;
  578.   Editor.GetHighlighterAttriAtRowCol(P, S, Attri);
  579.  
  580.   if (Editor.Highlighter.SymbolAttribute = Attri) then
  581.   begin
  582.     for i := low(OpenChars) to High(OpenChars) do
  583.     begin
  584.       if (S = OpenChars[i]) or (S = CloseChars[i]) then
  585.       begin
  586.         Pix := CharToPixels(P);
  587.  
  588.         Editor.Canvas.Brush.Style := bsSolid;//Clear;
  589.         Editor.Canvas.Font.Assign(Editor.Font);
  590.         Editor.Canvas.Font.Style := Attri.Style;
  591.  
  592.         if (TransientType = ttAfter) then
  593.         begin
  594.           Editor.Canvas.Font.Color := COLOR_FG;
  595.           Editor.Canvas.Brush.Color := COLOR_BG;
  596.         end
  597.         else
  598.         begin
  599.           Editor.Canvas.Font.Color := Attri.Foreground;
  600.           Editor.Canvas.Brush.Color := Attri.Background;
  601.         end;
  602.         if Editor.Canvas.Font.Color = clNone then
  603.           Editor.Canvas.Font.Color := Editor.Font.Color;
  604.         if Editor.Canvas.Brush.Color = clNone then
  605.           Editor.Canvas.Brush.Color := Editor.Color;
  606.  
  607.         Editor.Canvas.TextOut(Pix.X, Pix.Y, S);
  608.         P := Editor.GetMatchingBracketEx(P);
  609.  
  610.         if (P.Char > 0) and (P.Line > 0) then
  611.         begin
  612.           Pix := CharToPixels(P);
  613.           if Pix.X > Editor.Gutter.Width then
  614.           begin
  615.             {$REGION 'Added by ViaThinkSoft'}
  616.             if (TransientType = ttAfter) then
  617.             begin
  618.               Editor.Canvas.Font.Color := COLOR_FG;
  619.               Editor.Canvas.Brush.Color := COLOR_BG;
  620.             end
  621.             else
  622.             begin
  623.               Editor.Canvas.Font.Color := Attri.Foreground;
  624.               Editor.Canvas.Brush.Color := Attri.Background;
  625.             end;
  626.             if Editor.Canvas.Font.Color = clNone then
  627.               Editor.Canvas.Font.Color := Editor.Font.Color;
  628.             if Editor.Canvas.Brush.Color = clNone then
  629.               Editor.Canvas.Brush.Color := Editor.Color;
  630.             {$ENDREGION}
  631.             if S = OpenChars[i] then
  632.               Editor.Canvas.TextOut(Pix.X, Pix.Y, CloseChars[i])
  633.             else Editor.Canvas.TextOut(Pix.X, Pix.Y, OpenChars[i]);
  634.           end;
  635.         end;
  636.       end;
  637.     end;
  638.     Editor.Canvas.Brush.Style := bsSolid;
  639.   end;
  640. end;
  641.  
  642. procedure TForm1.SynEditFocusTimerTimer(Sender: TObject);
  643. begin
  644.   SynEditFocusTimer.Enabled := false;
  645.   Button1.SetFocus; // Workaround for weird bug... This (and the timer) is necessary to get the focus to SynEdit1
  646.   SynEdit1.SetFocus;
  647. end;
  648.  
  649. procedure TForm1.Timer1Timer(Sender: TObject);
  650. begin
  651.   {$IFDEF OnlineHelp}
  652.   Timer1.Enabled := false;
  653.  
  654.   // TODO: Insert a small online help hint
  655.   //Caption := gOnlineHelpWord;
  656.   {$ENDIF}
  657. end;
  658.  
  659. procedure TForm1.TreeView1DblClick(Sender: TObject);
  660. var
  661.   tn: TTreeNode;
  662.   lineNo: integer;
  663. begin
  664.   tn := TTreeView(Sender).Selected;
  665.   if tn = nil then exit;
  666.   lineNo := Integer(tn.Data);
  667.   if lineNo > 0 then GotoLineNo(lineNo);
  668. end;
  669.  
  670. (*
  671. {$IFDEF USE_SHDOCVW_TLB}
  672. *)
  673. procedure TForm1.WebBrowser1BeforeNavigate2(ASender: TObject;
  674.   const pDisp: IDispatch; const URL, Flags, TargetFrameName, PostData,
  675.   Headers: OleVariant; var Cancel: WordBool);
  676. begin
  677.   BeforeNavigate(URL, Cancel);
  678. end;
  679. (*
  680. {$ELSE}
  681. procedure TForm1.WebBrowser1BeforeNavigate2(ASender: TObject;
  682.   const pDisp: IDispatch; var URL, Flags, TargetFrameName, PostData,
  683.   Headers: OleVariant; var Cancel: WordBool);
  684. begin
  685.   BeforeNavigate(URL, Cancel);
  686. end;
  687. {$ENDIF}
  688. *)
  689.  
  690. procedure TForm1.BeforeNavigate(const URL: OleVariant; var Cancel: WordBool);
  691. var
  692.   s, myURL: string;
  693.   lineno: integer;
  694.   p: integer;
  695. begin
  696.   {$REGION 'Line number references (PHP errors and warnings)'}
  697.   if Copy(URL, 1, length(FASTPHP_GOTO_URI_PREFIX)) = FASTPHP_GOTO_URI_PREFIX then
  698.   begin
  699.     try
  700.       s := copy(URL, length(FASTPHP_GOTO_URI_PREFIX)+1, 99);
  701.       if not TryStrToInt(s, lineno) then exit;
  702.       GotoLineNo(lineno);
  703.       SynEditFocusTimer.Enabled := true;
  704.     finally
  705.       Cancel := true;
  706.     end;
  707.     Exit;
  708.   end;
  709.   {$ENDREGION}
  710.  
  711.   {$REGION 'Intelligent browser (executes PHP scripts)'}
  712.   if URL <> 'about:blank' then
  713.   begin
  714.     myUrl := URL;
  715.  
  716.     p := Pos('?', myUrl);
  717.     if p >= 1 then myURL := copy(myURL, 1, p-1);
  718.  
  719.     // TODO: myURL urldecode
  720.     // TODO: maybe we could even open that file in the editor!
  721.  
  722.     if FileExists(myURL) and (EndsText('.php', myURL) or EndsText('.php3', myURL) or EndsText('.php4', myURL) or EndsText('.php5', myURL) or EndsText('.phps', myURL)) then
  723.     begin
  724.       WebBrowser1.LoadHTML(RunPHPScript(myURL), myUrl);
  725.       Cancel := true;
  726.     end;
  727.   end;
  728.   {$ENDREGION}
  729. end;
  730.  
  731. procedure TForm1.BtnSpecialCharsClick(Sender: TObject);
  732. var
  733.   opts: TSynEditorOptions;
  734. begin
  735.   opts := SynEdit1.Options;
  736.   if eoShowSpecialChars in SynEdit1.Options then
  737.   begin
  738.     BtnSpecialChars.Picture.Assign(BtnSpecialCharsOff.Picture);
  739.     Exclude(opts, eoShowSpecialChars);
  740.     TFastPHPConfig.SpecialChars := false;
  741.   end
  742.   else
  743.   begin
  744.     BtnSpecialChars.Picture.Assign(BtnSpecialCharsOn.Picture);
  745.     Include(opts, eoShowSpecialChars);
  746.     TFastPHPConfig.SpecialChars := true;
  747.   end;
  748.   SynEdit1.Options := opts;
  749. end;
  750.  
  751. procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
  752. begin
  753.   TFastPHPConfig.FontSize := SynEdit1.Font.Size;
  754. end;
  755.  
  756. procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
  757. var
  758.   r: integer;
  759. begin
  760.   if SynEdit1.Modified then
  761.   begin
  762.     if (ParamStr(1) <> '') or (FSaveAsFilename <> '') then
  763.     begin
  764.       r := MessageDlg('Do you want to save?', mtConfirmation, mbYesNoCancel, 0);
  765.       if r = mrCancel then
  766.       begin
  767.         CanClose := false;
  768.         Exit;
  769.       end
  770.       else if r = mrYes then
  771.       begin
  772.         ActionSave.Execute;
  773.         CanClose := true;
  774.       end;
  775.     end
  776.     else
  777.     begin
  778.       ActionSave.Execute;
  779.       CanClose := true;
  780.     end;
  781.   end;
  782. end;
  783.  
  784. procedure TForm1.FormCreate(Sender: TObject);
  785. var
  786.   exeDir: string;
  787. begin
  788.   HlpPrevPageIndex := -1;
  789.   CurSearchTerm := '';
  790.   Caption := Caption + ' - ' + GetScrapFile;
  791.   SrcRep := TSynEditFindReplace.Create(self);
  792.   SrcRep.Editor := SynEdit1;
  793.   SynEdit1.Gutter.Gradient := HighColorWindows;
  794.  
  795.   Screen.Cursors[crMouseGutter] := LoadCursor(hInstance, 'MOUSEGUTTER');
  796.   SynEdit1.Gutter.Cursor := crMouseGutter;
  797.  
  798.   exeDir := IncludeTrailingPathDelimiter(ExtractFilePath(ParamStr(0)));
  799.   if FileExists(exeDir + 'codeexplorer.bmp') then ImageList1.LoadAndSplitImages(exeDir + 'codeexplorer.bmp');
  800. end;
  801.  
  802. procedure TForm1.FormDestroy(Sender: TObject);
  803. begin
  804.   if Assigned(ChmIndex) then
  805.   begin
  806.     FreeAndNil(ChmIndex);
  807.   end;
  808.   FreeAndNil(SrcRep);
  809.  
  810.   if Assigned(codeExplorer) then
  811.   begin
  812.     codeExplorer.Terminate;
  813.     codeExplorer.WaitFor;
  814.     FreeAndNil(codeExplorer);
  815.   end;
  816. end;
  817.  
  818. procedure TForm1.FormShow(Sender: TObject);
  819. var
  820.   ScrapFile: string;
  821.   tmpFontSize: integer;
  822.   opts: TSynEditorOptions;
  823. begin
  824.   ScrapFile := GetScrapFile;
  825.   if ScrapFile = '' then
  826.   begin
  827.     Application.Terminate; // Close;
  828.     exit;
  829.   end;
  830.  
  831.   opts := SynEdit1.Options;
  832.   if TFastPHPConfig.SpecialChars then
  833.   begin
  834.     BtnSpecialChars.Picture.Assign(BtnSpecialCharsOn.Picture);
  835.     Include(opts, eoShowSpecialChars);
  836.   end
  837.   else
  838.   begin
  839.     BtnSpecialChars.Picture.Assign(BtnSpecialCharsOff.Picture);
  840.     Exclude(opts, eoShowSpecialChars);
  841.   end;
  842.   SynEdit1.Options := opts;
  843.  
  844.   if FileExists(ScrapFile) then
  845.     SynEdit1.Lines.LoadFromFile(ScrapFile)
  846.   else
  847.     SynEdit1.Lines.Clear;
  848.  
  849.   PageControl1.ActivePage := PlaintextTabSheet;
  850.  
  851.   PageControl2.ActivePage := CodeTabsheet;
  852.   HelpTabsheet.TabVisible := false;
  853.  
  854.   tmpFontSize := TFastPHPConfig.FontSize;
  855.   if tmpFontSize <> -1 then SynEdit1.Font.Size := tmpFontSize;
  856.   SynEdit1.SetFocus;
  857.  
  858.   DoubleBuffered := true;
  859.   StartCodeExplorer;
  860. end;
  861.  
  862. procedure TForm1.Save1Click(Sender: TObject);
  863. begin
  864.   Button7.Click;
  865. end;
  866.  
  867. procedure TForm1.Saveas1Click(Sender: TObject);
  868. begin
  869.   if SaveDialog1.Execute then
  870.   begin
  871.     FSaveAsFilename := SaveDialog1.FileName;
  872.     Caption := Copy(Caption, 1, Pos(' - ', Caption)-1) + ' - ' + FSaveAsFilename;
  873.     Button7.Click;
  874.   end;
  875. end;
  876.  
  877. procedure TForm1.StartCodeExplorer;
  878. begin
  879.   codeExplorer := TRunCodeExplorer.Create(true);
  880.   codeExplorer.InputRequestCallback := InputRequestCallback;
  881.   codeExplorer.OutputNotifyCallback := OutputNotifyCallback;
  882.   codeExplorer.PhpExe := GetPHPExe;
  883.   codeExplorer.PhpFile := IncludeTrailingPathDelimiter(ExtractFileDir(Application.ExeName)) + 'codeexplorer.php'; // GetScrapFile;
  884.   codeExplorer.WorkDir := ExtractFileDir(Application.ExeName);
  885.   codeExplorer.Resume;
  886. end;
  887.  
  888. function TForm1.GetScrapFile: string;
  889. var
  890.   tmpPath: string;
  891. begin
  892.   if FSaveAsFilename <> '' then
  893.   begin
  894.     result := FSaveAsFilename;
  895.     exit;
  896.   end;
  897.  
  898.   if FScrapFile <> '' then
  899.   begin
  900.     result := FScrapFile;
  901.     exit;
  902.   end;
  903.  
  904.   if ParamStr(1) <> '' then
  905.   begin
  906.     // Program was started with a filename
  907.  
  908.     result := ParamStr(1);
  909.  
  910.     if not FileExists(result) then
  911.     begin
  912.       case MessageDlg(Format('File %s does not exist. Create it?', [result]), mtConfirmation, mbYesNoCancel, 0) of
  913.         mrYes:
  914.           try
  915.             SynEdit1.Lines.SaveToFile(result);
  916.           except
  917.             on E: Exception do
  918.             begin
  919.               MessageDlg(E.Message, mtError, [mbOk], 0);
  920.               Application.Terminate;
  921.               result := '';
  922.               exit;
  923.             end;
  924.           end;
  925.         mrNo:
  926.           begin
  927.             Application.Terminate;
  928.             result := '';
  929.             exit;
  930.           end;
  931.         mrCancel:
  932.           begin
  933.             Application.Terminate;
  934.             result := '';
  935.             exit;
  936.           end;
  937.       end;
  938.     end;
  939.   end
  940.   else
  941.   begin
  942.     // Program is started without filename -> use scrap file
  943.  
  944.     result := TFastPHPConfig.ScrapFile;
  945.  
  946.     if not FileExists(result) then
  947.     begin
  948.       repeat
  949.         {$REGION 'Determinate opendialog initial directory'}
  950.         if result <> '' then
  951.         begin
  952.           tmpPath := ExtractFilePath(result);
  953.           if DirectoryExists(tmpPath) then
  954.           begin
  955.             OpenDialog3.InitialDir := tmpPath;
  956.             OpenDialog3.FileName := Result;
  957.           end
  958.           else
  959.           begin
  960.             OpenDialog3.InitialDir := GetMyDocumentsFolder;
  961.           end;
  962.         end
  963.         else
  964.         begin
  965.           OpenDialog3.InitialDir := GetMyDocumentsFolder;
  966.         end;
  967.         {$ENDREGION}
  968.  
  969.         if not OpenDialog3.Execute then
  970.         begin
  971.           Application.Terminate;
  972.           result := '';
  973.           exit;
  974.         end;
  975.  
  976.         if not DirectoryExists(ExtractFilePath(OpenDialog3.FileName)) then
  977.         begin
  978.           MessageDlg('Path does not exist! Please try again.', mtWarning, [mbOk], 0);
  979.         end
  980.         else
  981.         begin
  982.           result := OpenDialog3.FileName;
  983.         end;
  984.       until result <> '';
  985.  
  986.       if not FileExists(result) then
  987.       begin
  988.         try
  989.           // Try saving the file; check if we have permissions
  990.           //SynEdit1.Lines.Clear;
  991.           SynEdit1.Lines.SaveToFile(result);
  992.         except
  993.           on E: Exception do
  994.           begin
  995.             MessageDlg(E.Message, mtError, [mbOk], 0);
  996.             Application.Terminate;
  997.             result := '';
  998.             exit;
  999.           end;
  1000.         end;
  1001.       end;
  1002.  
  1003.       TFastPHPConfig.ScrapFile := result;
  1004.       FScrapFile := result;
  1005.     end;
  1006.   end;
  1007. end;
  1008.  
  1009. procedure TForm1.Help;
  1010. var
  1011.   IndexFile, chmFile, w, OriginalWord, url: string;
  1012.   internalHtmlFile: string;
  1013. begin
  1014.   if not Assigned(ChmIndex) then
  1015.   begin
  1016.     IndexFile := TFastPHPConfig.HelpIndex;
  1017.     IndexFile := ChangeFileExt(IndexFile, '.ini'); // Just to be sure. Maybe someone wrote manually the ".chm" file in there
  1018.     if FileExists(IndexFile) then
  1019.     begin
  1020.       ChmIndex := TMemIniFile.Create(IndexFile);
  1021.     end;
  1022.   end;
  1023.  
  1024.   if Assigned(ChmIndex) then
  1025.   begin
  1026.     IndexFile := TFastPHPConfig.HelpIndex;
  1027.     // We don't check if IndexFile still exists. It is not important since we have ChmIndex pre-loaded in memory
  1028.  
  1029.     chmFile := ChangeFileExt(IndexFile, '.chm');
  1030.     if not FileExists(chmFile) then
  1031.     begin
  1032.       FreeAndNil(ChmIndex);
  1033.     end;
  1034.   end;
  1035.  
  1036.   if not Assigned(ChmIndex) then
  1037.   begin
  1038.     if not OpenDialog1.Execute then exit;
  1039.  
  1040.     chmFile := OpenDialog1.FileName;
  1041.     if not FileExists(chmFile) then exit;
  1042.  
  1043.     IndexFile := ChangeFileExt(chmFile, '.ini');
  1044.  
  1045.     if not FileExists(IndexFile) then
  1046.     begin
  1047.       Panel1.Align := alClient;
  1048.       Panel1.Visible := true;
  1049.       Panel1.BringToFront;
  1050.       Screen.Cursor := crHourGlass;
  1051.       Application.ProcessMessages;
  1052.       try
  1053.         if not ParseCHM(chmFile) then
  1054.         begin
  1055.           MessageDlg('The CHM file is not a valid PHP documentation. Cannot use help.', mtError, [mbOk], 0);
  1056.           exit;
  1057.         end;
  1058.       finally
  1059.         Screen.Cursor := crDefault;
  1060.         Panel1.Visible := false;
  1061.       end;
  1062.  
  1063.       if not FileExists(IndexFile) then
  1064.       begin
  1065.         MessageDlg('Unknown error. Cannot use help.', mtError, [mbOk], 0);
  1066.         exit;
  1067.       end;
  1068.     end;
  1069.  
  1070.     TFastPHPConfig.HelpIndex := IndexFile;
  1071.  
  1072.     ChmIndex := TMemIniFile.Create(IndexFile);
  1073.   end;
  1074.  
  1075.   w := GetWordUnderCaret(SynEdit1);
  1076.   if w = '' then exit;
  1077.   {$IFDEF UNICODE}
  1078.   if CharInSet(w[1], ['0'..'9']) then exit;
  1079.   {$ELSE}
  1080.   if w[1] in ['0'..'9'] then exit;
  1081.   {$ENDIF}
  1082.  
  1083.   Originalword := w;
  1084. //  w := StringReplace(w, '_', '-', [rfReplaceAll]);
  1085.   w := LowerCase(w);
  1086.   CurSearchTerm := w;
  1087.  
  1088.   internalHtmlFile := ChmIndex.ReadString('_HelpWords_', CurSearchTerm, '');
  1089.   if internalHtmlFile = '' then
  1090.   begin
  1091.     HelpTabsheet.TabVisible := false;
  1092.     HlpPrevPageIndex := -1;
  1093.     ShowMessageFmt('No help for "%s" available', [Originalword]);
  1094.     Exit;
  1095.   end;
  1096.  
  1097.   url := 'mk:@MSITStore:'+ChmFile+'::'+internalHtmlFile;
  1098.  
  1099.   HlpPrevPageIndex := PageControl2.ActivePageIndex; // Return by pressing ESC
  1100.   HelpTabsheet.TabVisible := true;
  1101.   PageControl2.ActivePage := HelpTabsheet;
  1102.   WebBrowser2.Navigate(url);
  1103.   WebBrowser2.Wait;
  1104. end;
  1105.  
  1106. procedure TForm1.GotoLineNo(LineNo:integer);
  1107. var
  1108.   line: string;
  1109.   i: integer;
  1110. begin
  1111.   SynEdit1.GotoLineAndCenter(LineNo);
  1112.  
  1113.   // Skip indent
  1114.   line := SynEdit1.Lines[SynEdit1.CaretY];
  1115.   for i := 1 to Length(line) do
  1116.   begin
  1117.     {$IFDEF UNICODE}
  1118.     if not CharInSet(line[i], [' ', #9]) then
  1119.     {$ELSE}
  1120.     if not (line[i] in [' ', #9]) then
  1121.     {$ENDIF}
  1122.     begin
  1123.       SynEdit1.CaretX := i-1;
  1124.       break;
  1125.     end;
  1126.   end;
  1127.  
  1128.   PageControl2.ActivePage := CodeTabsheet;
  1129.   if SynEdit1.CanFocus then SynEdit1.SetFocus;
  1130. end;
  1131.  
  1132. procedure TForm1.PageControl2Changing(Sender: TObject;
  1133.   var AllowChange: Boolean);
  1134. begin
  1135.   if PageControl2.ActivePage = HelpTabsheet then
  1136.     HlpPrevPageIndex := -1
  1137.   else
  1138.     HlpPrevPageIndex := PageControl2.ActivePageIndex;
  1139.  
  1140.   AllowChange := true;
  1141. end;
  1142.  
  1143. procedure TForm1.Memo2DblClick(Sender: TObject);
  1144. var
  1145.   line: string;
  1146.  
  1147.   procedure _process(toFind: string);
  1148.   var
  1149.     p, lineno: integer;
  1150.   begin
  1151.     if FileSystemCaseSensitive then
  1152.       p := Pos(toFind, line)
  1153.     else
  1154.       p := Pos(LowerCase(toFind), LowerCase(line));
  1155.     if p <> 0 then
  1156.     begin
  1157.       line := copy(line, p+length(toFind), 99);
  1158.       if not TryStrToInt(line, lineno) then exit;
  1159.       GotoLineNo(lineno);
  1160.     end;
  1161.   end;
  1162.  
  1163. begin
  1164.   line := memo2.Lines.Strings[Memo2.CaretPos.Y];
  1165.  
  1166.   {$REGION 'Possibility 1: filename.php:lineno'}
  1167.   _process(ExtractFileName(GetScrapFile) + ':');
  1168.   {$ENDREGION}
  1169.  
  1170.   {$REGION 'Possibility 2: on line xx'}
  1171.   _process(ExtractFileName(GetScrapFile) + ' on line ');
  1172.   {$ENDREGION}
  1173. end;
  1174.  
  1175. procedure TForm1.Memo2KeyDown(Sender: TObject; var Key: Word;
  1176.   Shift: TShiftState);
  1177. begin
  1178.   if ((ssCtrl in Shift) and (Key = 65)) then TMemo(Sender).SelectAll;
  1179. end;
  1180.  
  1181. function TForm1.MarkUpLineReference(cont: string): string;
  1182.  
  1183.   procedure _process(toFind: string);
  1184.   var
  1185.     p, a, b: integer;
  1186.     num: integer;
  1187.     insert_a, insert_b: string;
  1188.   begin
  1189.     if FileSystemCaseSensitive then
  1190.       p := Pos(toFind, cont)
  1191.     else
  1192.       p := Pos(LowerCase(toFind), LowerCase(cont));
  1193.     while p >= 1 do
  1194.     begin
  1195.       a := p;
  1196.       b := p + length(toFind);
  1197.       num := 0;
  1198.       {$IFDEF UNICODE}
  1199.       while CharInSet(cont[b], ['0'..'9']) do
  1200.       {$ELSE}
  1201.       while cont[b] in ['0'..'9'] do
  1202.       {$ENDIF}
  1203.       begin
  1204.         num := num*10 + StrToInt(cont[b]);
  1205.         inc(b);
  1206.       end;
  1207.  
  1208.       insert_b := '</a>';
  1209.       insert_a := '<a href="' + FASTPHP_GOTO_URI_PREFIX + IntToStr(num) + '">';
  1210.  
  1211.       insert(insert_b, cont, b);
  1212.       insert(insert_a, cont, a);
  1213.  
  1214.       p := b + Length(insert_a) + Length(insert_b);
  1215.  
  1216.       p := PosEx(toFind, cont, p+1);
  1217.     end;
  1218.   end;
  1219.  
  1220. begin
  1221.   {$REGION 'Possibility 1: filename.php:lineno'}
  1222.   _process(ExtractFileName(GetScrapFile) + ':');
  1223.   {$ENDREGION}
  1224.  
  1225.   {$REGION 'Possibility 2: on line xx'}
  1226.   _process(ExtractFileName(GetScrapFile) + ' on line ');
  1227.   {$ENDREGION}
  1228.  
  1229.   result := cont;
  1230. end;
  1231.  
  1232. function TForm1.InputRequestCallback(var data: AnsiString): boolean;
  1233. begin
  1234.   data := UTF8Encode(SynEdit1.Text);
  1235.   result := true;
  1236. end;
  1237.  
  1238. function TForm1.OutputNotifyCallback(const data: AnsiString): boolean;
  1239. begin
  1240.   result := TreeView1.FillWithFastPHPData(data);
  1241. end;
  1242.  
  1243. end.
  1244.