Subversion Repositories fastphp

Rev

Rev 72 | Rev 76 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

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