Subversion Repositories fastphp

Rev

Rev 69 | Rev 72 | 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
68 daniel-mar 295
  for i := 0 to SynEdit1.Lines.Count-1 do
67 daniel-mar 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;
68 daniel-mar 307
  if SynEdit1.CanFocus then SynEdit1.SetFocus;
13 daniel-mar 308
end;
309
 
23 daniel-mar 310
procedure TForm1.ActionSpaceToTabExecute(Sender: TObject);
311
 
312
    function SpacesAtBeginning(line: string): integer;
313
    begin
314
      result := 0;
44 daniel-mar 315
      if Trim(line) = '' then exit;
23 daniel-mar 316
      while line[result+1] = ' ' do
317
      begin
318
        inc(result);
319
      end;
320
    end;
321
 
44 daniel-mar 322
    function GuessIndent(lines: {$IFDEF UNICODE}TStrings{$ELSE}TUnicodeStrings{$ENDIF}): integer;
23 daniel-mar 323
      function _Check(indent: integer): boolean;
324
      var
325
        i: integer;
326
      begin
327
        result := true;
328
        for i := 0 to lines.Count-1 do
329
          if SpacesAtBeginning(lines.Strings[i]) mod indent <> 0 then
330
          begin
331
            // ShowMessageFmt('Zeile "%s" nicht durch %d teilbar!', [lines.strings[i], indent]);
44 daniel-mar 332
            result := false;
333
            exit;
23 daniel-mar 334
          end;
335
      end;
336
    var
337
      i: integer;
338
    begin
339
      for i := 8 downto 2 do
340
      begin
44 daniel-mar 341
        if _Check(i) then
342
        begin
343
          result := i;
344
          exit;
345
        end;
23 daniel-mar 346
      end;
347
      result := -1;
348
    end;
349
 
44 daniel-mar 350
    procedure SpaceToTab(lines: {$IFDEF UNICODE}TStrings{$ELSE}TUnicodeStrings{$ENDIF}; indent: integer);
23 daniel-mar 351
    var
352
      i, spaces: integer;
353
    begin
354
      for i := 0 to lines.Count-1 do
355
      begin
356
        spaces := SpacesAtBeginning(lines.Strings[i]);
