Subversion Repositories fastphp

Rev

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