Subversion Repositories fastphp

Rev

Rev 101 | Rev 103 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

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