Subversion Repositories fastphp

Rev

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