Subversion Repositories fastphp

Rev

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