Subversion Repositories fastphp

Rev

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