Subversion Repositories fastphp

Rev

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