Subversion Repositories fastphp

Rev

Rev 113 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
94 daniel-mar 1
unit EditorMain;
2 daniel-mar 2
 
25 daniel-mar 3
{$Include 'FastPHP.inc'}
4
 
4 daniel-mar 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
 
91 daniel-mar 14
// TODO: "crHourGlass" does not work if F9 is pressed!
72 daniel-mar 15
// TODO: if a scrapfile is already open, create a new scrap file (scrap2.php)
2 daniel-mar 16
// TODO: localize
17
// TODO: wieso geht copy paste im twebbrowser nicht???
49 daniel-mar 18
// TODO: Wieso dauert webbrowser1 erste kompilierung so lange???
5 daniel-mar 19
// TODO: wieso kommt syntax fehler zweimal? einmal stderr einmal stdout?
20
// TODO: Browser titlebar (link preview)
49 daniel-mar 21
// TODO: "jump to next/prev todo" buttons/shortcuts
22
// TODO: "increase/decrease indent" buttons/shortcuts
96 daniel-mar 23
// TODO: Solve all compiler warnings, especially in regards Encoding
2 daniel-mar 24
 
94 daniel-mar 25
// Note in re Unicode:
26
// - In Embarcadero® Delphi 10.4 Version 27.0.40680.4203:
27
//   SynEdit correctly detects UTF-8 files without BOM as well as ANSI files with Umlauts.
28
//   (Previous versions could not detect UTF-8 files without BOM?!)
29
// - If BOM is existing, it will be removed. (which is good, because this is defined by PSR-1)
30
 
63 daniel-mar 31
// Small things:
32
// - The scroll bars of SynEdit are not affected by the dark theme
96 daniel-mar 33
// - dark theme full screen: doubleclick to desktop pixel "0,0" cannot be used to close the app
63 daniel-mar 34
 
2 daniel-mar 35
// Future ideas
31 daniel-mar 36
// - code insight
2 daniel-mar 37
// - verschiedene php versionen?
104 daniel-mar 38
// - webbrowser1 nur laden, wenn man den tab anwaehlt?
39
// - doppelklick auf tab soll diesen schliessen
91 daniel-mar 40
// - Onlinehelp (www) aufrufen oder CHM datei
13 daniel-mar 41
// - Let all colors be adjustable
21 daniel-mar 42
// - code in bildschirmmitte (horizontal)?
72 daniel-mar 43
// - search in files of a directory
91 daniel-mar 44
// - Files in multiple tabs?
45
// - Configurable tabulator display-width
2 daniel-mar 46
 
47
interface
48
 
49
uses
27 daniel-mar 50
  // TODO: "{$IFDEF USE_SHDOCVW_TLB}_TLB{$ENDIF}" does not work with Delphi 10.2
2 daniel-mar 51
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
4 daniel-mar 52
  Dialogs, StdCtrls, OleCtrls, ComCtrls, ExtCtrls, ToolWin, IniFiles,
80 daniel-mar 53
  SynEditHighlighter, SynHighlighterPHP, SynEdit, ShDocVw, FindReplace,
45 daniel-mar 54
  ActnList, SynEditMiscClasses, SynEditSearch, RunPHP, ImgList, SynUnicode,
94 daniel-mar 55
  System.ImageList, System.Actions, Vcl.Menus, Vcl.Themes, System.UITypes,
105 daniel-mar 56
  SynEditCodeFolding, WebBrowserUtils;
2 daniel-mar 57
 
23 daniel-mar 58
{.$DEFINE OnlineHelp}
59
 
2 daniel-mar 60
type
61
  TForm1 = class(TForm)
62
    PageControl1: TPageControl;
63
    PlaintextTabSheet: TTabSheet;
64
    HtmlTabSheet: TTabSheet;
65
    Memo2: TMemo;
66
    WebBrowser1: TWebBrowser;
67
    Splitter1: TSplitter;
68
    PageControl2: TPageControl;
20 daniel-mar 69
    CodeTabsheet: TTabSheet;
2 daniel-mar 70
    HelpTabsheet: TTabSheet;
71
    WebBrowser2: TWebBrowser;
72
    OpenDialog1: TOpenDialog;
73
    Panel1: TPanel;
74
    OpenDialog3: TOpenDialog;
4 daniel-mar 75
    SynEdit1: TSynEdit;
76
    SynPHPSyn1: TSynPHPSyn;
5 daniel-mar 77
    Panel2: TPanel;
78
    SynEditFocusTimer: TTimer;
79
    Button1: TButton;
80
    Button2: TButton;
81
    Button3: TButton;
13 daniel-mar 82
    Button4: TButton;
83
    Button5: TButton;
84
    Button6: TButton;
85
    ActionList: TActionList;
86
    ActionFind: TAction;
87
    ActionReplace: TAction;
88
    ActionFindNext: TAction;
89
    ActionGoto: TAction;
90
    ActionSave: TAction;
91
    ActionHelp: TAction;
92
    ActionRun: TAction;
93
    ActionESC: TAction;
94
    Button7: TButton;
15 daniel-mar 95
    ActionOpen: TAction;
96
    Button8: TButton;
22 daniel-mar 97
    Button9: TButton;
98
    ActionFindPrev: TAction;
23 daniel-mar 99
    Timer1: TTimer;
100
    ActionSpaceToTab: TAction;
101
    Button11: TButton;
24 daniel-mar 102
    SynEditSearch1: TSynEditSearch;
27 daniel-mar 103
    TreeView1: TTreeView;
26 daniel-mar 104
    Splitter2: TSplitter;
33 daniel-mar 105
    btnLint: TButton;
106
    ActionLint: TAction;
36 daniel-mar 107
    ImageList1: TImageList;
45 daniel-mar 108
    RunPopup: TPopupMenu;
109
    OpeninIDE1: TMenuItem;
110
    ActionRunConsole: TAction;
111
    Runinconsole1: TMenuItem;
56 daniel-mar 112
    SavePopup: TPopupMenu;
113
    Saveas1: TMenuItem;
114
    Save1: TMenuItem;
115
    SaveDialog1: TSaveDialog;
57 daniel-mar 116
    BtnSpecialChars: TImage;
117
    BtnSpecialCharsOff: TImage;
118
    BtnSpecialCharsOn: TImage;
62 daniel-mar 119
    BtnLightOn: TImage;
120
    BtnLightOff: TImage;
121
    BtnLight: TImage;
122
    StartUpTimer: TTimer;
83 daniel-mar 123
    FileModTimer: TTimer;
87 daniel-mar 124
    GotoPHPdir1: TMenuItem;
125
    PHPShell1: TMenuItem;
91 daniel-mar 126
    ActionSaveAs: TAction;
127
    ActionGoToPHPDir: TAction;
128
    ActionPHPInteractiveShell: TAction;
99 daniel-mar 129
    FontSizeTimer: TTimer;
114 daniel-mar 130
    ChooseanotherPHPinterpreter1: TMenuItem;
131
    ChooseanotherCHMhelpfile1: TMenuItem;
2 daniel-mar 132
    procedure Run(Sender: TObject);
45 daniel-mar 133
    procedure RunConsole(Sender: TObject);
2 daniel-mar 134
    procedure FormShow(Sender: TObject);
135
    procedure FormCreate(Sender: TObject);
136
    procedure FormDestroy(Sender: TObject);
137
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
138
    procedure PageControl2Changing(Sender: TObject; var AllowChange: Boolean);
5 daniel-mar 139
    procedure Memo2DblClick(Sender: TObject);
45 daniel-mar 140
    (*
44 daniel-mar 141
    {$IFDEF USE_SHDOCVW_TLB}
45 daniel-mar 142
    *)
5 daniel-mar 143
    procedure WebBrowser1BeforeNavigate2(ASender: TObject;
27 daniel-mar 144
      const pDisp: IDispatch; const URL, Flags, TargetFrameName, PostData,
5 daniel-mar 145
      Headers: OleVariant; var Cancel: WordBool);
45 daniel-mar 146
    (*
44 daniel-mar 147
    {$ELSE}
148
    procedure WebBrowser1BeforeNavigate2(ASender: TObject;
149
      const pDisp: IDispatch; var URL, Flags, TargetFrameName, PostData,
150
      Headers: OleVariant; var Cancel: WordBool);
151
    {$ENDIF}
45 daniel-mar 152
    *)
44 daniel-mar 153
    procedure BeforeNavigate(const URL: OleVariant; var Cancel: WordBool);
5 daniel-mar 154
    procedure SynEditFocusTimerTimer(Sender: TObject);
13 daniel-mar 155
    procedure ActionFindExecute(Sender: TObject);
156
    procedure ActionReplaceExecute(Sender: TObject);
157
    procedure ActionFindNextExecute(Sender: TObject);
158
    procedure ActionGotoExecute(Sender: TObject);
159
    procedure ActionSaveExecute(Sender: TObject);
160
    procedure ActionHelpExecute(Sender: TObject);
161
    procedure ActionRunExecute(Sender: TObject);
162
    procedure ActionESCExecute(Sender: TObject);
163
    procedure SynEdit1MouseWheelDown(Sender: TObject; Shift: TShiftState;
164
      MousePos: TPoint; var Handled: Boolean);
165
    procedure SynEdit1MouseWheelUp(Sender: TObject; Shift: TShiftState;
166
      MousePos: TPoint; var Handled: Boolean);
15 daniel-mar 167
    procedure ActionOpenExecute(Sender: TObject);
168
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
17 daniel-mar 169
    procedure Memo2KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
22 daniel-mar 170
    procedure ActionFindPrevExecute(Sender: TObject);
23 daniel-mar 171
    procedure SynEdit1MouseCursor(Sender: TObject;
172
      const aLineCharPos: TBufferCoord; var aCursor: TCursor);
173
    procedure Timer1Timer(Sender: TObject);
174
    procedure ActionSpaceToTabExecute(Sender: TObject);
27 daniel-mar 175
    procedure TreeView1DblClick(Sender: TObject);
30 daniel-mar 176
    procedure SynEdit1GutterClick(Sender: TObject; Button: TMouseButton; X, Y,
177
      Line: Integer; Mark: TSynEditMark);
31 daniel-mar 178
    procedure SynEdit1PaintTransient(Sender: TObject; Canvas: TCanvas;
179
      TransientType: TTransientType);
33 daniel-mar 180
    procedure ActionLintExecute(Sender: TObject);
45 daniel-mar 181
    procedure ActionRunConsoleExecute(Sender: TObject);
57 daniel-mar 182
    procedure BtnSpecialCharsClick(Sender: TObject);
