Subversion Repositories fastphp

Rev

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