Subversion Repositories fastphp

Rev

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