Subversion Repositories fastphp

Rev

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