Subversion Repositories fastphp

Rev

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