Subversion Repositories fastphp

Rev

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