Subversion Repositories fastphp

Rev

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