60 daniel-mar 183
    procedure WebBrowser1WindowClosing(ASender: TObject;
184
      IsChildWindow: WordBool; var Cancel: WordBool);
62 daniel-mar 185
    procedure BtnLightClick(Sender: TObject);
186
    procedure StartUpTimerTimer(Sender: TObject);
83 daniel-mar 187
    procedure FileModTimerTimer(Sender: TObject);
89 daniel-mar 188
    procedure SynEdit1DropFiles(Sender: TObject; X, Y: Integer;
189
      AFiles: TStrings);
91 daniel-mar 190
    procedure ActionSaveAsExecute(Sender: TObject);
191
    procedure ActionGoToPHPDirExecute(Sender: TObject);
192
    procedure ActionPHPInteractiveShellExecute(Sender: TObject);
98 daniel-mar 193
    procedure SynEdit1KeyDown(Sender: TObject; var Key: Word;
194
      Shift: TShiftState);
99 daniel-mar 195
    procedure FontSizeTimerTimer(Sender: TObject);
103 daniel-mar 196
    procedure SynEdit1StatusChange(Sender: TObject; Changes: TSynStatusChanges);
114 daniel-mar 197
    procedure ChooseanotherPHPinterpreter1Click(Sender: TObject);
198
    procedure ChooseanotherCHMhelpfile1Click(Sender: TObject);
2 daniel-mar 199
  private
67 daniel-mar 200
    hMutex: THandle;
2 daniel-mar 201
    CurSearchTerm: string;
202
    HlpPrevPageIndex: integer;
24 daniel-mar 203
    SrcRep: TSynEditFindReplace;
23 daniel-mar 204
    {$IFDEF OnlineHelp}
205
    gOnlineHelpWord: string;
206
    {$ENDIF}
83 daniel-mar 207
    FileModLast: TDateTime;
91 daniel-mar 208
    FormShowRanOnce: boolean;
209
    BrowserLoadedOnce: boolean;
114 daniel-mar 210
    procedure Help(AForceSelectNewHelpFile: boolean=false);
40 daniel-mar 211
    function InputRequestCallback(var data: AnsiString): boolean;
212
    function OutputNotifyCallback(const data: AnsiString): boolean;
67 daniel-mar 213
    procedure RightTrimAll;
2 daniel-mar 214
  protected
215
    ChmIndex: TMemIniFile;
19 daniel-mar 216
    FScrapFile: string;
56 daniel-mar 217
    FSaveAsFilename: string;
27 daniel-mar 218
    codeExplorer: TRunCodeExplorer;
219
    procedure GotoLineNo(LineNo: integer);
2 daniel-mar 220
    function GetScrapFile: string;
27 daniel-mar 221
    procedure StartCodeExplorer;
47 daniel-mar 222
    procedure RefreshModifySign;
62 daniel-mar 223
    procedure Theme_Light;
224
    procedure Theme_Dark;
225
    function IsThemeDark: boolean;
67 daniel-mar 226
    function MarkUpLineReference(cont: string): string;
96 daniel-mar 227
    function ContainsUnicodeCharsInAnsiFile: boolean;
76 daniel-mar 228
    procedure SaveToFile(filename: string);
2 daniel-mar 229
  end;
230
 
231
var
232
  Form1: TForm1;
233
 
234
implementation
235
 
236
{$R *.dfm}
237
 
30 daniel-mar 238
{$R Cursors.res}
239
 
2 daniel-mar 240
uses
105 daniel-mar 241
  Functions, StrUtils, FastPHPUtils, Math, ShellAPI, RichEdit,
49 daniel-mar 242
  FastPHPTreeView, ImageListEx, FastPHPConfig;
2 daniel-mar 243
 
30 daniel-mar 244
const
245
  crMouseGutter = 1;
246
 
47 daniel-mar 247
procedure TForm1.RefreshModifySign;
248
var
249
  tmp: string;
250
begin
102 daniel-mar 251
  if SynEdit1 = nil then exit;
252
 
47 daniel-mar 253
  tmp := Caption;
254
 
255
  tmp := StringReplace(tmp, '*', '', [rfReplaceAll]);
256
  if SynEdit1.Modified then tmp := tmp + '*';
257
 
258
  if Caption <> tmp then Caption := tmp;
259
end;
260
 
13 daniel-mar 261
procedure TForm1.ActionFindNextExecute(Sender: TObject);
262
begin
263
  SrcRep.FindNext;
264
end;
265
 
22 daniel-mar 266
procedure TForm1.ActionFindPrevExecute(Sender: TObject);
267
begin
268
  SrcRep.FindPrev;
269
end;
270
 
13 daniel-mar 271
procedure TForm1.ActionGotoExecute(Sender: TObject);
5 daniel-mar 272
var
273
  val: string;
274
  lineno: integer;
91 daniel-mar 275
resourcestring
276
  SGoTo = 'Go to';
277
  SLineNumber = 'Line number:';
5 daniel-mar 278
begin
13 daniel-mar 279
  // TODO: VK_LMENU does not work! only works with AltGr but not Alt
280
  // http://stackoverflow.com/questions/16828250/delphi-xe2-how-to-prevent-the-alt-key-stealing-focus ?
5 daniel-mar 281
 
91 daniel-mar 282
  if not InputQuery(SGoTo, SLineNumber, val) or
283
     not TryStrToInt(val, lineno) then
13 daniel-mar 284
  begin
285
    if SynEdit1.CanFocus then SynEdit1.SetFocus;
286
    exit;
287
  end;
288
  GotoLineNo(lineno);
289
end;
5 daniel-mar 290
 
91 daniel-mar 291
procedure TForm1.ActionGoToPHPDirExecute(Sender: TObject);
292
var
293
  phpExe: string;
294
begin
295
  phpExe := GetPHPExe;
296
  if phpExe <> '' then
297
    ShellExecute(Handle, 'open', 'explorer.exe', PChar(ExtractFilePath(phpExe)), '', SW_NORMAL);
298
end;
299
 
13 daniel-mar 300
procedure TForm1.ActionHelpExecute(Sender: TObject);
301
begin
302
  Help;
303
  if PageControl2.ActivePage = HelpTabsheet then
304
    WebBrowser2.SetFocus
20 daniel-mar 305
  else if PageControl2.ActivePage = CodeTabsheet then
13 daniel-mar 306
    SynEdit1.SetFocus;
307
end;
8 daniel-mar 308
 
33 daniel-mar 309
procedure TForm1.ActionLintExecute(Sender: TObject);
310
begin
311
  Run(Sender);
312
  SynEdit1.SetFocus;
313
end;
314
 
15 daniel-mar 315
procedure TForm1.ActionOpenExecute(Sender: TObject);
316
begin
317
  If OpenDialog3.Execute then
318
  begin
62 daniel-mar 319
    ShellExecute(0, 'open', PChar(ParamStr(0)), PChar('"' + OpenDialog3.FileName + '"'), '', SW_NORMAL);
15 daniel-mar 320
  end;
321
end;
322
 
91 daniel-mar 323
procedure TForm1.ActionPHPInteractiveShellExecute(Sender: TObject);
324
var
325
  phpExe: string;
326
begin
327
  phpExe := GetPHPExe;
328
  if phpExe <> '' then
329
    ShellExecute(Handle, 'open', PChar(phpExe), '-a', PChar(ExtractFilePath(phpExe)), SW_NORMAL);
330
end;
331
 
13 daniel-mar 332
procedure TForm1.ActionReplaceExecute(Sender: TObject);
333
begin
334
  SrcRep.ReplaceExecute;
335
end;
5 daniel-mar 336
 
45 daniel-mar 337
procedure TForm1.ActionRunConsoleExecute(Sender: TObject);
338
begin
339
  RunConsole(Sender);
340
  SynEdit1.SetFocus;
341
end;
342
 
13 daniel-mar 343
procedure TForm1.ActionRunExecute(Sender: TObject);
344
begin
345
  Run(Sender);
346
  SynEdit1.SetFocus;
347
end;
5 daniel-mar 348
 
67 daniel-mar 349
procedure TForm1.RightTrimAll;
350
var
351
  i: integer;
352
begin
68 daniel-mar 353
  for i := 0 to SynEdit1.Lines.Count-1 do
67 daniel-mar 354
  begin
355
    SynEdit1.Lines.Strings[i] := TrimRight(SynEdit1.Lines.Strings[i]);
356
  end;
77 daniel-mar 357
 
358
  (*
359
  while (SynEdit1.Lines.Count > 0) and (SynEdit1.Lines.Strings[SynEdit1.Lines.Count-1] = '') do
360
  begin
361
    SynEdit1.Lines.Delete(SynEdit1.Lines.Count-1);
362
  end;
363
  if SynEdit1.SelStart > Length(SynEdit1.Text)-1 then
364
  begin
365
    // TODO: This code does not work...
366
    SynEdit1.SelStart := Length(SynEdit1.Text)-1;
367
    SynEdit1.SelEnd   := Length(SynEdit1.Text)-1;
368
  end;
369
  *)
67 daniel-mar 370
end;
371
 
91 daniel-mar 372
procedure TForm1.ActionSaveAsExecute(Sender: TObject);
373
var
374
  hMutexNew: THandle;
375
resourcestring
376
  SCannotSaveBecauseMutex = 'Cannot save because file "%s", because it is alrady open in another FastPHP window!';
377
begin
378
  if SaveDialog1.Execute then
379
  begin
380
    {$REGION 'Switch mutex'}
381
    hMutexNew := CreateMutex(nil, True, PChar('FastPHP'+md5(UpperCase(SaveDialog1.FileName))));
382
    if GetLastError = ERROR_ALREADY_EXISTS then
383
    begin
384
      ShowMessageFmt(SCannotSaveBecauseMutex, [SaveDialog1.FileName]);
385
      Close;
386
    end;
387
 
388
    if hMutex <> 0 then CloseHandle(hMutex); // Note: ReleaseMutex does not work as expected!
389
    hMutex := hMutexNew;
390
    {$ENDREGION}
391
 
392
    FSaveAsFilename := SaveDialog1.FileName;
393
    Caption := Copy(Caption, 1, Pos(' - ', Caption)-1) + ' - ' + FSaveAsFilename;
394
    Application.Title := Format('%s - FastPHP', [ExtractFileName(FSaveAsFilename)]); // do not translate!
395
    Button7.Click;
396
  end;
397
end;
398
 
13 daniel-mar 399
procedure TForm1.ActionSaveExecute(Sender: TObject);
400
begin
67 daniel-mar 401
  RightTrimAll;
76 daniel-mar 402
  SaveToFile(GetScrapFile);
16 daniel-mar 403
  SynEdit1.Modified := false;
