Subversion Repositories fastphp

Rev

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