357
        lines.Strings[i] := StringOfChar(#9, spaces div indent) + StringOfChar(' ', spaces mod indent) + Copy(lines.Strings[i], spaces+1, Length(lines.Strings[i])-spaces);
358
      end;
359
    end;
360
 
44 daniel-mar 361
    function SpacesAvailable(lines: {$IFDEF UNICODE}TStrings{$ELSE}TUnicodeStrings{$ENDIF}): boolean;
23 daniel-mar 362
    var
363
      i, spaces: integer;
364
    begin
365
      for i := 0 to lines.Count-1 do
366
      begin
367
        spaces := SpacesAtBeginning(lines.Strings[i]);
44 daniel-mar 368
        if spaces > 0 then
369
        begin
370
          result := true;
371
          exit;
372
        end;
23 daniel-mar 373
      end;
44 daniel-mar 374
      result := false;
375
      exit;
23 daniel-mar 376
    end;
377
 
378
var
379
  val: string;
380
  ind: integer;
381
resourcestring
382
  SNoLinesAvailable = 'No lines with spaces at the beginning available';
383
begin
384
  // TODO: if something is selected, only process the selected part
385
 
386
  if not SpacesAvailable(SynEdit1.Lines) then
387
  begin
49 daniel-mar 388
    MessageDlg(SNoLinesAvailable, mtInformation, [mbOk], 0);
23 daniel-mar 389
    exit;
390
  end;
391
 
392
  ind := GuessIndent(SynEdit1.Lines);
393
  if ind <> -1 then val := IntToStr(ind);
394
 
395
  InputQuery('Spaces to tabs', 'Indent:', val); // TODO: handle CANCEL correctly...
44 daniel-mar 396
  if TryStrToInt(Trim(val), ind) then
23 daniel-mar 397
  begin
398
    if ind = 0 then exit;
399
    SpaceToTab(SynEdit1.Lines, ind);
400
  end;
401
 
402
  if SynEdit1.CanFocus then SynEdit1.SetFocus;
403
end;
404
 
13 daniel-mar 405
procedure TForm1.ActionESCExecute(Sender: TObject);
406
begin
407
  if (HlpPrevPageIndex <> -1) and (PageControl2.ActivePage = HelpTabSheet) and
408
     (HelpTabsheet.TabVisible) then
409
  begin
410
    PageControl2.ActivePageIndex := HlpPrevPageIndex;
411
    HelpTabsheet.TabVisible := false;
2 daniel-mar 412
  end;
13 daniel-mar 413
 
414
  // Dirty hack...
22 daniel-mar 415
  SrcRep.CloseDialogs;
2 daniel-mar 416
end;
417
 
13 daniel-mar 418
procedure TForm1.ActionFindExecute(Sender: TObject);
419
begin
420
  SrcRep.FindExecute;
421
end;
422
 
16 daniel-mar 423
var
424
  firstTimeBrowserLoad: boolean = true;
2 daniel-mar 425
procedure TForm1.Run(Sender: TObject);
16 daniel-mar 426
var
427
  bakTS: TTabSheet;
67 daniel-mar 428
  //ss: TStringStream;
429
  //bakPos: Int64;
2 daniel-mar 430
begin
5 daniel-mar 431
  memo2.Lines.Text := '';
16 daniel-mar 432
 
433
  if firstTimeBrowserLoad then
434
  begin
435
    bakTS := PageControl1.ActivePage;
436
    try
437
      PageControl1.ActivePage := HtmlTabSheet; // Required for the first time, otherwise, WebBrowser1.Clear will hang
438
      Webbrowser1.Clear;
439
    finally
440
      PageControl1.ActivePage := bakTS;
441
    end;
442
    firstTimeBrowserLoad := false;
443
  end
444
  else
445
    Webbrowser1.Clear;
446
 
5 daniel-mar 447
  Screen.Cursor := crHourGlass;
448
  Application.ProcessMessages;
449
 
450
  try
47 daniel-mar 451
    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 452
 
62 daniel-mar 453
    // 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 !!!!!!!!
454
    // TODO 70422 * <fastphp> wenn ein script hängt, soll man es abwürgen dürfen!!!!!!
45 daniel-mar 455
    memo2.Lines.Text := RunPHPScript(GetScrapFile, Sender=ActionLint, False);
5 daniel-mar 456
 
457
 
62 daniel-mar 458
    // Webbrowser1.LoadHTML(MarkUpLineReference(memo2.Lines.Text), GetScrapFile);
459
 
460
    // Alternatively:
461
    (*
462
    ss := TstringStream.Create;
463
    ss.WriteString(MarkUpLineReference(memo2.Lines.Text));
464
    ss.Position := 0;
465
    Webbrowser1.LoadStream(ss, GetScrapFile);
466
    Webbrowser1.Wait;
467
    ss.Free;
468
    *)
469
 
5 daniel-mar 470
    if IsTextHTML(memo2.lines.text) then
471
      PageControl1.ActivePage := HtmlTabSheet
472
    else
473
      PageControl1.ActivePage := PlaintextTabSheet;
474
  finally
475
    Screen.Cursor := crDefault;
2 daniel-mar 476
  end;
5 daniel-mar 477
end;
2 daniel-mar 478
 
45 daniel-mar 479
procedure TForm1.RunConsole(Sender: TObject);
480
begin
47 daniel-mar 481
  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 482
  RunPHPScript(GetScrapFile, Sender=ActionLint, True);
483
end;
484
 
47 daniel-mar 485
procedure TForm1.SynEdit1Change(Sender: TObject);
486
begin
487
  RefreshModifySign;
488
end;
489
 
30 daniel-mar 490
procedure TForm1.SynEdit1GutterClick(Sender: TObject; Button: TMouseButton; X,
491
  Y, Line: Integer; Mark: TSynEditMark);
492
begin
493
  (*
494
  TSynEdit(Sender).CaretX := 1;
495
  TSynEdit(Sender).CaretY := Line;
496
  TSynEdit(Sender).SelLength := Length(TSynEdit(Sender).LineText);
497
  *)
498
end;
499
 
23 daniel-mar 500
procedure TForm1.SynEdit1MouseCursor(Sender: TObject; const aLineCharPos: TBufferCoord; var aCursor: TCursor);
501
{$IFDEF OnlineHelp}
502
var
503
  Line: Integer;
504
  Column: Integer;
505
  word: string;
506
begin
507
  Line  := aLineCharPos.Line-1;
508
  Column := aLineCharPos.Char-1;
509
  word := GetWordUnderPos(TSynEdit(Sender), Line, Column);
510
  if word <> gOnlineHelpWord then
511
  begin
512
    gOnlineHelpWord := word;
513
    Timer1.Enabled := false;
514
    Timer1.Enabled := true;
515
  end;
516
{$ELSE}
517
begin
518
{$ENDIF}
519
end;
520
 
13 daniel-mar 521
procedure TForm1.SynEdit1MouseWheelDown(Sender: TObject; Shift: TShiftState;
522
  MousePos: TPoint; var Handled: Boolean);
523
begin
524
  if ssCtrl in Shift then
525
  begin
526
    SynEdit1.Font.Size := Max(SynEdit1.Font.Size - 1, 5);
23 daniel-mar 527
    Handled := true;
528
  end
529
  else Handled := false;
13 daniel-mar 530
end;
531
 
532
procedure TForm1.SynEdit1MouseWheelUp(Sender: TObject; Shift: TShiftState;
533
  MousePos: TPoint; var Handled: Boolean);
534
begin
535
  if ssCtrl in Shift then
536
  begin
537
    SynEdit1.Font.Size := SynEdit1.Font.Size + 1;
23 daniel-mar 538
    Handled := true;
539
  end
540
  else Handled := false;
13 daniel-mar 541
end;
542
 
31 daniel-mar 543
procedure TForm1.SynEdit1PaintTransient(Sender: TObject; Canvas: TCanvas; TransientType: TTransientType);
544
var
545
  Editor: TSynEdit;
546
  OpenChars: array of WideChar;//[0..2] of WideChar=();
547
  CloseChars: array of WideChar;//[0..2] of WideChar=();
548
 
549
  function IsCharBracket(AChar: WideChar): Boolean;
550
  begin
551
    case AChar of
552
      '{','[','(','<','}',']',')','>':
553
        Result := True;
554
      else
555
        Result := False;
556
    end;
557
  end;
558
 
559
  function CharToPixels(P: TBufferCoord): TPoint;
560
  begin
561
    Result := Editor.RowColumnToPixels(Editor.BufferToDisplayPos(P));
562
  end;
563
 
564
var
63 daniel-mar 565
  COLOR_FG: TColor;
566
  COLOR_BG: TColor;
31 daniel-mar 567
  P: TBufferCoord;
568
  Pix: TPoint;
569
  D: TDisplayCoord;
570
  S: UnicodeString;
571
  I: Integer;
572
  Attri: TSynHighlighterAttributes;
573
  ArrayLength: Integer;
574
  start: Integer;
575
  TmpCharA, TmpCharB: WideChar;
63 daniel-mar 576
begin
31 daniel-mar 577
  // Source: https://github.com/SynEdit/SynEdit/blob/master/Demos/OnPaintTransientDemo/Unit1.pas
578
 
63 daniel-mar 579
  if IsThemeDark then
580
  begin
581
    COLOR_FG := clLime;
582
    COLOR_BG := clGreen;
583
  end
584
  else
585
  begin
586
    COLOR_FG := clGreen;
587
    COLOR_BG := clLime;
588
  end;
589
 
31 daniel-mar 590
  if TSynEdit(Sender).SelAvail then exit;
591
  Editor := TSynEdit(Sender);
592
  ArrayLength:= 3;
593
 
594
  (*
595
  if (Editor.Highlighter = shHTML) or (Editor.Highlighter = shXML) then
596
    inc(ArrayLength);
597
  *)
598
 
599
  SetLength(OpenChars, ArrayLength);
600
  SetLength(CloseChars, ArrayLength);
601
  for i := 0 to ArrayLength - 1 do
602
  begin
603
    case i of
604
      0: begin OpenChars[i] := '('; CloseChars[i] := ')'; end;
605
      1: begin OpenChars[i] := '{'; CloseChars[i] := '}'; end;
606
      2: begin OpenChars[i] := '['; CloseChars[i] := ']'; end;
607
      3: begin OpenChars[i] := '<'; CloseChars[i] := '>'; end;
608
    end;
609
  end;
610
 
611
  P := Editor.CaretXY;
612
  D := Editor.DisplayXY;
613
 
614
  Start := Editor.SelStart;
615
 
616
  if (Start > 0) and (Start <= length(Editor.Text)) then
617
    TmpCharA := Editor.Text[Start]
618
  else
619
    TmpCharA := #0;
620
 
44 daniel-mar 621
  if (Start > 0){Added by VTS} and (Start < length(Editor.Text)) then
31 daniel-mar 622
    TmpCharB := Editor.Text[Start + 1]
623
  else
624
    TmpCharB := #0;
625
 
626
  if not IsCharBracket(TmpCharA) and not IsCharBracket(TmpCharB) then exit;
627
  S := TmpCharB;
628
  if not IsCharBracket(TmpCharB) then
629
  begin
630
    P.Char := P.Char - 1;
631
    S := TmpCharA;
632
  end;
633
  Editor.GetHighlighterAttriAtRowCol(P, S, Attri);
634
 
635
  if (Editor.Highlighter.SymbolAttribute = Attri) then
636
  begin
637
    for i := low(OpenChars) to High(OpenChars) do
638
    begin
639
      if (S = OpenChars[i]) or (S = CloseChars[i]) then
640
      begin
641
        Pix := CharToPixels(P);
642
 
643
        Editor.Canvas.Brush.Style := bsSolid;//Clear;
644
        Editor.Canvas.Font.Assign(Editor.Font);
645
        Editor.Canvas.Font.Style := Attri.Style;
646
 
647
        if (TransientType = ttAfter) then
648
        begin
649
          Editor.Canvas.Font.Color := COLOR_FG;
650
          Editor.Canvas.Brush.Color := COLOR_BG;
651
        end
652
        else
653
        begin
654
          Editor.Canvas.Font.Color := Attri.Foreground;
655
          Editor.Canvas.Brush.Color := Attri.Background;
656
        end;
657
        if Editor.Canvas.Font.Color = clNone then
658
          Editor.Canvas.Font.Color := Editor.Font.Color;
659
        if Editor.Canvas.Brush.Color = clNone then
660
          Editor.Canvas.Brush.Color := Editor.Color;
661
 
662
        Editor.Canvas.TextOut(Pix.X, Pix.Y, S);
663
        P := Editor.GetMatchingBracketEx(P);
664
 
665
        if (P.Char > 0) and (P.Line > 0) then
666
        begin
667
          Pix := CharToPixels(P);
668
          if Pix.X > Editor.Gutter.Width then
669
          begin
670
            {$REGION 'Added by ViaThinkSoft'}
671
            if (TransientType = ttAfter) then
672
            begin
673
              Editor.Canvas.Font.Color := COLOR_FG;
674
              Editor.Canvas.Brush.Color := COLOR_BG;
675
            end
676
            else
677
            begin
678
              Editor.Canvas.Font.Color := Attri.Foreground;
679
              Editor.Canvas.Brush.Color := Attri.Background;
680
            end;
681
            if Editor.Canvas.Font.Color = clNone then
682
              Editor.Canvas.Font.Color := Editor.Font.Color;
683
            if Editor.Canvas.Brush.Color = clNone then
684
              Editor.Canvas.Brush.Color := Editor.Color;
685
            {$ENDREGION}
686
            if S = OpenChars[i] then
687
              Editor.Canvas.TextOut(Pix.X, Pix.Y, CloseChars[i])
688
            else Editor.Canvas.TextOut(Pix.X, Pix.Y, OpenChars[i]);
689
          end;
690
        end;
691
      end;
692
    end;
693
    Editor.Canvas.Brush.Style := bsSolid;
694
  end;
695
end;
696
 
5 daniel-mar 697
procedure TForm1.SynEditFocusTimerTimer(Sender: TObject);
698
begin
699
  SynEditFocusTimer.Enabled := false;
700
  Button1.SetFocus; // Workaround for weird bug... This (and the timer) is necessary to get the focus to SynEdit1
701
  SynEdit1.SetFocus;
702
end;
2 daniel-mar 703
 
62 daniel-mar 704
procedure TForm1.Theme_Dark;
705
begin
706
  if IsThemeDark then exit;
707
  TStyleManager.TrySetStyle('Windows10 SlateGray');
708
  Color := 1316887;
709
  Font.Color := clCream;
710
  //Memo2.Font.Color := clCream;
711
  //Memo2.ParentColor := true;
712
  SynEdit1.ActiveLineColor := 2238502;
713
  SynEdit1.Color := 1316887;
714
  SynEdit1.Font.Color := clCream;
715
  SynEdit1.Gutter.Color := 1316887;
716
  SynEdit1.Gutter.Font.Color := clCream;
717
  SynEdit1.Gutter.GradientStartColor := 2238502;
718
  SynEdit1.Gutter.GradientEndColor := 1316887;
64 daniel-mar 719
  SynPHPSyn1.CommentAttri.Foreground := $00837B82;
62 daniel-mar 720
  SynPHPSyn1.IdentifierAttri.Foreground := 9627120;
721
  SynPHPSyn1.KeyAttri.Foreground := 4157595;
722
  SynPHPSyn1.NumberAttri.Foreground := 5008079;
723
  SynPHPSyn1.StringAttri.Foreground := 6987151;
724
  SynPHPSyn1.SymbolAttri.Foreground := 8769754;
725
  SynPHPSyn1.VariableAttri.Foreground := 6924493;
726
end;
727
 
728
procedure TForm1.Theme_Light;
729
begin
730
  if not IsThemeDark then exit;
731
  TStyleManager.TrySetStyle('Windows');
732
  Color := clBtnFace;
733
  Font.Color := clWindowText;
734
  //Memo2.Font.Color := clWindowText;
735
  SynEdit1.ActiveLineColor := 14680010;
736
  SynEdit1.Color := clWindow;
737
  SynEdit1.Font.Color := clWindowText;
738
  SynEdit1.Gutter.Color := clBtnFace;
739
  SynEdit1.Gutter.Font.Color := clWindowText;
740
  SynEdit1.Gutter.GradientStartcolor := cl3dLight;
741
  SynEdit1.Gutter.GradientEndColor := clBtnFace;;
742
  SynPHPSyn1.CommentAttri.Foreground := 33023;
743
  SynPHPSyn1.IdentifierAttri.Foreground := 4194304;
744
  SynPHPSyn1.KeyAttri.Foreground := 4227072;
745
  SynPHPSyn1.NumberAttri.Foreground := 213;
746
  SynPHPSyn1.StringAttri.Foreground := 13762560;
747
  SynPHPSyn1.SymbolAttri.Foreground := 4227072;
748
  SynPHPSyn1.VariableAttri.Foreground := 213;
749
end;
750
 
23 daniel-mar 751
procedure TForm1.Timer1Timer(Sender: TObject);
752
begin
753
  {$IFDEF OnlineHelp}
754
  Timer1.Enabled := false;
755
 
756
  // TODO: Insert a small online help hint
757
  //Caption := gOnlineHelpWord;
758
  {$ENDIF}
759
end;
760
 
27 daniel-mar 761
procedure TForm1.TreeView1DblClick(Sender: TObject);
762
var
763
  tn: TTreeNode;
32 daniel-mar 764
  lineNo: integer;
27 daniel-mar 765
begin
766
  tn := TTreeView(Sender).Selected;
767
  if tn = nil then exit;
32 daniel-mar 768
  lineNo := Integer(tn.Data);
769
  if lineNo > 0 then GotoLineNo(lineNo);
27 daniel-mar 770
end;
771
 
45 daniel-mar 772
(*
44 daniel-mar 773
{$IFDEF USE_SHDOCVW_TLB}
45 daniel-mar 774
*)
5 daniel-mar 775
procedure TForm1.WebBrowser1BeforeNavigate2(ASender: TObject;
27 daniel-mar 776
  const pDisp: IDispatch; const URL, Flags, TargetFrameName, PostData,
5 daniel-mar 777
  Headers: OleVariant; var Cancel: WordBool);
44 daniel-mar 778
begin
779
  BeforeNavigate(URL, Cancel);
780
end;
45 daniel-mar 781
(*
44 daniel-mar 782
{$ELSE}
783
procedure TForm1.WebBrowser1BeforeNavigate2(ASender: TObject;
784
  const pDisp: IDispatch; var URL, Flags, TargetFrameName, PostData,
785
  Headers: OleVariant; var Cancel: WordBool);
786
begin
787
  BeforeNavigate(URL, Cancel);
788
end;
789
{$ENDIF}
45 daniel-mar 790
*)
44 daniel-mar 791
 
67 daniel-mar 792
procedure TForm1.WebBrowser1WindowClosing(ASender: TObject;
793
  IsChildWindow: WordBool; var Cancel: WordBool);
794
resourcestring
795
  LNG_CLOSE_REQUEST = 'A script has requested the window to be closed. The window of a standalone script would now close.';
796
begin
797
  ShowMessage(LNG_CLOSE_REQUEST);
798
  TWebBrowser(ASender).Clear;
799
  Cancel := true;
800
end;
801
 
44 daniel-mar 802
procedure TForm1.BeforeNavigate(const URL: OleVariant; var Cancel: WordBool);
5 daniel-mar 803
var
8 daniel-mar 804
  s, myURL: string;
5 daniel-mar 805
  lineno: integer;
7 daniel-mar 806
  p: integer;
5 daniel-mar 807
begin
7 daniel-mar 808
  {$REGION 'Line number references (PHP errors and warnings)'}
8 daniel-mar 809
  if Copy(URL, 1, length(FASTPHP_GOTO_URI_PREFIX)) = FASTPHP_GOTO_URI_PREFIX then
5 daniel-mar 810
  begin
811
    try
8 daniel-mar 812
      s := copy(URL, length(FASTPHP_GOTO_URI_PREFIX)+1, 99);
5 daniel-mar 813
      if not TryStrToInt(s, lineno) then exit;
814
      GotoLineNo(lineno);
815
      SynEditFocusTimer.Enabled := true;
816
    finally
817
      Cancel := true;
818
    end;
8 daniel-mar 819
    Exit;
5 daniel-mar 820
  end;
7 daniel-mar 821
  {$ENDREGION}
822
 
62 daniel-mar 823
  {$REGION 'Intelligent browser (executes PHP scripts which are clicked in a hyperlink)'}
7 daniel-mar 824
  if URL <> 'about:blank' then
825
  begin
826
    myUrl := URL;
827
 
8 daniel-mar 828
    p := Pos('?', myUrl);
829
    if p >= 1 then myURL := copy(myURL, 1, p-1);
7 daniel-mar 830
 
8 daniel-mar 831
    // TODO: myURL urldecode
832
    // TODO: maybe we could even open that file in the editor!
62 daniel-mar 833
    // TODO: ?parameter=....
7 daniel-mar 834
 
8 daniel-mar 835
    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 836
    begin
27 daniel-mar 837
      WebBrowser1.LoadHTML(RunPHPScript(myURL), myUrl);
7 daniel-mar 838
      Cancel := true;
839
    end;
840
  end;
841
  {$ENDREGION}
5 daniel-mar 842
end;
2 daniel-mar 843
 
62 daniel-mar 844
procedure TForm1.BtnLightClick(Sender: TObject);
66 daniel-mar 845
var
846
  CanClose: boolean;
62 daniel-mar 847
begin
66 daniel-mar 848
  FormCloseQuery(Form1, CanClose);
849
  if not CanClose then exit;
850
 
62 daniel-mar 851
  if IsThemeDark then
852
  begin
853
    BtnLight.Picture.Assign(BtnLightOn.Picture);
854
    Theme_Light;
855
    TFastPHPConfig.DarkTheme := false;
856
  end
857
  else
858
  begin
859
    BtnLight.Picture.Assign(BtnLightOff.Picture);
860
    Theme_Dark;
861
    TFastPHPConfig.DarkTheme := true;
862
  end;
863
end;
864
 
57 daniel-mar 865
procedure TForm1.BtnSpecialCharsClick(Sender: TObject);
866
var
867
  opts: TSynEditorOptions;
868
begin
869
  opts := SynEdit1.Options;
870
  if eoShowSpecialChars in SynEdit1.Options then
871
  begin
872
    BtnSpecialChars.Picture.Assign(BtnSpecialCharsOff.Picture);
873
    Exclude(opts, eoShowSpecialChars);
874
    TFastPHPConfig.SpecialChars := false;
875
  end
876
  else
877
  begin
878
    BtnSpecialChars.Picture.Assign(BtnSpecialCharsOn.Picture);
879
    Include(opts, eoShowSpecialChars);
880
    TFastPHPConfig.SpecialChars := true;
881
  end;
882
  SynEdit1.Options := opts;
883
end;
884
 
2 daniel-mar 885
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
886
begin
49 daniel-mar 887
  TFastPHPConfig.FontSize := SynEdit1.Font.Size;
2 daniel-mar 888
end;
889
 
15 daniel-mar 890
procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
891
var
892
  r: integer;
893
begin
894
  if SynEdit1.Modified then
895
  begin
56 daniel-mar 896
    if (ParamStr(1) <> '') or (FSaveAsFilename <> '') then
15 daniel-mar 897
    begin
898
      r := MessageDlg('Do you want to save?', mtConfirmation, mbYesNoCancel, 0);
899
      if r = mrCancel then
900
      begin
901
        CanClose := false;
902
        Exit;
903
      end
904
      else if r = mrYes then
905
      begin
47 daniel-mar 906
        ActionSave.Execute;
15 daniel-mar 907
        CanClose := true;
908
      end;
909
    end
910
    else
911
    begin
47 daniel-mar 912
      ActionSave.Execute;
15 daniel-mar 913
      CanClose := true;
914
    end;
915
  end;
916
end;
917
 
2 daniel-mar 918
procedure TForm1.FormCreate(Sender: TObject);
44 daniel-mar 919
var
920
  exeDir: string;
2 daniel-mar 921
begin
922
  HlpPrevPageIndex := -1;
923
  CurSearchTerm := '';
13 daniel-mar 924
  Caption := Caption + ' - ' + GetScrapFile;
24 daniel-mar 925
  SrcRep := TSynEditFindReplace.Create(self);
13 daniel-mar 926
  SrcRep.Editor := SynEdit1;
29 daniel-mar 927
  SynEdit1.Gutter.Gradient := HighColorWindows;
30 daniel-mar 928
 
929
  Screen.Cursors[crMouseGutter] := LoadCursor(hInstance, 'MOUSEGUTTER');
930
  SynEdit1.Gutter.Cursor := crMouseGutter;
36 daniel-mar 931
 
44 daniel-mar 932
  exeDir := IncludeTrailingPathDelimiter(ExtractFilePath(ParamStr(0)));
933
  if FileExists(exeDir + 'codeexplorer.bmp') then ImageList1.LoadAndSplitImages(exeDir + 'codeexplorer.bmp');
2 daniel-mar 934
end;
935
 
936
procedure TForm1.FormDestroy(Sender: TObject);
937
begin
938
  if Assigned(ChmIndex) then
939
  begin
940
    FreeAndNil(ChmIndex);
941
  end;
13 daniel-mar 942
  FreeAndNil(SrcRep);
27 daniel-mar 943
 
67 daniel-mar 944
  if hMutex <> 0 then CloseHandle(hMutex);
945
 
27 daniel-mar 946
  if Assigned(codeExplorer) then
947
  begin
948
    codeExplorer.Terminate;
949
    codeExplorer.WaitFor;
950
    FreeAndNil(codeExplorer);
951
  end;
2 daniel-mar 952
end;
953
 
71 daniel-mar 954
var
955
  FormShowRanOnce: boolean;
2 daniel-mar 956
procedure TForm1.FormShow(Sender: TObject);
957
var
958
  ScrapFile: string;
49 daniel-mar 959
  tmpFontSize: integer;
57 daniel-mar 960
  opts: TSynEditorOptions;
2 daniel-mar 961
begin
71 daniel-mar 962
  if FormShowRanOnce then exit; // If the theme is changed from normal to dark, OnShow will be called another time
963
  FormShowRanOnce := true;
964
 
2 daniel-mar 965
  ScrapFile := GetScrapFile;
966
  if ScrapFile = '' then
967
  begin
10 daniel-mar 968
    Application.Terminate; // Close;
2 daniel-mar 969
    exit;
970
  end;
57 daniel-mar 971
 
972
  opts := SynEdit1.Options;
973
  if TFastPHPConfig.SpecialChars then
974
  begin
975
    BtnSpecialChars.Picture.Assign(BtnSpecialCharsOn.Picture);
976
    Include(opts, eoShowSpecialChars);
977
  end
978
  else
979
  begin
980
    BtnSpecialChars.Picture.Assign(BtnSpecialCharsOff.Picture);
981
    Exclude(opts, eoShowSpecialChars);
982
  end;
983
  SynEdit1.Options := opts;
984
 
15 daniel-mar 985
  if FileExists(ScrapFile) then
67 daniel-mar 986
  begin
71 daniel-mar 987
    if hMutex = 0 then
67 daniel-mar 988
    begin
71 daniel-mar 989
      hMutex := CreateMutex(nil, True, PChar('FastPHP'+md5(ScrapFile)));
69 daniel-mar 990
      if GetLastError = ERROR_ALREADY_EXISTS then
991
      begin
992
        // TODO: It would be great if the window of that FastPHP instance would switched to foreground
993
        ShowMessageFmt('File "%s" is alrady open!', [ScrapFile]);
994
        Close;
995
      end;
996
 
997
      SynEdit1.Lines.LoadFromFile(ScrapFile);
67 daniel-mar 998
    end;
999
  end
15 daniel-mar 1000
  else
1001
    SynEdit1.Lines.Clear;
2 daniel-mar 1002
 
1003
  PageControl1.ActivePage := PlaintextTabSheet;
1004
 
20 daniel-mar 1005
  PageControl2.ActivePage := CodeTabsheet;
2 daniel-mar 1006
  HelpTabsheet.TabVisible := false;
5 daniel-mar 1007
 
49 daniel-mar 1008
  tmpFontSize := TFastPHPConfig.FontSize;
1009
  if tmpFontSize <> -1 then SynEdit1.Font.Size := tmpFontSize;
5 daniel-mar 1010
  SynEdit1.SetFocus;
27 daniel-mar 1011
 
1012
  DoubleBuffered := true;
1013
  StartCodeExplorer;
62 daniel-mar 1014
 
1015
  StartupTimer.Enabled := true;
2 daniel-mar 1016
end;
1017
 
56 daniel-mar 1018
procedure TForm1.Save1Click(Sender: TObject);
1019
begin
1020
  Button7.Click;
1021
end;
1022
 
1023
procedure TForm1.Saveas1Click(Sender: TObject);
1024
begin
1025
  if SaveDialog1.Execute then
1026
  begin
1027
    FSaveAsFilename := SaveDialog1.FileName;
1028
    Caption := Copy(Caption, 1, Pos(' - ', Caption)-1) + ' - ' + FSaveAsFilename;
1029
    Button7.Click;
1030
  end;
1031
end;
1032
 
27 daniel-mar 1033
procedure TForm1.StartCodeExplorer;
1034
begin
1035
  codeExplorer := TRunCodeExplorer.Create(true);
1036
  codeExplorer.InputRequestCallback := InputRequestCallback;
1037
  codeExplorer.OutputNotifyCallback := OutputNotifyCallback;
1038
  codeExplorer.PhpExe := GetPHPExe;
1039
  codeExplorer.PhpFile := IncludeTrailingPathDelimiter(ExtractFileDir(Application.ExeName)) + 'codeexplorer.php'; // GetScrapFile;
1040
  codeExplorer.WorkDir := ExtractFileDir(Application.ExeName);
44 daniel-mar 1041
  codeExplorer.Resume;
27 daniel-mar 1042
end;
1043
 
62 daniel-mar 1044
procedure TForm1.StartUpTimerTimer(Sender: TObject);
1045
begin
1046
  StartupTimer.Enabled := false;
1047
 
1048
  // We need this timer because we cannot change the Theme during OnShow,
1049
  // because the Delphi VCL Theme is buggy!
1050
 
1051
  if TFastPHPConfig.DarkTheme then
1052
  begin
1053
    BtnLight.Picture.Assign(BtnLightOff.Picture);
1054
    Theme_Dark;
1055
  end
1056
  else
1057
  begin
1058
    BtnLight.Picture.Assign(BtnLightOn.Picture);
1059
    Theme_Light;
1060
  end;
1061
end;
1062
 
2 daniel-mar 1063
function TForm1.GetScrapFile: string;
49 daniel-mar 1064
var
1065
  tmpPath: string;
2 daniel-mar 1066
begin
56 daniel-mar 1067
  if FSaveAsFilename <> '' then
1068
  begin
1069
    result := FSaveAsFilename;
1070
    exit;
1071
  end;
1072
 
44 daniel-mar 1073
  if FScrapFile <> '' then
1074
  begin
1075
    result := FScrapFile;
1076
    exit;
1077
  end;
19 daniel-mar 1078
 
15 daniel-mar 1079
  if ParamStr(1) <> '' then
49 daniel-mar 1080
  begin
1081
    // Program was started with a filename
1082
 
1083
    result := ParamStr(1);
1084
 
1085
    if not FileExists(result) then
1086
    begin
1087
      case MessageDlg(Format('File %s does not exist. Create it?', [result]), mtConfirmation, mbYesNoCancel, 0) of
1088
        mrYes:
1089
          try
1090
            SynEdit1.Lines.SaveToFile(result);
1091
          except
1092
            on E: Exception do
1093
            begin
1094
              MessageDlg(E.Message, mtError, [mbOk], 0);
1095
              Application.Terminate;
1096
              result := '';
1097
              exit;
1098
            end;
1099
          end;
1100
        mrNo:
1101
          begin
1102
            Application.Terminate;
1103
            result := '';
1104
            exit;
1105
          end;
1106
        mrCancel:
1107
          begin
1108
            Application.Terminate;
1109
            result := '';
1110
            exit;
1111
          end;
1112
      end;
1113
    end;
1114
  end
13 daniel-mar 1115
  else
2 daniel-mar 1116
  begin
49 daniel-mar 1117
    // Program is started without filename -> use scrap file
2 daniel-mar 1118
 
49 daniel-mar 1119
    result := TFastPHPConfig.ScrapFile;
1120
 
1121
    if not FileExists(result) then
1122
    begin
1123
      repeat
1124
        {$REGION 'Determinate opendialog initial directory'}
1125
        if result <> '' then
1126
        begin
1127
          tmpPath := ExtractFilePath(result);
1128
          if DirectoryExists(tmpPath) then
1129
          begin
1130
            OpenDialog3.InitialDir := tmpPath;
1131
            OpenDialog3.FileName := Result;
1132
          end
1133
          else
1134
          begin
1135
            OpenDialog3.InitialDir := GetMyDocumentsFolder;
1136
          end;
1137
        end
1138
        else
1139
        begin
1140
          OpenDialog3.InitialDir := GetMyDocumentsFolder;
1141
        end;
1142
        {$ENDREGION}
1143
 
1144
        if not OpenDialog3.Execute then
1145
        begin
1146
          Application.Terminate;
1147
          result := '';
1148
          exit;
1149
        end;
1150
 
1151
        if not DirectoryExists(ExtractFilePath(OpenDialog3.FileName)) then
1152
        begin
1153
          MessageDlg('Path does not exist! Please try again.', mtWarning, [mbOk], 0);
1154
        end
1155
        else
1156
        begin
1157
          result := OpenDialog3.FileName;
1158
        end;
1159
      until result <> '';
1160
 
1161
      if not FileExists(result) then
19 daniel-mar 1162
      begin
49 daniel-mar 1163
        try
1164
          // Try saving the file; check if we have permissions
1165
          //SynEdit1.Lines.Clear;
1166
          SynEdit1.Lines.SaveToFile(result);
1167
        except
1168
          on E: Exception do
1169
          begin
1170
            MessageDlg(E.Message, mtError, [mbOk], 0);
1171
            Application.Terminate;
1172
            result := '';
1173
            exit;
1174
          end;
1175
        end;
19 daniel-mar 1176
      end;
2 daniel-mar 1177
 
49 daniel-mar 1178
      TFastPHPConfig.ScrapFile := result;
1179
      FScrapFile := result;
1180
    end;
2 daniel-mar 1181
  end;
1182
end;
1183
 
1184
procedure TForm1.Help;
1185
var
19 daniel-mar 1186
  IndexFile, chmFile, w, OriginalWord, url: string;
2 daniel-mar 1187
  internalHtmlFile: string;
1188
begin
1189
  if not Assigned(ChmIndex) then
1190
  begin
49 daniel-mar 1191
    IndexFile := TFastPHPConfig.HelpIndex;
2 daniel-mar 1192
    IndexFile := ChangeFileExt(IndexFile, '.ini'); // Just to be sure. Maybe someone wrote manually the ".chm" file in there
1193
    if FileExists(IndexFile) then
1194
    begin
1195
      ChmIndex := TMemIniFile.Create(IndexFile);
1196
    end;
1197
  end;
1198
 
1199
  if Assigned(ChmIndex) then
1200
  begin
49 daniel-mar 1201
    IndexFile := TFastPHPConfig.HelpIndex;
2 daniel-mar 1202
    // We don't check if IndexFile still exists. It is not important since we have ChmIndex pre-loaded in memory
1203
 
1204
    chmFile := ChangeFileExt(IndexFile, '.chm');
1205
    if not FileExists(chmFile) then
1206
    begin
1207
      FreeAndNil(ChmIndex);
1208
    end;
1209
  end;
1210
 
1211
  if not Assigned(ChmIndex) then
1212
  begin
1213
    if not OpenDialog1.Execute then exit;
1214
 
1215
    chmFile := OpenDialog1.FileName;
1216
    if not FileExists(chmFile) then exit;
1217
 
1218
    IndexFile := ChangeFileExt(chmFile, '.ini');
1219
 
1220
    if not FileExists(IndexFile) then
1221
    begin
1222
      Panel1.Align := alClient;
1223
      Panel1.Visible := true;
1224
      Panel1.BringToFront;
1225
      Screen.Cursor := crHourGlass;
1226
      Application.ProcessMessages;
1227
      try
1228
        if not ParseCHM(chmFile) then
1229
        begin
49 daniel-mar 1230
          MessageDlg('The CHM file is not a valid PHP documentation. Cannot use help.', mtError, [mbOk], 0);
2 daniel-mar 1231
          exit;
1232
        end;
1233
      finally
1234
        Screen.Cursor := crDefault;
1235
        Panel1.Visible := false;
1236
      end;
1237
 
1238
      if not FileExists(IndexFile) then
1239
      begin
49 daniel-mar 1240
        MessageDlg('Unknown error. Cannot use help.', mtError, [mbOk], 0);
2 daniel-mar 1241
        exit;
1242
      end;
1243
    end;
1244
 
49 daniel-mar 1245
    TFastPHPConfig.HelpIndex := IndexFile;
2 daniel-mar 1246
 
1247
    ChmIndex := TMemIniFile.Create(IndexFile);
1248
  end;
1249
 
4 daniel-mar 1250
  w := GetWordUnderCaret(SynEdit1);
2 daniel-mar 1251
  if w = '' then exit;
44 daniel-mar 1252
  {$IFDEF UNICODE}
8 daniel-mar 1253
  if CharInSet(w[1], ['0'..'9']) then exit;
44 daniel-mar 1254
  {$ELSE}
1255
  if w[1] in ['0'..'9'] then exit;
1256
  {$ENDIF}
19 daniel-mar 1257
 
1258
  Originalword := w;
1259
//  w := StringReplace(w, '_', '-', [rfReplaceAll]);
2 daniel-mar 1260
  w := LowerCase(w);
1261
  CurSearchTerm := w;
1262
 
1263
  internalHtmlFile := ChmIndex.ReadString('_HelpWords_', CurSearchTerm, '');
1264
  if internalHtmlFile = '' then
1265
  begin
1266
    HelpTabsheet.TabVisible := false;
1267
    HlpPrevPageIndex := -1;
19 daniel-mar 1268
    ShowMessageFmt('No help for "%s" available', [Originalword]);
2 daniel-mar 1269
    Exit;
1270
  end;
1271
 
1272
  url := 'mk:@MSITStore:'+ChmFile+'::'+internalHtmlFile;
1273
 
1274
  HlpPrevPageIndex := PageControl2.ActivePageIndex; // Return by pressing ESC
1275
  HelpTabsheet.TabVisible := true;
1276
  PageControl2.ActivePage := HelpTabsheet;
8 daniel-mar 1277
  WebBrowser2.Navigate(url);
1278
  WebBrowser2.Wait;
2 daniel-mar 1279
end;
1280
 
5 daniel-mar 1281
procedure TForm1.GotoLineNo(LineNo:integer);
1282
var
1283
  line: string;
1284
  i: integer;
2 daniel-mar 1285
begin
5 daniel-mar 1286
  SynEdit1.GotoLineAndCenter(LineNo);
1287
 
1288
  // Skip indent
1289
  line := SynEdit1.Lines[SynEdit1.CaretY];
1290
  for i := 1 to Length(line) do
1291
  begin
44 daniel-mar 1292
    {$IFDEF UNICODE}
8 daniel-mar 1293
    if not CharInSet(line[i], [' ', #9]) then
44 daniel-mar 1294
    {$ELSE}
1295
    if not (line[i] in [' ', #9]) then
1296
    {$ENDIF}
5 daniel-mar 1297
    begin
1298
      SynEdit1.CaretX := i-1;
1299
      break;
1300
    end;
1301
  end;
1302
 
20 daniel-mar 1303
  PageControl2.ActivePage := CodeTabsheet;
5 daniel-mar 1304
  if SynEdit1.CanFocus then SynEdit1.SetFocus;
2 daniel-mar 1305
end;
1306
 
8 daniel-mar 1307
procedure TForm1.PageControl2Changing(Sender: TObject;
1308
  var AllowChange: Boolean);
1309
begin
1310
  if PageControl2.ActivePage = HelpTabsheet then
1311
    HlpPrevPageIndex := -1
1312
  else
1313
    HlpPrevPageIndex := PageControl2.ActivePageIndex;
1314
 
1315
  AllowChange := true;
1316
end;
1317
 
5 daniel-mar 1318
procedure TForm1.Memo2DblClick(Sender: TObject);
1319
var
22 daniel-mar 1320
  line: string;
1321
 
1322
  procedure _process(toFind: string);
1323
  var
1324
    p, lineno: integer;
1325
  begin
1326
    if FileSystemCaseSensitive then
1327
      p := Pos(toFind, line)
1328
    else
44 daniel-mar 1329
      p := Pos(LowerCase(toFind), LowerCase(line));
22 daniel-mar 1330
    if p <> 0 then
1331
    begin
1332
      line := copy(line, p+length(toFind), 99);
1333
      if not TryStrToInt(line, lineno) then exit;
1334
      GotoLineNo(lineno);
1335
    end;
1336
  end;
1337
 
5 daniel-mar 1338
begin
1339
  line := memo2.Lines.Strings[Memo2.CaretPos.Y];
16 daniel-mar 1340
 
18 daniel-mar 1341
  {$REGION 'Possibility 1: filename.php:lineno'}
22 daniel-mar 1342
  _process(ExtractFileName(GetScrapFile) + ':');
18 daniel-mar 1343
  {$ENDREGION}
16 daniel-mar 1344
 
18 daniel-mar 1345
  {$REGION 'Possibility 2: on line xx'}
22 daniel-mar 1346
  _process(ExtractFileName(GetScrapFile) + ' on line ');
18 daniel-mar 1347
  {$ENDREGION}
5 daniel-mar 1348
end;
1349
 
17 daniel-mar 1350
procedure TForm1.Memo2KeyDown(Sender: TObject; var Key: Word;
1351
  Shift: TShiftState);
1352
begin
1353
  if ((ssCtrl in Shift) and (Key = 65)) then TMemo(Sender).SelectAll;
1354
end;
1355
 
5 daniel-mar 1356
function TForm1.MarkUpLineReference(cont: string): string;
18 daniel-mar 1357
 
1358
  procedure _process(toFind: string);
22 daniel-mar 1359
  var
1360
    p, a, b: integer;
1361
    num: integer;
1362
    insert_a, insert_b: string;
5 daniel-mar 1363
  begin
22 daniel-mar 1364
    if FileSystemCaseSensitive then
1365
      p := Pos(toFind, cont)
1366
    else
44 daniel-mar 1367
      p := Pos(LowerCase(toFind), LowerCase(cont));
18 daniel-mar 1368
    while p >= 1 do
5 daniel-mar 1369
    begin
22 daniel-mar 1370
      a := p;
1371
      b := p + length(toFind);
18 daniel-mar 1372
      num := 0;
44 daniel-mar 1373
      {$IFDEF UNICODE}
18 daniel-mar 1374
      while CharInSet(cont[b], ['0'..'9']) do
44 daniel-mar 1375
      {$ELSE}
1376
      while cont[b] in ['0'..'9'] do
1377
      {$ENDIF}
18 daniel-mar 1378
      begin
1379
        num := num*10 + StrToInt(cont[b]);
1380
        inc(b);
1381
      end;
5 daniel-mar 1382
 
18 daniel-mar 1383
      insert_b := '</a>';
22 daniel-mar 1384
      insert_a := '<a href="' + FASTPHP_GOTO_URI_PREFIX + IntToStr(num) + '">';
5 daniel-mar 1385
 
18 daniel-mar 1386
      insert(insert_b, cont, b);
1387
      insert(insert_a, cont, a);
5 daniel-mar 1388
 
18 daniel-mar 1389
      p := b + Length(insert_a) + Length(insert_b);
5 daniel-mar 1390
 
18 daniel-mar 1391
      p := PosEx(toFind, cont, p+1);
1392
    end;
5 daniel-mar 1393
  end;
22 daniel-mar 1394
 
18 daniel-mar 1395
begin
1396
  {$REGION 'Possibility 1: filename.php:lineno'}
22 daniel-mar 1397
  _process(ExtractFileName(GetScrapFile) + ':');
18 daniel-mar 1398
  {$ENDREGION}
5 daniel-mar 1399
 
18 daniel-mar 1400
  {$REGION 'Possibility 2: on line xx'}
22 daniel-mar 1401
  _process(ExtractFileName(GetScrapFile) + ' on line ');
18 daniel-mar 1402
  {$ENDREGION}
1403
 
5 daniel-mar 1404
  result := cont;
1405
end;
1406
 
40 daniel-mar 1407
function TForm1.InputRequestCallback(var data: AnsiString): boolean;
27 daniel-mar 1408
begin
40 daniel-mar 1409
  data := UTF8Encode(SynEdit1.Text);
1410
  result := true;
27 daniel-mar 1411
end;
1412
 
62 daniel-mar 1413
function TForm1.IsThemeDark: boolean;
1414
begin
1415
  result := Assigned(TStyleManager.ActiveStyle) and (TStyleManager.ActiveStyle.Name<>'Windows');
1416
end;
1417
 
40 daniel-mar 1418
function TForm1.OutputNotifyCallback(const data: AnsiString): boolean;
27 daniel-mar 1419
begin
40 daniel-mar 1420
  result := TreeView1.FillWithFastPHPData(data);
27 daniel-mar 1421
end;
1422
 
2 daniel-mar 1423
end.