Subversion Repositories fastphp

Rev

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

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