Subversion Repositories fastphp

Rev

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