47 daniel-mar 404
  RefreshModifySign;
68 daniel-mar 405
  if SynEdit1.CanFocus then SynEdit1.SetFocus;
13 daniel-mar 406
end;
407
 
23 daniel-mar 408
procedure TForm1.ActionSpaceToTabExecute(Sender: TObject);
409
 
410
    function SpacesAtBeginning(line: string): integer;
411
    begin
412
      result := 0;
44 daniel-mar 413
      if Trim(line) = '' then exit;
23 daniel-mar 414
      while line[result+1] = ' ' do
415
      begin
416
        inc(result);
417
      end;
418
    end;
419
 
44 daniel-mar 420
    function GuessIndent(lines: {$IFDEF UNICODE}TStrings{$ELSE}TUnicodeStrings{$ENDIF}): integer;
23 daniel-mar 421
      function _Check(indent: integer): boolean;
422
      var
423
        i: integer;
424
      begin
425
        result := true;
426
        for i := 0 to lines.Count-1 do
427
          if SpacesAtBeginning(lines.Strings[i]) mod indent <> 0 then
428
          begin
429
            // ShowMessageFmt('Zeile "%s" nicht durch %d teilbar!', [lines.strings[i], indent]);
44 daniel-mar 430
            result := false;
431
            exit;
23 daniel-mar 432
          end;
433
      end;
434
    var
435
      i: integer;
436
    begin
437
      for i := 8 downto 2 do
438
      begin
44 daniel-mar 439
        if _Check(i) then
440
        begin
441
          result := i;
442
          exit;
443
        end;
23 daniel-mar 444
      end;
445
      result := -1;
446
    end;
447
 
44 daniel-mar 448
    procedure SpaceToTab(lines: {$IFDEF UNICODE}TStrings{$ELSE}TUnicodeStrings{$ENDIF}; indent: integer);
23 daniel-mar 449
    var
450
      i, spaces: integer;
93 daniel-mar 451
      newval: string;
452
      somethingchanged: boolean;
23 daniel-mar 453
    begin
93 daniel-mar 454
      somethingchanged := false;
23 daniel-mar 455
      for i := 0 to lines.Count-1 do
456
      begin
457
        spaces := SpacesAtBeginning(lines.Strings[i]);
93 daniel-mar 458
        newval := StringOfChar(#9, spaces div indent) + StringOfChar(' ', spaces mod indent) + Copy(lines.Strings[i], spaces+1, Length(lines.Strings[i])-spaces);
459
        if lines.Strings[i] <> newval then
460
        begin
461
          somethingchanged := true;
462
          lines.Strings[i] := newval;
463
        end;
23 daniel-mar 464
      end;
93 daniel-mar 465
      if somethingchanged then
466
      begin
105 daniel-mar 467
        SynEdit1.Modified := true; // seems to be required by Delphi 10.x
103 daniel-mar 468
        RefreshModifySign;
93 daniel-mar 469
      end;
23 daniel-mar 470
    end;
471
 
44 daniel-mar 472
    function SpacesAvailable(lines: {$IFDEF UNICODE}TStrings{$ELSE}TUnicodeStrings{$ENDIF}): boolean;
23 daniel-mar 473
    var
474
      i, spaces: integer;
475
    begin
476
      for i := 0 to lines.Count-1 do
477
      begin
478
        spaces := SpacesAtBeginning(lines.Strings[i]);
44 daniel-mar 479
        if spaces > 0 then
480
        begin
481
          result := true;
482
          exit;
483
        end;
23 daniel-mar 484
      end;
44 daniel-mar 485
      result := false;
486
      exit;
23 daniel-mar 487
    end;
488
 
489
var
490
  val: string;
491
  ind: integer;
492
resourcestring
493
  SNoLinesAvailable = 'No lines with spaces at the beginning available';
91 daniel-mar 494
  SSpacesToLabs = 'Spaces to tabs';
495
  SIndent = 'Indent:';
23 daniel-mar 496
begin
497
  // TODO: if something is selected, only process the selected part
498
 
499
  if not SpacesAvailable(SynEdit1.Lines) then
500
  begin
49 daniel-mar 501
    MessageDlg(SNoLinesAvailable, mtInformation, [mbOk], 0);
23 daniel-mar 502
    exit;
503
  end;
504
 
505
  ind := GuessIndent(SynEdit1.Lines);
506
  if ind <> -1 then val := IntToStr(ind);
507
 
91 daniel-mar 508
  if not InputQuery(SSpacesToLabs, SIndent, val) or
509
     not TryStrToInt(Trim(val), ind) then
23 daniel-mar 510
  begin
91 daniel-mar 511
    if SynEdit1.CanFocus then SynEdit1.SetFocus;
512
    exit;
23 daniel-mar 513
  end;
514
 
91 daniel-mar 515
  if ind = 0 then exit;
516
  SpaceToTab(SynEdit1.Lines, ind);
23 daniel-mar 517
end;
518
 
13 daniel-mar 519
procedure TForm1.ActionESCExecute(Sender: TObject);
520
begin
521
  if (HlpPrevPageIndex <> -1) and (PageControl2.ActivePage = HelpTabSheet) and
522
     (HelpTabsheet.TabVisible) then
523
  begin
524
    PageControl2.ActivePageIndex := HlpPrevPageIndex;
525
    HelpTabsheet.TabVisible := false;
2 daniel-mar 526
  end;
13 daniel-mar 527
 
528
  // Dirty hack...
22 daniel-mar 529
  SrcRep.CloseDialogs;
2 daniel-mar 530
end;
531
 
13 daniel-mar 532
procedure TForm1.ActionFindExecute(Sender: TObject);
533
begin
534
  SrcRep.FindExecute;
535
end;
536
 
2 daniel-mar 537
procedure TForm1.Run(Sender: TObject);
16 daniel-mar 538
var
539
  bakTS: TTabSheet;
67 daniel-mar 540
  //ss: TStringStream;
541
  //bakPos: Int64;
2 daniel-mar 542
begin
5 daniel-mar 543
  memo2.Lines.Text := '';
16 daniel-mar 544
 
91 daniel-mar 545
  if not BrowserLoadedOnce then
16 daniel-mar 546
  begin
547
    bakTS := PageControl1.ActivePage;
548
    try
549
      PageControl1.ActivePage := HtmlTabSheet; // Required for the first time, otherwise, WebBrowser1.Clear will hang
550
      Webbrowser1.Clear;
551
    finally
552
      PageControl1.ActivePage := bakTS;
553
    end;
91 daniel-mar 554
    BrowserLoadedOnce := true;
16 daniel-mar 555
  end
556
  else
557
    Webbrowser1.Clear;
558
 
91 daniel-mar 559
  Screen.Cursor := crHourGlass; // TODO: Doesn't work with F9
5 daniel-mar 560
  Application.ProcessMessages;
561
 
562
  try
47 daniel-mar 563
    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.
5 daniel-mar 564
 
94 daniel-mar 565
    // TODO 70421 * <fastphp> implement flush() with ContentCallBack implementieren... For long running scripts I want to see status changes via javascript which are loaded step by step
566
    // TODO 70422 * <fastphp> when a script has an endless loop, i want to have a possibility to cancel it
567
    if SynEdit1.Lines.Encoding = TEncoding.UTF8 then
568
      memo2.Lines.Text := Utf8Decode(RunPHPScript(GetScrapFile, Sender=ActionLint, False)) // if we have a UTF-8 file, then the DOS output is double-UTF8 encoded
569
    else
570
      memo2.Lines.Text := RunPHPScript(GetScrapFile, Sender=ActionLint, False);
5 daniel-mar 571
 
72 daniel-mar 572
    {$REGION 'Show in Web Browser'}
94 daniel-mar 573
    if SynEdit1.Lines.Encoding = TEncoding.UTF8 then
574
      Webbrowser1.LoadHTML(MarkUpLineReference('<meta charset="utf-8">'+memo2.Lines.Text), GetScrapFile)
575
    else
576
      Webbrowser1.LoadHTML(MarkUpLineReference(memo2.Lines.Text), GetScrapFile);
5 daniel-mar 577
 
62 daniel-mar 578
    // Alternatively:
579
    (*
580
    ss := TstringStream.Create;
581
    ss.WriteString(MarkUpLineReference(memo2.Lines.Text));
582
    ss.Position := 0;
583
    Webbrowser1.LoadStream(ss, GetScrapFile);
584
    Webbrowser1.Wait;
585
    ss.Free;
586
    *)
72 daniel-mar 587
    {$ENDREGION}
62 daniel-mar 588
 
5 daniel-mar 589
    if IsTextHTML(memo2.lines.text) then
590
      PageControl1.ActivePage := HtmlTabSheet
591
    else
592
      PageControl1.ActivePage := PlaintextTabSheet;
593
  finally
594
    Screen.Cursor := crDefault;
2 daniel-mar 595
  end;
5 daniel-mar 596
end;
2 daniel-mar 597
 
45 daniel-mar 598
procedure TForm1.RunConsole(Sender: TObject);
599
begin
47 daniel-mar 600
  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.
45 daniel-mar 601
  RunPHPScript(GetScrapFile, Sender=ActionLint, True);
602
end;
603
 
89 daniel-mar 604
procedure TForm1.SynEdit1DropFiles(Sender: TObject; X, Y: Integer;
605
  AFiles: TStrings);
606
var
607
  FileName: string;
608
const
609
  WARN_FILE_COUNT = 10;
91 daniel-mar 610
resourcestring
611
  SAreYouSure = 'Are you sure you want to open %d files?';
89 daniel-mar 612
begin
613
  if AFiles.Count > WARN_FILE_COUNT then
614
  begin
91 daniel-mar 615
    if not MessageDlg(Format(SAreYouSure, [WARN_FILE_COUNT]), mtConfirmation, mbYesNoCancel, 0) <> mrYes then
89 daniel-mar 616
      exit;
617
  end;
618
 
619
  for FileName in AFiles do
620
  begin
621
    ShellExecute(0, 'open', PChar(ParamStr(0)), PChar('"' + FileName + '"'), '', SW_NORMAL);
622
  end;
623
end;
624
 
30 daniel-mar 625
procedure TForm1.SynEdit1GutterClick(Sender: TObject; Button: TMouseButton; X,
626
  Y, Line: Integer; Mark: TSynEditMark);
627
begin
628
  (*
629
  TSynEdit(Sender).CaretX := 1;
630
  TSynEdit(Sender).CaretY := Line;
631
  TSynEdit(Sender).SelLength := Length(TSynEdit(Sender).LineText);
632
  *)
633
end;
634
 
98 daniel-mar 635
procedure TForm1.SynEdit1KeyDown(Sender: TObject; var Key: Word;
636
  Shift: TShiftState);
637
begin
638
  if (Shift = [ssCtrl, ssShift]) and (Key = ord('C')) then
639
  begin
640
    // Disable "Column Select Mode" (https://github.com/SynEdit/SynEdit/blob/master/Source/SynEditKeyCmds.pas#L879)
641
    // which can be enabled by pressing Ctrl+Shift+C
642
    // Reasons why we disable it:
643
    // 1. I think nobody needs this, and sometimes you accidentally press it
644
    // 2. Ctrl+Shift+L would the combination to disable column select mode,
645
    //    but you cannot use it, because it is already in use for linting
646
    Key := 0;
647
  end;
648
end;
649
 
23 daniel-mar 650
procedure TForm1.SynEdit1MouseCursor(Sender: TObject; const aLineCharPos: TBufferCoord; var aCursor: TCursor);
651
{$IFDEF OnlineHelp}
652
var
653
  Line: Integer;
654
  Column: Integer;
655
  word: string;
656
begin
657
  Line  := aLineCharPos.Line-1;
658
  Column := aLineCharPos.Char-1;
659
  word := GetWordUnderPos(TSynEdit(Sender), Line, Column);
660
  if word <> gOnlineHelpWord then
661
  begin
662
    gOnlineHelpWord := word;
663
    Timer1.Enabled := false;
664
    Timer1.Enabled := true;
665
  end;
666
{$ELSE}
667
begin
668
{$ENDIF}
669
end;
670
 
13 daniel-mar 671
procedure TForm1.SynEdit1MouseWheelDown(Sender: TObject; Shift: TShiftState;
672
  MousePos: TPoint; var Handled: Boolean);
673
begin
674
  if ssCtrl in Shift then
675
  begin
676
    SynEdit1.Font.Size := Max(SynEdit1.Font.Size - 1, 5);
23 daniel-mar 677
    Handled := true;
678
  end
679
  else Handled := false;
13 daniel-mar 680
end;
681
 
682
procedure TForm1.SynEdit1MouseWheelUp(Sender: TObject; Shift: TShiftState;
683
  MousePos: TPoint; var Handled: Boolean);
684
begin
685
  if ssCtrl in Shift then
686
  begin
687
    SynEdit1.Font.Size := SynEdit1.Font.Size + 1;
23 daniel-mar 688
    Handled := true;
689
  end
690
  else Handled := false;
13 daniel-mar 691
end;
692
 
31 daniel-mar 693
procedure TForm1.SynEdit1PaintTransient(Sender: TObject; Canvas: TCanvas; TransientType: TTransientType);
694
var
695
  Editor: TSynEdit;
696
  OpenChars: array of WideChar;//[0..2] of WideChar=();
697
  CloseChars: array of WideChar;//[0..2] of WideChar=();
698
 
699
  function IsCharBracket(AChar: WideChar): Boolean;
700
  begin
701
    case AChar of
702
      '{','[','(','<','}',']',')','>':
703
        Result := True;
704
      else
705
        Result := False;
706
    end;
707
  end;
708
 
709
  function CharToPixels(P: TBufferCoord): TPoint;
710
  begin
711
    Result := Editor.RowColumnToPixels(Editor.BufferToDisplayPos(P));
712
  end;
713
 
714
var
63 daniel-mar 715
  COLOR_FG: TColor;
716
  COLOR_BG: TColor;
31 daniel-mar 717
  P: TBufferCoord;
718
  Pix: TPoint;
719
  D: TDisplayCoord;
720
  S: UnicodeString;
721
  I: Integer;
722
  Attri: TSynHighlighterAttributes;
723
  ArrayLength: Integer;
724
  start: Integer;
725
  TmpCharA, TmpCharB: WideChar;
63 daniel-mar 726
begin
31 daniel-mar 727
  // Source: https://github.com/SynEdit/SynEdit/blob/master/Demos/OnPaintTransientDemo/Unit1.pas
728
 
63 daniel-mar 729
  if IsThemeDark then
730
  begin
731
    COLOR_FG := clLime;
732
    COLOR_BG := clGreen;
733
  end
734
  else
735
  begin
736
    COLOR_FG := clGreen;
737
    COLOR_BG := clLime;
738
  end;
739
 
31 daniel-mar 740
  if TSynEdit(Sender).SelAvail then exit;
741
  Editor := TSynEdit(Sender);
742
  ArrayLength:= 3;
743
 
744
  (*
745
  if (Editor.Highlighter = shHTML) or (Editor.Highlighter = shXML) then
746
    inc(ArrayLength);
747
  *)
748
 
749
  SetLength(OpenChars, ArrayLength);
750
  SetLength(CloseChars, ArrayLength);
751
  for i := 0 to ArrayLength - 1 do
752
  begin
753
    case i of
754
      0: begin OpenChars[i] := '('; CloseChars[i] := ')'; end;
755
      1: begin OpenChars[i] := '{'; CloseChars[i] := '}'; end;
756
      2: begin OpenChars[i] := '['; CloseChars[i] := ']'; end;
757
      3: begin OpenChars[i] := '<'; CloseChars[i] := '>'; end;
758
    end;
759
  end;
760
 
761
  P := Editor.CaretXY;
762
  D := Editor.DisplayXY;
763
 
764
  Start := Editor.SelStart;
765
 
766
  if (Start > 0) and (Start <= length(Editor.Text)) then
767
    TmpCharA := Editor.Text[Start]
768
  else
769
    TmpCharA := #0;
770
 
44 daniel-mar 771
  if (Start > 0){Added by VTS} and (Start < length(Editor.Text)) then
31 daniel-mar 772
    TmpCharB := Editor.Text[Start + 1]
773
  else
774
    TmpCharB := #0;
775
 
776
  if not IsCharBracket(TmpCharA) and not IsCharBracket(TmpCharB) then exit;
777
  S := TmpCharB;
778
  if not IsCharBracket(TmpCharB) then
779
  begin
780
    P.Char := P.Char - 1;
781
    S := TmpCharA;
782
  end;
783
  Editor.GetHighlighterAttriAtRowCol(P, S, Attri);
784
 
785
  if (Editor.Highlighter.SymbolAttribute = Attri) then
786
  begin
787
    for i := low(OpenChars) to High(OpenChars) do
788
    begin
789
      if (S = OpenChars[i]) or (S = CloseChars[i]) then
790
      begin
791
        Pix := CharToPixels(P);
792
 
793
        Editor.Canvas.Brush.Style := bsSolid;//Clear;
794
        Editor.Canvas.Font.Assign(Editor.Font);
795
        Editor.Canvas.Font.Style := Attri.Style;
796
 
797
        if (TransientType = ttAfter) then
798
        begin
799
          Editor.Canvas.Font.Color := COLOR_FG;
800
          Editor.Canvas.Brush.Color := COLOR_BG;
801
        end
802
        else
803
        begin
804
          Editor.Canvas.Font.Color := Attri.Foreground;
805
          Editor.Canvas.Brush.Color := Attri.Background;
806
        end;
807
        if Editor.Canvas.Font.Color = clNone then
808
          Editor.Canvas.Font.Color := Editor.Font.Color;
809
        if Editor.Canvas.Brush.Color = clNone then
810
          Editor.Canvas.Brush.Color := Editor.Color;
811
 
812
        Editor.Canvas.TextOut(Pix.X, Pix.Y, S);
813
        P := Editor.GetMatchingBracketEx(P);
814
 
815
        if (P.Char > 0) and (P.Line > 0) then
816
        begin
817
          Pix := CharToPixels(P);
105 daniel-mar 818
          {$IF CompilerVersion >= 35.0} // Added by ViaThinkSoft (not sure about the exact version)
819
          if Pix.X > Editor.Gutter.RealGutterWidth then // Added by ViaThinkSoft (not sure about the exact version)
820
          {$ELSE}
31 daniel-mar 821
          if Pix.X > Editor.Gutter.Width then
105 daniel-mar 822
          {$IFEND}
31 daniel-mar 823
          begin
824
            {$REGION 'Added by ViaThinkSoft'}
825
            if (TransientType = ttAfter) then
826
            begin
827
              Editor.Canvas.Font.Color := COLOR_FG;
828
              Editor.Canvas.Brush.Color := COLOR_BG;
829
            end
830
            else
831
            begin
832
              Editor.Canvas.Font.Color := Attri.Foreground;
833
              Editor.Canvas.Brush.Color := Attri.Background;
834
            end;
835
            if Editor.Canvas.Font.Color = clNone then
836
              Editor.Canvas.Font.Color := Editor.Font.Color;
837
            if Editor.Canvas.Brush.Color = clNone then
838
              Editor.Canvas.Brush.Color := Editor.Color;
839
            {$ENDREGION}
840
            if S = OpenChars[i] then
841
              Editor.Canvas.TextOut(Pix.X, Pix.Y, CloseChars[i])
842
            else Editor.Canvas.TextOut(Pix.X, Pix.Y, OpenChars[i]);
843
          end;
844
        end;
845
      end;
846
    end;
847
    Editor.Canvas.Brush.Style := bsSolid;
848
  end;
849
end;
850
 
103 daniel-mar 851
procedure TForm1.SynEdit1StatusChange(Sender: TObject;
852
  Changes: TSynStatusChanges);
853
begin
854
  if scModified in Changes then
855
    RefreshModifySign;
856
end;
857
 
5 daniel-mar 858
procedure TForm1.SynEditFocusTimerTimer(Sender: TObject);
859
begin
860
  SynEditFocusTimer.Enabled := false;
861
  Button1.SetFocus; // Workaround for weird bug... This (and the timer) is necessary to get the focus to SynEdit1
862
  SynEdit1.SetFocus;
863
end;
2 daniel-mar 864
 
62 daniel-mar 865
procedure TForm1.Theme_Dark;
866
begin
867
  if IsThemeDark then exit;
91 daniel-mar 868
  TStyleManager.TrySetStyle('Windows10 SlateGray'); // do not translate
62 daniel-mar 869
  Color := 1316887;
870
  Font.Color := clCream;
871
  //Memo2.Font.Color := clCream;
872
  //Memo2.ParentColor := true;
873
  SynEdit1.ActiveLineColor := 2238502;
874
  SynEdit1.Color := 1316887;
875
  SynEdit1.Font.Color := clCream;
876
  SynEdit1.Gutter.Color := 1316887;
877
  SynEdit1.Gutter.Font.Color := clCream;
878
  SynEdit1.Gutter.GradientStartColor := 2238502;
879
  SynEdit1.Gutter.GradientEndColor := 1316887;
64 daniel-mar 880
  SynPHPSyn1.CommentAttri.Foreground := $00837B82;
62 daniel-mar 881
  SynPHPSyn1.IdentifierAttri.Foreground := 9627120;
882
  SynPHPSyn1.KeyAttri.Foreground := 4157595;
883
  SynPHPSyn1.NumberAttri.Foreground := 5008079;
884
  SynPHPSyn1.StringAttri.Foreground := 6987151;
885
  SynPHPSyn1.SymbolAttri.Foreground := 8769754;
886
  SynPHPSyn1.VariableAttri.Foreground := 6924493;
887
end;
888
 
889
procedure TForm1.Theme_Light;
890
begin
891
  if not IsThemeDark then exit;
91 daniel-mar 892
  TStyleManager.TrySetStyle('Windows'); // do not translate
62 daniel-mar 893
  Color := clBtnFace;
894
  Font.Color := clWindowText;
895
  //Memo2.Font.Color := clWindowText;
896
  SynEdit1.ActiveLineColor := 14680010;
91 daniel-mar 897
  SynEdit1.Color := clCream;
62 daniel-mar 898
  SynEdit1.Font.Color := clWindowText;
899
  SynEdit1.Gutter.Color := clBtnFace;
900
  SynEdit1.Gutter.Font.Color := clWindowText;
901
  SynEdit1.Gutter.GradientStartcolor := cl3dLight;
902
  SynEdit1.Gutter.GradientEndColor := clBtnFace;;
903
  SynPHPSyn1.CommentAttri.Foreground := 33023;
904
  SynPHPSyn1.IdentifierAttri.Foreground := 4194304;
905
  SynPHPSyn1.KeyAttri.Foreground := 4227072;
906
  SynPHPSyn1.NumberAttri.Foreground := 213;
907
  SynPHPSyn1.StringAttri.Foreground := 13762560;
908
  SynPHPSyn1.SymbolAttri.Foreground := 4227072;
909
  SynPHPSyn1.VariableAttri.Foreground := 213;
910
end;
911
 
23 daniel-mar 912
procedure TForm1.Timer1Timer(Sender: TObject);
913
begin
914
  {$IFDEF OnlineHelp}
915
  Timer1.Enabled := false;
916
 
917
  // TODO: Insert a small online help hint
918
  //Caption := gOnlineHelpWord;
919
  {$ENDIF}
920
end;
921
 
27 daniel-mar 922
procedure TForm1.TreeView1DblClick(Sender: TObject);
923
var
924
  tn: TTreeNode;
32 daniel-mar 925
  lineNo: integer;
27 daniel-mar 926
begin
927
  tn := TTreeView(Sender).Selected;
928
  if tn = nil then exit;
32 daniel-mar 929
  lineNo := Integer(tn.Data);
930
  if lineNo > 0 then GotoLineNo(lineNo);
27 daniel-mar 931
end;
932
 
45 daniel-mar 933
(*
44 daniel-mar 934
{$IFDEF USE_SHDOCVW_TLB}
45 daniel-mar 935
*)
5 daniel-mar 936
procedure TForm1.WebBrowser1BeforeNavigate2(ASender: TObject;
27 daniel-mar 937
  const pDisp: IDispatch; const URL, Flags, TargetFrameName, PostData,
5 daniel-mar 938
  Headers: OleVariant; var Cancel: WordBool);
44 daniel-mar 939
begin
940
  BeforeNavigate(URL, Cancel);
941
end;
45 daniel-mar 942
(*
44 daniel-mar 943
{$ELSE}
944
procedure TForm1.WebBrowser1BeforeNavigate2(ASender: TObject;
945
  const pDisp: IDispatch; var URL, Flags, TargetFrameName, PostData,
946
  Headers: OleVariant; var Cancel: WordBool);
947
begin
948
  BeforeNavigate(URL, Cancel);
949
end;
950
{$ENDIF}
45 daniel-mar 951
*)
44 daniel-mar 952
 
67 daniel-mar 953
procedure TForm1.WebBrowser1WindowClosing(ASender: TObject;
954
  IsChildWindow: WordBool; var Cancel: WordBool);
955
resourcestring
91 daniel-mar 956
  SCloseRequest = 'A script has requested the window to be closed. The window of a standalone script would now close.';
67 daniel-mar 957
begin
91 daniel-mar 958
  ShowMessage(SCloseRequest);
67 daniel-mar 959
  TWebBrowser(ASender).Clear;
960
  Cancel := true;
961
end;
962
 
44 daniel-mar 963
procedure TForm1.BeforeNavigate(const URL: OleVariant; var Cancel: WordBool);
5 daniel-mar 964
var
8 daniel-mar 965
  s, myURL: string;
5 daniel-mar 966
  lineno: integer;
7 daniel-mar 967
  p: integer;
5 daniel-mar 968
begin
7 daniel-mar 969
  {$REGION 'Line number references (PHP errors and warnings)'}
8 daniel-mar 970
  if Copy(URL, 1, length(FASTPHP_GOTO_URI_PREFIX)) = FASTPHP_GOTO_URI_PREFIX then
5 daniel-mar 971
  begin
972
    try
8 daniel-mar 973
      s := copy(URL, length(FASTPHP_GOTO_URI_PREFIX)+1, 99);
5 daniel-mar 974
      if not TryStrToInt(s, lineno) then exit;
975
      GotoLineNo(lineno);
976
      SynEditFocusTimer.Enabled := true;
977
    finally
978
      Cancel := true;
979
    end;
8 daniel-mar 980
    Exit;
5 daniel-mar 981
  end;
7 daniel-mar 982
  {$ENDREGION}
983
 
62 daniel-mar 984
  {$REGION 'Intelligent browser (executes PHP scripts which are clicked in a hyperlink)'}
7 daniel-mar 985
  if URL <> 'about:blank' then
986
  begin
987
    myUrl := URL;
988
 
8 daniel-mar 989
    p := Pos('?', myUrl);
990
    if p >= 1 then myURL := copy(myURL, 1, p-1);
7 daniel-mar 991
 
8 daniel-mar 992
    // TODO: myURL urldecode
993
    // TODO: maybe we could even open that file in the editor!
62 daniel-mar 994
    // TODO: ?parameter=....
7 daniel-mar 995
 
8 daniel-mar 996
    if FileExists(myURL) and (EndsText('.php', myURL) or EndsText('.php3', myURL) or EndsText('.php4', myURL) or EndsText('.php5', myURL) or EndsText('.phps', myURL)) then
7 daniel-mar 997
    begin
27 daniel-mar 998
      WebBrowser1.LoadHTML(RunPHPScript(myURL), myUrl);
7 daniel-mar 999
      Cancel := true;
1000
    end;
1001
  end;
1002
  {$ENDREGION}
5 daniel-mar 1003
end;
2 daniel-mar 1004
 
62 daniel-mar 1005
procedure TForm1.BtnLightClick(Sender: TObject);
66 daniel-mar 1006
var
1007
  CanClose: boolean;
62 daniel-mar 1008
begin
66 daniel-mar 1009
  FormCloseQuery(Form1, CanClose);
1010
  if not CanClose then exit;
1011
 
62 daniel-mar 1012
  if IsThemeDark then
1013
  begin
1014
    BtnLight.Picture.Assign(BtnLightOn.Picture);
1015
    Theme_Light;
1016
    TFastPHPConfig.DarkTheme := false;
1017
  end
1018
  else
1019
  begin
1020
    BtnLight.Picture.Assign(BtnLightOff.Picture);
1021
    Theme_Dark;
1022
    TFastPHPConfig.DarkTheme := true;
1023
  end;
1024
end;
1025
 
57 daniel-mar 1026
procedure TForm1.BtnSpecialCharsClick(Sender: TObject);
1027
var
1028
  opts: TSynEditorOptions;
1029
begin
1030
  opts := SynEdit1.Options;
1031
  if eoShowSpecialChars in SynEdit1.Options then
1032
  begin
1033
    BtnSpecialChars.Picture.Assign(BtnSpecialCharsOff.Picture);
1034
    Exclude(opts, eoShowSpecialChars);
1035
    TFastPHPConfig.SpecialChars := false;
1036
  end
1037
  else
1038
  begin
1039
    BtnSpecialChars.Picture.Assign(BtnSpecialCharsOn.Picture);
1040
    Include(opts, eoShowSpecialChars);
1041
    TFastPHPConfig.SpecialChars := true;
1042
  end;
1043
  SynEdit1.Options := opts;
1044
end;
1045
 
83 daniel-mar 1046
procedure TForm1.FileModTimerTimer(Sender: TObject);
91 daniel-mar 1047
resourcestring
1048
  SChangeConflict = 'The file was changed in a different application BUT IT WAS ALSO MODIFIED HERE! Reload file AND LOSE CHANGES HERE?';
1049
  SChangeNotify = 'The file was changed in a different application! Reload file?';
83 daniel-mar 1050
begin
1051
  FileModTimer.Enabled := false;
1052
  if FileModLast <> FileAge(GetScrapFile) then
1053
  begin
1054
    FileModLast := FileAge(GetScrapFile);
1055
    if SynEdit1.Modified then
1056
    begin
91 daniel-mar 1057
      if MessageDlg(SChangeConflict, mtWarning, mbYesNoCancel, 0) = mrYes then
83 daniel-mar 1058
      begin
1059
        SynEdit1.Lines.LoadFromFile(GetScrapFile);
1060
      end;
1061
    end
1062
    else
1063
    begin
91 daniel-mar 1064
      if MessageDlg(SChangeNotify, mtConfirmation, mbYesNoCancel, 0) = mrYes then
83 daniel-mar 1065
      begin
1066
        SynEdit1.Lines.LoadFromFile(GetScrapFile);
1067
      end;
1068
    end;
1069
  end;
1070
  FileModTimer.Enabled := true;
1071
end;
1072
 
99 daniel-mar 1073
procedure TForm1.FontSizeTimerTimer(Sender: TObject);
1074
begin
101 daniel-mar 1075
  if Memo2.Font.Size <> (SynEdit1.Font.Size-3) then
1076
    Memo2.Font.Size := (SynEdit1.Font.Size-3);
99 daniel-mar 1077
end;
1078
 
2 daniel-mar 1079
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
1080
begin
86 daniel-mar 1081
  DragAcceptFiles(Handle, False);
49 daniel-mar 1082
  TFastPHPConfig.FontSize := SynEdit1.Font.Size;
2 daniel-mar 1083
end;
1084
 
15 daniel-mar 1085
procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
1086
var
1087
  r: integer;
91 daniel-mar 1088
resourcestring
1089
  SWantToSave = 'Do you want to save?';
15 daniel-mar 1090
begin
1091
  if SynEdit1.Modified then
1092
  begin
56 daniel-mar 1093
    if (ParamStr(1) <> '') or (FSaveAsFilename <> '') then
15 daniel-mar 1094
    begin
91 daniel-mar 1095
      r := MessageDlg(SWantToSave, mtConfirmation, mbYesNoCancel, 0);
15 daniel-mar 1096
      if r = mrCancel then
1097
      begin
1098
        CanClose := false;
1099
        Exit;
1100
      end
1101
      else if r = mrYes then
1102
      begin
47 daniel-mar 1103
        ActionSave.Execute;
15 daniel-mar 1104
        CanClose := true;
1105
      end;
1106
    end
1107
    else
1108
    begin
47 daniel-mar 1109
      ActionSave.Execute;
15 daniel-mar 1110
      CanClose := true;
1111
    end;
110 daniel-mar 1112
  end
1113
  else
1114
  begin
1115
    CanClose := true;
15 daniel-mar 1116
  end;
1117
end;
1118
 
2 daniel-mar 1119
procedure TForm1.FormCreate(Sender: TObject);
44 daniel-mar 1120
var
1121
  exeDir: string;
83 daniel-mar 1122
  sScrapFile: string;
2 daniel-mar 1123
begin
1124
  HlpPrevPageIndex := -1;
1125
  CurSearchTerm := '';
83 daniel-mar 1126
  sScrapFile := GetScrapFile;
1127
  Caption := Caption + ' - ' + sScrapFile;
91 daniel-mar 1128
  Application.Title := Format('%s - FastPHP', [ExtractFileName(sScrapFile)]); // do not translate!
24 daniel-mar 1129
  SrcRep := TSynEditFindReplace.Create(self);
13 daniel-mar 1130
  SrcRep.Editor := SynEdit1;
29 daniel-mar 1131
  SynEdit1.Gutter.Gradient := HighColorWindows;
30 daniel-mar 1132
 
1133
  Screen.Cursors[crMouseGutter] := LoadCursor(hInstance, 'MOUSEGUTTER');
1134
  SynEdit1.Gutter.Cursor := crMouseGutter;
36 daniel-mar 1135
 
44 daniel-mar 1136
  exeDir := IncludeTrailingPathDelimiter(ExtractFilePath(ParamStr(0)));
1137
  if FileExists(exeDir + 'codeexplorer.bmp') then ImageList1.LoadAndSplitImages(exeDir + 'codeexplorer.bmp');
83 daniel-mar 1138
 
1139
  FileModLast := FileAge(sScrapFile);
1140
  FileModTimer.Enabled := True;
2 daniel-mar 1141
end;
1142
 
1143
procedure TForm1.FormDestroy(Sender: TObject);
1144
begin
1145
  if Assigned(ChmIndex) then
1146
  begin
1147
    FreeAndNil(ChmIndex);
1148
  end;
13 daniel-mar 1149
  FreeAndNil(SrcRep);
27 daniel-mar 1150
 
75 daniel-mar 1151
  if hMutex <> 0 then CloseHandle(hMutex); // Note: ReleaseMutex does not work as expected!
67 daniel-mar 1152
 
27 daniel-mar 1153
  if Assigned(codeExplorer) then
1154
  begin
1155
    codeExplorer.Terminate;
1156
    codeExplorer.WaitFor;
1157
    FreeAndNil(codeExplorer);
1158
  end;
2 daniel-mar 1159
end;
1160
 
1161
procedure TForm1.FormShow(Sender: TObject);
1162
var
1163
  ScrapFile: string;
49 daniel-mar 1164
  tmpFontSize: integer;
57 daniel-mar 1165
  opts: TSynEditorOptions;
91 daniel-mar 1166
resourcestring
1167
  SFileAlreadyOpen = 'File "%s" is alrady open!';
2 daniel-mar 1168
begin
71 daniel-mar 1169
  if FormShowRanOnce then exit; // If the theme is changed from normal to dark, OnShow will be called another time
1170
  FormShowRanOnce := true;
1171
 
104 daniel-mar 1172
  DoubleBuffered := true;
1173
 
2 daniel-mar 1174
  ScrapFile := GetScrapFile;
1175
  if ScrapFile = '' then
1176
  begin
10 daniel-mar 1177
    Application.Terminate; // Close;
2 daniel-mar 1178
    exit;
1179
  end;
57 daniel-mar 1180
 
1181
  opts := SynEdit1.Options;
1182
  if TFastPHPConfig.SpecialChars then
1183
  begin
1184
    BtnSpecialChars.Picture.Assign(BtnSpecialCharsOn.Picture);
1185
    Include(opts, eoShowSpecialChars);
1186
  end
1187
  else
1188
  begin
1189
    BtnSpecialChars.Picture.Assign(BtnSpecialCharsOff.Picture);
1190
    Exclude(opts, eoShowSpecialChars);
1191
  end;
1192
  SynEdit1.Options := opts;
1193
 
104 daniel-mar 1194
  PageControl1.ActivePage := PlaintextTabSheet;
1195
 
1196
  PageControl2.ActivePage := CodeTabsheet;
1197
  HelpTabsheet.TabVisible := false;
1198
 
1199
  tmpFontSize := TFastPHPConfig.FontSize;
1200
  if tmpFontSize <> -1 then SynEdit1.Font.Size := tmpFontSize;
1201
 
15 daniel-mar 1202
  if FileExists(ScrapFile) then
67 daniel-mar 1203
  begin
71 daniel-mar 1204
    if hMutex = 0 then
67 daniel-mar 1205
    begin
104 daniel-mar 1206
      hMutex := CreateMutex(nil, True, PChar('FastPHP'+md5(UpperCase(ScrapFile)))); // do not translate
69 daniel-mar 1207
      if GetLastError = ERROR_ALREADY_EXISTS then
1208
      begin
1209
        // TODO: It would be great if the window of that FastPHP instance would switched to foreground
91 daniel-mar 1210
        ShowMessageFmt(SFileAlreadyOpen, [ScrapFile]);
104 daniel-mar 1211
        Application.Terminate; // Close;
1212
        exit;
69 daniel-mar 1213
      end;
1214
 
1215
      SynEdit1.Lines.LoadFromFile(ScrapFile);
67 daniel-mar 1216
    end;
1217
  end
15 daniel-mar 1218
  else
1219
    SynEdit1.Lines.Clear;
2 daniel-mar 1220
 
105 daniel-mar 1221
  SynEdit1.Modified := false; // is required for Delphi 11
1222
 
113 daniel-mar 1223
  SynEdit1.ClearUndo; // Avoid that Ctrl+Z deletes a freshly opened file
1224
 
5 daniel-mar 1225
  SynEdit1.SetFocus;
27 daniel-mar 1226
 
1227
  StartCodeExplorer;
62 daniel-mar 1228
 
86 daniel-mar 1229
  DragAcceptFiles(Handle, True);
1230
 
62 daniel-mar 1231
  StartupTimer.Enabled := true;
2 daniel-mar 1232
end;
1233
 
114 daniel-mar 1234
procedure TForm1.ChooseanotherCHMhelpfile1Click(Sender: TObject);
1235
begin
1236
  Help(true);
1237
end;
1238
 
1239
procedure TForm1.ChooseanotherPHPinterpreter1Click(Sender: TObject);
1240
begin
1241
  GetPHPExe(true);
1242
end;
1243
 
96 daniel-mar 1244
function TForm1.ContainsUnicodeCharsInAnsiFile: boolean;
1245
var
1246
  lines: TStringList;
1247
  ms: TMemoryStream;
1248
begin
1249
  if SynEdit1.Lines.Encoding = TEncoding.UTF8 then
1250
  begin
1251
    result := false;
1252
    exit;
1253
  end;
1254
 
1255
  lines := TStringList.Create;
1256
  ms := TMemoryStream.Create;
1257
  try
1258
    SynEdit1.Lines.SaveToStream(ms);
1259
    ms.Position := 0;
1260
    lines.LoadFromStream(ms);
1261
    result := Trim(lines.Text) <> Trim(synedit1.Lines.Text);
1262
  finally
1263
    FreeAndNil(ms);
1264
    FreeAndNil(lines);
1265
  end;
1266
end;
1267
 
76 daniel-mar 1268
procedure TForm1.SaveToFile(filename: string);
1269
var
79 daniel-mar 1270
  ss: TStringStream;
78 daniel-mar 1271
  ms: TMemoryStream;
1272
  fs: TFileStream;
79 daniel-mar 1273
  eolStyle: string;
1274
  str: string;
96 daniel-mar 1275
  UpgradeEncodingToUnicode: boolean;
1276
  DoReloadEncoding: boolean;
1277
resourcestring
1278
  LNG_UpgradeToUtf8 = 'Unicode characters have been inserted to this ANSI encoded file. Convert file to UTF-8? (If you choose "No", the Unicode characters will be replaced with "?")';
76 daniel-mar 1279
begin
83 daniel-mar 1280
  FileModTimer.Enabled := false;
1281
 
96 daniel-mar 1282
  if ContainsUnicodeCharsInAnsiFile then
1283
  begin
1284
    DoReloadEncoding := true;
1285
    case MessageBox(Handle, PChar(LNG_UpgradeToUtf8), PChar(Caption), MB_YESNOCANCEL) of
1286
      ID_YES:
1287
      begin
1288
        UpgradeEncodingToUnicode := true;
1289
      end;
1290
      ID_NO:
1291
      begin
1292
        UpgradeEncodingToUnicode := false;
1293
      end;
1294
      ID_CANCEL:
1295
      begin
1296
        Abort;
1297
      end;
1298
    end;
1299
  end
1300
  else
1301
  begin
1302
    DoReloadEncoding := false;
1303
    UpgradeEncodingToUnicode := false;
1304
  end;
1305
 
78 daniel-mar 1306
  ms := TMemoryStream.Create;
79 daniel-mar 1307
  ss := TStringStream.Create('');
78 daniel-mar 1308
  fs := TFileStream.Create(filename, fmCreate);
76 daniel-mar 1309
  try
79 daniel-mar 1310
    // Save everything in a memory stream and then to a string
1311
    // in comparison to "str := SynEdit1.Lines.Text;",
1312
    // This approach should preserve LF / CRLF line endings
96 daniel-mar 1313
    if UpgradeEncodingToUnicode then
1314
      SynEdit1.Lines.SaveToStream(ms, TEncoding.UTF8) // upgrade an ANSI file to UTF-8 (e.g. if you include Japanese characters)
1315
    else
1316
      SynEdit1.Lines.SaveToStream(ms);
1317
    if DoReloadEncoding then
1318
    begin
1319
      ms.Position := 0;
1320
      SynEdit1.Lines.LoadFromStream(ms);
1321
    end;
79 daniel-mar 1322
    ms.Position := 0;
1323
    ss.CopyFrom(ms, ms.Size);
1324
    ss.Position := 0;
1325
    str := ss.ReadString(ss.Size);
1326
    ss.Size := 0; // clear string-stream, because we need it later again
77 daniel-mar 1327
 
79 daniel-mar 1328
    // Detect current line-endings
1329
    if Copy(str, 1, 2) = '#!' then
77 daniel-mar 1330
    begin
79 daniel-mar 1331
      // Shebang. Use ONLY Linux LF
1332
      str := StringReplace(str, #13#10, #10, [rfReplaceAll]);
1333
      eolStyle := #10 // Linux LF
1334
    end
1335
    else
1336
    begin
1337
      if Pos(#13#10, str) > 0 then
1338
        eolStyle := #13#10 // Windows CRLF
1339
      else if Pos(#10, str) > 0 then
1340
        eolStyle := #10 // Linux LF
78 daniel-mar 1341
      else
1342
      begin
79 daniel-mar 1343
        if DefaultTextLineBreakStyle = tlbsLF then
1344
          eolStyle := #10 // Linux LF
1345
        else if DefaultTextLineBreakStyle = tlbsCRLF then
1346
          eolStyle := #13#10 // Windows CRLF
1347
        //else if DefaultTextLineBreakStyle = tlbsCR then
1348
        //  eolStyle := #13 // Old Mac CR
1349
        else
1350
          eolStyle := #13#10; // (Should not happen)
78 daniel-mar 1351
      end;
77 daniel-mar 1352
    end;
1353
 
79 daniel-mar 1354
    // Unitfy line-endings
97 daniel-mar 1355
    str := StringReplace(str, #13, '', [rfReplaceAll]);
79 daniel-mar 1356
    str := StringReplace(str, #10, eolStyle, [rfReplaceAll]);
1357
 
1358
    // Replace all trailing linebreaks by a single line break
1359
    // Note: Removing all line breaks is not good, since Linux's "nano" will
1360
    //       re-add a linebreak at the end of the file
1361
    str := TrimRight(str) + eolStyle;
1362
 
78 daniel-mar 1363
    // Old versions of Delphi/SynEdit write an UTF-8 BOM, which makes problems
1364
    // e.g. with AJAX handlers (because AJAX reponses must not have a BOM).
1365
    // So we try to avoid that.
1366
    // Note that the output is still UTF-8 encoded if the input file was UTF-8 encoded
79 daniel-mar 1367
    if Copy(str,1,3) = #$EF#$BB#$BF then Delete(str, 1, 3);
77 daniel-mar 1368
 
78 daniel-mar 1369
    // Now save to the file
79 daniel-mar 1370
    ss.WriteString(str);
1371
    ss.Position := 0;
1372
    fs.CopyFrom(ss, ss.Size-ss.Position);
76 daniel-mar 1373
  finally
78 daniel-mar 1374
    FreeAndNil(ms);
79 daniel-mar 1375
    FreeAndNil(ss);
78 daniel-mar 1376
    FreeAndNil(fs);
76 daniel-mar 1377
  end;
83 daniel-mar 1378
 
1379
  FileModLast := FileAge(GetScrapFile);
1380
  FileModTimer.Enabled := True;
76 daniel-mar 1381
end;
1382
 
27 daniel-mar 1383
procedure TForm1.StartCodeExplorer;
1384
begin
1385
  codeExplorer := TRunCodeExplorer.Create(true);
1386
  codeExplorer.InputRequestCallback := InputRequestCallback;
1387
  codeExplorer.OutputNotifyCallback := OutputNotifyCallback;
1388
  codeExplorer.PhpExe := GetPHPExe;
1389
  codeExplorer.PhpFile := IncludeTrailingPathDelimiter(ExtractFileDir(Application.ExeName)) + 'codeexplorer.php'; // GetScrapFile;
1390
  codeExplorer.WorkDir := ExtractFileDir(Application.ExeName);
44 daniel-mar 1391
  codeExplorer.Resume;
27 daniel-mar 1392
end;
1393
 
62 daniel-mar 1394
procedure TForm1.StartUpTimerTimer(Sender: TObject);
1395
begin
1396
  StartupTimer.Enabled := false;
1397
 
1398
  // We need this timer because we cannot change the Theme during OnShow,
1399
  // because the Delphi VCL Theme is buggy!
1400
 
1401
  if TFastPHPConfig.DarkTheme then
1402
  begin
1403
    BtnLight.Picture.Assign(BtnLightOff.Picture);
1404
    Theme_Dark;
1405
  end
1406
  else
1407
  begin
1408
    BtnLight.Picture.Assign(BtnLightOn.Picture);
1409
    Theme_Light;
1410
  end;
1411
end;
1412
 
2 daniel-mar 1413
function TForm1.GetScrapFile: string;
49 daniel-mar 1414
var
1415
  tmpPath: string;
91 daniel-mar 1416
resourcestring
1417
  SFileDoesNotExistsCreate = 'File %s does not exist. Create it?';
1418
  SPathDoesNotExistTryAgain = 'Path does not exist! Please try again.';
2 daniel-mar 1419
begin
56 daniel-mar 1420
  if FSaveAsFilename <> '' then
1421
  begin
1422
    result := FSaveAsFilename;
1423
    exit;
1424
  end;
1425
 
44 daniel-mar 1426
  if FScrapFile <> '' then
1427
  begin
1428
    result := FScrapFile;
1429
    exit;
1430
  end;
19 daniel-mar 1431
 
15 daniel-mar 1432
  if ParamStr(1) <> '' then
49 daniel-mar 1433
  begin
1434
    // Program was started with a filename
1435
 
1436
    result := ParamStr(1);
1437
 
1438
    if not FileExists(result) then
1439
    begin
91 daniel-mar 1440
      case MessageDlg(Format(SFileDoesNotExistsCreate, [result]), mtConfirmation, mbYesNoCancel, 0) of
49 daniel-mar 1441
        mrYes:
1442
          try
76 daniel-mar 1443
            SaveToFile(result);
49 daniel-mar 1444
          except
1445
            on E: Exception do
1446
            begin
1447
              MessageDlg(E.Message, mtError, [mbOk], 0);
1448
              Application.Terminate;
1449
              result := '';
1450
              exit;
1451
            end;
1452
          end;
1453
        mrNo:
1454
          begin
1455
            Application.Terminate;
1456
            result := '';
1457
            exit;
1458
          end;
1459
        mrCancel:
1460
          begin
1461
            Application.Terminate;
1462
            result := '';
1463
            exit;
1464
          end;
1465
      end;
1466
    end;
1467
  end
13 daniel-mar 1468
  else
2 daniel-mar 1469
  begin
49 daniel-mar 1470
    // Program is started without filename -> use scrap file
2 daniel-mar 1471
 
49 daniel-mar 1472
    result := TFastPHPConfig.ScrapFile;
1473
 
1474
    if not FileExists(result) then
1475
    begin
1476
      repeat
1477
        {$REGION 'Determinate opendialog initial directory'}
1478
        if result <> '' then
1479
        begin
1480
          tmpPath := ExtractFilePath(result);
1481
          if DirectoryExists(tmpPath) then
1482
          begin
1483
            OpenDialog3.InitialDir := tmpPath;
1484
            OpenDialog3.FileName := Result;
1485
          end
1486
          else
1487
          begin
1488
            OpenDialog3.InitialDir := GetMyDocumentsFolder;
1489
          end;
1490
        end
1491
        else
1492
        begin
1493
          OpenDialog3.InitialDir := GetMyDocumentsFolder;
1494
        end;
1495
        {$ENDREGION}
1496
 
1497
        if not OpenDialog3.Execute then
1498
        begin
1499
          Application.Terminate;
1500
          result := '';
1501
          exit;
1502
        end;
1503
 
1504
        if not DirectoryExists(ExtractFilePath(OpenDialog3.FileName)) then
1505
        begin
91 daniel-mar 1506
          MessageDlg(SPathDoesNotExistTryAgain, mtWarning, [mbOk], 0);
49 daniel-mar 1507
        end
1508
        else
1509
        begin
1510
          result := OpenDialog3.FileName;
1511
        end;
1512
      until result <> '';
1513
 
1514
      if not FileExists(result) then
19 daniel-mar 1515
      begin
49 daniel-mar 1516
        try
1517
          // Try saving the file; check if we have permissions
1518
          //SynEdit1.Lines.Clear;
76 daniel-mar 1519
          SaveToFile(result);
49 daniel-mar 1520
        except
1521
          on E: Exception do
1522
          begin
1523
            MessageDlg(E.Message, mtError, [mbOk], 0);
1524
            Application.Terminate;
1525
            result := '';
1526
            exit;
1527
          end;
1528
        end;
19 daniel-mar 1529
      end;
2 daniel-mar 1530
 
49 daniel-mar 1531
      TFastPHPConfig.ScrapFile := result;
1532
      FScrapFile := result;
1533
    end;
2 daniel-mar 1534
  end;
1535
end;
1536
 
114 daniel-mar 1537
procedure TForm1.Help(AForceSelectNewHelpFile: boolean=false);
2 daniel-mar 1538
var
19 daniel-mar 1539
  IndexFile, chmFile, w, OriginalWord, url: string;
2 daniel-mar 1540
  internalHtmlFile: string;
91 daniel-mar 1541
resourcestring
1542
  SChmFileNotAValidPHPDocumentation = 'The CHM file is not a valid PHP documentation. Cannot use help.';
1543
  SUnknownErrorCannotUseHelp = 'Unknown error. Cannot use help.';
1544
  SNoHelpAvailable = 'No help for "%s" available';
2 daniel-mar 1545
begin
1546
  if not Assigned(ChmIndex) then
1547
  begin
49 daniel-mar 1548
    IndexFile := TFastPHPConfig.HelpIndex;
2 daniel-mar 1549
    IndexFile := ChangeFileExt(IndexFile, '.ini'); // Just to be sure. Maybe someone wrote manually the ".chm" file in there
1550
    if FileExists(IndexFile) then
1551
    begin
1552
      ChmIndex := TMemIniFile.Create(IndexFile);
1553
    end;
1554
  end;
1555
 
1556
  if Assigned(ChmIndex) then
1557
  begin
49 daniel-mar 1558
    IndexFile := TFastPHPConfig.HelpIndex;
2 daniel-mar 1559
    // We don't check if IndexFile still exists. It is not important since we have ChmIndex pre-loaded in memory
1560
 
1561
    chmFile := ChangeFileExt(IndexFile, '.chm');
1562
    if not FileExists(chmFile) then
1563
    begin
1564
      FreeAndNil(ChmIndex);
1565
    end;
1566
  end;
1567
 
1568
  if not Assigned(ChmIndex) then
1569
  begin
1570
    if not OpenDialog1.Execute then exit;
1571
 
1572
    chmFile := OpenDialog1.FileName;
1573
    if not FileExists(chmFile) then exit;
1574
 
1575
    IndexFile := ChangeFileExt(chmFile, '.ini');
1576
 
1577
    if not FileExists(IndexFile) then
1578
    begin
1579
      Panel1.Align := alClient;
1580
      Panel1.Visible := true;
1581
      Panel1.BringToFront;
1582
      Screen.Cursor := crHourGlass;
1583
      Application.ProcessMessages;
1584
      try
1585
        if not ParseCHM(chmFile) then
1586
        begin
91 daniel-mar 1587
          MessageDlg(SChmFileNotAValidPHPDocumentation, mtError, [mbOk], 0);
2 daniel-mar 1588
          exit;
1589
        end;
1590
      finally
1591
        Screen.Cursor := crDefault;
1592
        Panel1.Visible := false;
1593
      end;
1594
 
1595
      if not FileExists(IndexFile) then
1596
      begin
91 daniel-mar 1597
        MessageDlg(SUnknownErrorCannotUseHelp, mtError, [mbOk], 0);
2 daniel-mar 1598
        exit;
1599
      end;
1600
    end;
1601
 
49 daniel-mar 1602
    TFastPHPConfig.HelpIndex := IndexFile;
2 daniel-mar 1603
 
1604
    ChmIndex := TMemIniFile.Create(IndexFile);
1605
  end;
1606
 
4 daniel-mar 1607
  w := GetWordUnderCaret(SynEdit1);
2 daniel-mar 1608
  if w = '' then exit;
44 daniel-mar 1609
  {$IFDEF UNICODE}
8 daniel-mar 1610
  if CharInSet(w[1], ['0'..'9']) then exit;
44 daniel-mar 1611
  {$ELSE}
1612
  if w[1] in ['0'..'9'] then exit;
1613
  {$ENDIF}
19 daniel-mar 1614
 
1615
  Originalword := w;
1616
//  w := StringReplace(w, '_', '-', [rfReplaceAll]);
2 daniel-mar 1617
  w := LowerCase(w);
1618
  CurSearchTerm := w;
1619
 
91 daniel-mar 1620
  internalHtmlFile := ChmIndex.ReadString('function', CurSearchTerm, ''); // do not translate
2 daniel-mar 1621
  if internalHtmlFile = '' then
91 daniel-mar 1622
    internalHtmlFile := ChmIndex.ReadString('_HelpWords_', CurSearchTerm, ''); // do not translate
72 daniel-mar 1623
  if internalHtmlFile = '' then
2 daniel-mar 1624
  begin
1625
    HelpTabsheet.TabVisible := false;
1626
    HlpPrevPageIndex := -1;
91 daniel-mar 1627
    ShowMessageFmt(SNoHelpAvailable, [Originalword]);
2 daniel-mar 1628
    Exit;
1629
  end;
1630
 
1631
  url := 'mk:@MSITStore:'+ChmFile+'::'+internalHtmlFile;
1632
 
1633
  HlpPrevPageIndex := PageControl2.ActivePageIndex; // Return by pressing ESC
1634
  HelpTabsheet.TabVisible := true;
1635
  PageControl2.ActivePage := HelpTabsheet;
8 daniel-mar 1636
  WebBrowser2.Navigate(url);
1637
  WebBrowser2.Wait;
2 daniel-mar 1638
end;
1639
 
5 daniel-mar 1640
procedure TForm1.GotoLineNo(LineNo:integer);
1641
var
1642
  line: string;
1643
  i: integer;
2 daniel-mar 1644
begin
5 daniel-mar 1645
  SynEdit1.GotoLineAndCenter(LineNo);
1646
 
1647
  // Skip indent
1648
  line := SynEdit1.Lines[SynEdit1.CaretY];
1649
  for i := 1 to Length(line) do
1650
  begin
44 daniel-mar 1651
    {$IFDEF UNICODE}
8 daniel-mar 1652
    if not CharInSet(line[i], [' ', #9]) then
44 daniel-mar 1653
    {$ELSE}
1654
    if not (line[i] in [' ', #9]) then
1655
    {$ENDIF}
5 daniel-mar 1656
    begin
1657
      SynEdit1.CaretX := i-1;
1658
      break;
1659
    end;
1660
  end;
1661
 
20 daniel-mar 1662
  PageControl2.ActivePage := CodeTabsheet;
5 daniel-mar 1663
  if SynEdit1.CanFocus then SynEdit1.SetFocus;
2 daniel-mar 1664
end;
1665
 
8 daniel-mar 1666
procedure TForm1.PageControl2Changing(Sender: TObject;
1667
  var AllowChange: Boolean);
1668
begin
1669
  if PageControl2.ActivePage = HelpTabsheet then
1670
    HlpPrevPageIndex := -1
1671
  else
1672
    HlpPrevPageIndex := PageControl2.ActivePageIndex;
1673
 
1674
  AllowChange := true;
1675
end;
1676
 
5 daniel-mar 1677
procedure TForm1.Memo2DblClick(Sender: TObject);
1678
var
22 daniel-mar 1679
  line: string;
1680
 
1681
  procedure _process(toFind: string);
1682
  var
1683
    p, lineno: integer;
1684
  begin
1685
    if FileSystemCaseSensitive then
1686
      p := Pos(toFind, line)
1687
    else
44 daniel-mar 1688
      p := Pos(LowerCase(toFind), LowerCase(line));
22 daniel-mar 1689
    if p <> 0 then
1690
    begin
101 daniel-mar 1691
      line := copy(line, p+length(toFind), 99); // TODO: "(test.php:123)" does not work, because "123)" will be tried to convert to Integer
22 daniel-mar 1692
      if not TryStrToInt(line, lineno) then exit;
1693
      GotoLineNo(lineno);
1694
    end;
1695
  end;
1696
 
5 daniel-mar 1697
begin
1698
  line := memo2.Lines.Strings[Memo2.CaretPos.Y];
16 daniel-mar 1699
 
18 daniel-mar 1700
  {$REGION 'Possibility 1: filename.php:lineno'}
22 daniel-mar 1701
  _process(ExtractFileName(GetScrapFile) + ':');
18 daniel-mar 1702
  {$ENDREGION}
16 daniel-mar 1703
 
18 daniel-mar 1704
  {$REGION 'Possibility 2: on line xx'}
91 daniel-mar 1705
  _process(ExtractFileName(GetScrapFile) + ' on line '); // do not translate!
18 daniel-mar 1706
  {$ENDREGION}
5 daniel-mar 1707
end;
1708
 
17 daniel-mar 1709
procedure TForm1.Memo2KeyDown(Sender: TObject; var Key: Word;
1710
  Shift: TShiftState);
1711
begin
1712
  if ((ssCtrl in Shift) and (Key = 65)) then TMemo(Sender).SelectAll;
1713
end;
1714
 
5 daniel-mar 1715
function TForm1.MarkUpLineReference(cont: string): string;
18 daniel-mar 1716
 
1717
  procedure _process(toFind: string);
22 daniel-mar 1718
  var
1719
    p, a, b: integer;
1720
    num: integer;
1721
    insert_a, insert_b: string;
5 daniel-mar 1722
  begin
22 daniel-mar 1723
    if FileSystemCaseSensitive then
1724
      p := Pos(toFind, cont)
1725
    else
44 daniel-mar 1726
      p := Pos(LowerCase(toFind), LowerCase(cont));
18 daniel-mar 1727
    while p >= 1 do
5 daniel-mar 1728
    begin
22 daniel-mar 1729
      a := p;
1730
      b := p + length(toFind);
18 daniel-mar 1731
      num := 0;
44 daniel-mar 1732
      {$IFDEF UNICODE}
18 daniel-mar 1733
      while CharInSet(cont[b], ['0'..'9']) do
44 daniel-mar 1734
      {$ELSE}
1735
      while cont[b] in ['0'..'9'] do
1736
      {$ENDIF}
18 daniel-mar 1737
      begin
1738
        num := num*10 + StrToInt(cont[b]);
1739
        inc(b);
1740
      end;
5 daniel-mar 1741
 
18 daniel-mar 1742
      insert_b := '</a>';
22 daniel-mar 1743
      insert_a := '<a href="' + FASTPHP_GOTO_URI_PREFIX + IntToStr(num) + '">';
5 daniel-mar 1744
 
18 daniel-mar 1745
      insert(insert_b, cont, b);
1746
      insert(insert_a, cont, a);
5 daniel-mar 1747
 
18 daniel-mar 1748
      p := b + Length(insert_a) + Length(insert_b);
5 daniel-mar 1749
 
18 daniel-mar 1750
      p := PosEx(toFind, cont, p+1);
1751
    end;
5 daniel-mar 1752
  end;
22 daniel-mar 1753
 
18 daniel-mar 1754
begin
1755
  {$REGION 'Possibility 1: filename.php:lineno'}
22 daniel-mar 1756
  _process(ExtractFileName(GetScrapFile) + ':');
18 daniel-mar 1757
  {$ENDREGION}
5 daniel-mar 1758
 
18 daniel-mar 1759
  {$REGION 'Possibility 2: on line xx'}
91 daniel-mar 1760
  _process(ExtractFileName(GetScrapFile) + ' on line '); // do not translate!
18 daniel-mar 1761
  {$ENDREGION}
1762
 
5 daniel-mar 1763
  result := cont;
1764
end;
1765
 
40 daniel-mar 1766
function TForm1.InputRequestCallback(var data: AnsiString): boolean;
27 daniel-mar 1767
begin
40 daniel-mar 1768
  data := UTF8Encode(SynEdit1.Text);
1769
  result := true;
27 daniel-mar 1770
end;
1771
 
62 daniel-mar 1772
function TForm1.IsThemeDark: boolean;
1773
begin
1774
  result := Assigned(TStyleManager.ActiveStyle) and (TStyleManager.ActiveStyle.Name<>'Windows');
1775
end;
1776
 
40 daniel-mar 1777
function TForm1.OutputNotifyCallback(const data: AnsiString): boolean;
27 daniel-mar 1778
begin
91 daniel-mar 1779
  result := TreeView1.FillWithFastPHPData(string(data));
27 daniel-mar 1780
end;
1781
 
2 daniel-mar 1782
end.