Subversion Repositories fastphp

Rev

Rev 29 | Rev 31 | 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
21 daniel-mar 22
// - code explorer / 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,
27 daniel-mar 36
  SynEditHighlighter, SynHighlighterPHP, SynEdit, ShDocVw_TLB, FindReplace,
37
  System.Actions, Vcl.ActnList, System.UITypes, SynEditMiscClasses,
38
  SynEditSearch, RunPHP;
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;
2 daniel-mar 87
    procedure Run(Sender: TObject);
88
    procedure FormShow(Sender: TObject);
89
    procedure FormCreate(Sender: TObject);
90
    procedure FormDestroy(Sender: TObject);
91
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
92
    procedure PageControl2Changing(Sender: TObject; var AllowChange: Boolean);
5 daniel-mar 93
    procedure Memo2DblClick(Sender: TObject);
94
    procedure WebBrowser1BeforeNavigate2(ASender: TObject;
27 daniel-mar 95
      const pDisp: IDispatch; const URL, Flags, TargetFrameName, PostData,
5 daniel-mar 96
      Headers: OleVariant; var Cancel: WordBool);
97
    procedure SynEditFocusTimerTimer(Sender: TObject);
13 daniel-mar 98
    procedure ActionFindExecute(Sender: TObject);
99
    procedure ActionReplaceExecute(Sender: TObject);
100
    procedure ActionFindNextExecute(Sender: TObject);
101
    procedure ActionGotoExecute(Sender: TObject);
102
    procedure ActionSaveExecute(Sender: TObject);
103
    procedure ActionHelpExecute(Sender: TObject);
104
    procedure ActionRunExecute(Sender: TObject);
105
    procedure ActionESCExecute(Sender: TObject);
106
    procedure SynEdit1MouseWheelDown(Sender: TObject; Shift: TShiftState;
107
      MousePos: TPoint; var Handled: Boolean);
108
    procedure SynEdit1MouseWheelUp(Sender: TObject; Shift: TShiftState;
109
      MousePos: TPoint; var Handled: Boolean);
15 daniel-mar 110
    procedure ActionOpenExecute(Sender: TObject);
111
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
17 daniel-mar 112
    procedure Memo2KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
22 daniel-mar 113
    procedure ActionFindPrevExecute(Sender: TObject);
23 daniel-mar 114
    procedure SynEdit1MouseCursor(Sender: TObject;
115
      const aLineCharPos: TBufferCoord; var aCursor: TCursor);
116
    procedure Timer1Timer(Sender: TObject);
117
    procedure ActionSpaceToTabExecute(Sender: TObject);
27 daniel-mar 118
    procedure TreeView1DblClick(Sender: TObject);
30 daniel-mar 119
    procedure SynEdit1GutterClick(Sender: TObject; Button: TMouseButton; X, Y,
120
      Line: Integer; Mark: TSynEditMark);
2 daniel-mar 121
  private
122
    CurSearchTerm: string;
123
    HlpPrevPageIndex: integer;
24 daniel-mar 124
    SrcRep: TSynEditFindReplace;
23 daniel-mar 125
    {$IFDEF OnlineHelp}
126
    gOnlineHelpWord: string;
127
    {$ENDIF}
2 daniel-mar 128
    procedure Help;
5 daniel-mar 129
    function MarkUpLineReference(cont: string): string;
27 daniel-mar 130
    function InputRequestCallback: string;
131
    procedure OutputNotifyCallback(const data: string);
2 daniel-mar 132
  protected
133
    ChmIndex: TMemIniFile;
19 daniel-mar 134
    FScrapFile: string;
27 daniel-mar 135
    codeExplorer: TRunCodeExplorer;
136
    procedure GotoLineNo(LineNo: integer);
2 daniel-mar 137
    function GetScrapFile: string;
27 daniel-mar 138
    procedure StartCodeExplorer;
2 daniel-mar 139
  end;
140
 
141
var
142
  Form1: TForm1;
143
 
144
implementation
145
 
146
{$R *.dfm}
147
 
30 daniel-mar 148
{$R Cursors.res}
149
 
2 daniel-mar 150
uses
25 daniel-mar 151
  Functions, StrUtils, WebBrowserUtils, FastPHPUtils, Math, ShellAPI, RichEdit,
27 daniel-mar 152
  FastPHPTreeView;
2 daniel-mar 153
 
30 daniel-mar 154
const
155
  crMouseGutter = 1;
156
 
13 daniel-mar 157
// TODO: FindPrev ?
158
procedure TForm1.ActionFindNextExecute(Sender: TObject);
159
begin
160
  SrcRep.FindNext;
161
end;
162
 
22 daniel-mar 163
procedure TForm1.ActionFindPrevExecute(Sender: TObject);
164
begin
165
  SrcRep.FindPrev;
166
end;
167
 
13 daniel-mar 168
procedure TForm1.ActionGotoExecute(Sender: TObject);
5 daniel-mar 169
var
170
  val: string;
171
  lineno: integer;
172
begin
13 daniel-mar 173
  // TODO: VK_LMENU does not work! only works with AltGr but not Alt
174
  // http://stackoverflow.com/questions/16828250/delphi-xe2-how-to-prevent-the-alt-key-stealing-focus ?
5 daniel-mar 175
 
13 daniel-mar 176
  InputQuery('Go to', 'Line number:', val);
177
  if not TryStrToInt(val, lineno) then
178
  begin
179
    if SynEdit1.CanFocus then SynEdit1.SetFocus;
180
    exit;
181
  end;
182
  GotoLineNo(lineno);
183
end;
5 daniel-mar 184
 
13 daniel-mar 185
procedure TForm1.ActionHelpExecute(Sender: TObject);
186
begin
187
  Help;
188
  if PageControl2.ActivePage = HelpTabsheet then
189
    WebBrowser2.SetFocus
20 daniel-mar 190
  else if PageControl2.ActivePage = CodeTabsheet then
13 daniel-mar 191
    SynEdit1.SetFocus;
192
end;
8 daniel-mar 193
 
15 daniel-mar 194
procedure TForm1.ActionOpenExecute(Sender: TObject);
195
begin
196
  If OpenDialog3.Execute then
197
  begin
198
    ShellExecute(0, 'open', PChar(ParamStr(0)), PChar(OpenDialog3.FileName), '', SW_NORMAL);
199
  end;
200
end;
201
 
13 daniel-mar 202
procedure TForm1.ActionReplaceExecute(Sender: TObject);
203
begin
204
  SrcRep.ReplaceExecute;
205
end;
5 daniel-mar 206
 
13 daniel-mar 207
procedure TForm1.ActionRunExecute(Sender: TObject);
208
begin
209
  Run(Sender);
210
  SynEdit1.SetFocus;
211
end;
5 daniel-mar 212
 
13 daniel-mar 213
procedure TForm1.ActionSaveExecute(Sender: TObject);
214
begin
27 daniel-mar 215
  SynEdit1.Lines.SaveToFile(GetScrapFile);
16 daniel-mar 216
  SynEdit1.Modified := false;
13 daniel-mar 217
end;
218
 
23 daniel-mar 219
procedure TForm1.ActionSpaceToTabExecute(Sender: TObject);
220
 
221
    function SpacesAtBeginning(line: string): integer;
222
    begin
27 daniel-mar 223
      if line.Trim = '' then exit(0);
23 daniel-mar 224
      result := 0;
225
      while line[result+1] = ' ' do
226
      begin
227
        inc(result);
228
      end;
229
    end;
230
 
27 daniel-mar 231
    function GuessIndent(lines: TStrings): integer;
23 daniel-mar 232
      function _Check(indent: integer): boolean;
233
      var
234
        i: integer;
235
      begin
236
        result := true;
237
        for i := 0 to lines.Count-1 do
238
          if SpacesAtBeginning(lines.Strings[i]) mod indent <> 0 then
239
          begin
240
            // ShowMessageFmt('Zeile "%s" nicht durch %d teilbar!', [lines.strings[i], indent]);
27 daniel-mar 241
            exit(false);
23 daniel-mar 242
          end;
243
      end;
244
    var
245
      i: integer;
246
    begin
247
      for i := 8 downto 2 do
248
      begin
27 daniel-mar 249
        if _Check(i) then exit(i);
23 daniel-mar 250
      end;
251
      result := -1;
252
    end;
253
 
27 daniel-mar 254
    procedure SpaceToTab(lines: TStrings; indent: integer);
23 daniel-mar 255
    var
256
      i, spaces: integer;
257
    begin
258
      for i := 0 to lines.Count-1 do
259
      begin
260
        spaces := SpacesAtBeginning(lines.Strings[i]);
261
        lines.Strings[i] := StringOfChar(#9, spaces div indent) + StringOfChar(' ', spaces mod indent) + Copy(lines.Strings[i], spaces+1, Length(lines.Strings[i])-spaces);
262
      end;
263
    end;
264
 
27 daniel-mar 265
    function SpacesAvailable(lines: TStrings): boolean;
23 daniel-mar 266
    var
267
      i, spaces: integer;
268
    begin
269
      for i := 0 to lines.Count-1 do
270
      begin
271
        spaces := SpacesAtBeginning(lines.Strings[i]);
27 daniel-mar 272
        if spaces > 0 then exit(true);
23 daniel-mar 273
      end;
27 daniel-mar 274
      exit(false);
23 daniel-mar 275
    end;
276
 
277
var
278
  val: string;
279
  ind: integer;
280
resourcestring
281
  SNoLinesAvailable = 'No lines with spaces at the beginning available';
282
begin
283
  // TODO: if something is selected, only process the selected part
284
 
285
  if not SpacesAvailable(SynEdit1.Lines) then
286
  begin
287
    ShowMessage(SNoLinesAvailable);
288
    exit;
289
  end;
290
 
291
  ind := GuessIndent(SynEdit1.Lines);
292
  if ind <> -1 then val := IntToStr(ind);
293
 
294
  InputQuery('Spaces to tabs', 'Indent:', val); // TODO: handle CANCEL correctly...
27 daniel-mar 295
  if TryStrToInt(val.Trim, ind) then
23 daniel-mar 296
  begin
297
    if ind = 0 then exit;
298
    SpaceToTab(SynEdit1.Lines, ind);
299
  end;
300
 
301
  if SynEdit1.CanFocus then SynEdit1.SetFocus;
302
end;
303
 
13 daniel-mar 304
procedure TForm1.ActionESCExecute(Sender: TObject);
305
begin
306
  if (HlpPrevPageIndex <> -1) and (PageControl2.ActivePage = HelpTabSheet) and
307
     (HelpTabsheet.TabVisible) then
308
  begin
309
    PageControl2.ActivePageIndex := HlpPrevPageIndex;
310
    HelpTabsheet.TabVisible := false;
2 daniel-mar 311
  end;
13 daniel-mar 312
 
313
  // Dirty hack...
22 daniel-mar 314
  SrcRep.CloseDialogs;
2 daniel-mar 315
end;
316
 
13 daniel-mar 317
procedure TForm1.ActionFindExecute(Sender: TObject);
318
begin
319
  SrcRep.FindExecute;
320
end;
321
 
16 daniel-mar 322
var
323
  firstTimeBrowserLoad: boolean = true;
2 daniel-mar 324
procedure TForm1.Run(Sender: TObject);
16 daniel-mar 325
var
326
  bakTS: TTabSheet;
2 daniel-mar 327
begin
5 daniel-mar 328
  memo2.Lines.Text := '';
16 daniel-mar 329
 
330
  if firstTimeBrowserLoad then
331
  begin
332
    bakTS := PageControl1.ActivePage;
333
    try
334
      PageControl1.ActivePage := HtmlTabSheet; // Required for the first time, otherwise, WebBrowser1.Clear will hang
335
      Webbrowser1.Clear;
336
    finally
337
      PageControl1.ActivePage := bakTS;
338
    end;
339
    firstTimeBrowserLoad := false;
340
  end
341
  else
342
    Webbrowser1.Clear;
343
 
5 daniel-mar 344
  Screen.Cursor := crHourGlass;
345
  Application.ProcessMessages;
346
 
347
  try
27 daniel-mar 348
    SynEdit1.Lines.SaveToFile(GetScrapFile);
5 daniel-mar 349
 
8 daniel-mar 350
    memo2.Lines.Text := RunPHPScript(GetScrapFile);
5 daniel-mar 351
 
8 daniel-mar 352
    Webbrowser1.LoadHTML(MarkUpLineReference(memo2.Lines.Text), GetScrapFile);
5 daniel-mar 353
 
354
    if IsTextHTML(memo2.lines.text) then
355
      PageControl1.ActivePage := HtmlTabSheet
356
    else
357
      PageControl1.ActivePage := PlaintextTabSheet;
358
  finally
359
    Screen.Cursor := crDefault;
2 daniel-mar 360
  end;
5 daniel-mar 361
end;
2 daniel-mar 362
 
30 daniel-mar 363
procedure TForm1.SynEdit1GutterClick(Sender: TObject; Button: TMouseButton; X,
364
  Y, Line: Integer; Mark: TSynEditMark);
365
begin
366
  (*
367
  TSynEdit(Sender).CaretX := 1;
368
  TSynEdit(Sender).CaretY := Line;
369
  TSynEdit(Sender).SelLength := Length(TSynEdit(Sender).LineText);
370
  *)
371
end;
372
 
23 daniel-mar 373
procedure TForm1.SynEdit1MouseCursor(Sender: TObject; const aLineCharPos: TBufferCoord; var aCursor: TCursor);
374
{$IFDEF OnlineHelp}
375
var
376
  Line: Integer;
377
  Column: Integer;
378
  word: string;
379
begin
380
  Line  := aLineCharPos.Line-1;
381
  Column := aLineCharPos.Char-1;
382
  word := GetWordUnderPos(TSynEdit(Sender), Line, Column);
383
  if word <> gOnlineHelpWord then
384
  begin
385
    gOnlineHelpWord := word;
386
    Timer1.Enabled := false;
387
    Timer1.Enabled := true;
388
  end;
389
{$ELSE}
390
begin
391
{$ENDIF}
392
end;
393
 
13 daniel-mar 394
procedure TForm1.SynEdit1MouseWheelDown(Sender: TObject; Shift: TShiftState;
395
  MousePos: TPoint; var Handled: Boolean);
396
begin
397
  if ssCtrl in Shift then
398
  begin
399
    SynEdit1.Font.Size := Max(SynEdit1.Font.Size - 1, 5);
23 daniel-mar 400
    Handled := true;
401
  end
402
  else Handled := false;
13 daniel-mar 403
end;
404
 
405
procedure TForm1.SynEdit1MouseWheelUp(Sender: TObject; Shift: TShiftState;
406
  MousePos: TPoint; var Handled: Boolean);
407
begin
408
  if ssCtrl in Shift then
409
  begin
410
    SynEdit1.Font.Size := SynEdit1.Font.Size + 1;
23 daniel-mar 411
    Handled := true;
412
  end
413
  else Handled := false;
13 daniel-mar 414
end;
415
 
5 daniel-mar 416
procedure TForm1.SynEditFocusTimerTimer(Sender: TObject);
417
begin
418
  SynEditFocusTimer.Enabled := false;
419
  Button1.SetFocus; // Workaround for weird bug... This (and the timer) is necessary to get the focus to SynEdit1
420
  SynEdit1.SetFocus;
421
end;
2 daniel-mar 422
 
23 daniel-mar 423
procedure TForm1.Timer1Timer(Sender: TObject);
424
begin
425
  {$IFDEF OnlineHelp}
426
  Timer1.Enabled := false;
427
 
428
  // TODO: Insert a small online help hint
429
  //Caption := gOnlineHelpWord;
430
  {$ENDIF}
431
end;
432
 
27 daniel-mar 433
procedure TForm1.TreeView1DblClick(Sender: TObject);
434
var
435
  tn: TTreeNode;
436
begin
437
  tn := TTreeView(Sender).Selected;
438
  if tn = nil then exit;
439
  GotoLineNo(Integer(tn.Data));
440
end;
441
 
5 daniel-mar 442
procedure TForm1.WebBrowser1BeforeNavigate2(ASender: TObject;
27 daniel-mar 443
  const pDisp: IDispatch; const URL, Flags, TargetFrameName, PostData,
5 daniel-mar 444
  Headers: OleVariant; var Cancel: WordBool);
445
var
8 daniel-mar 446
  s, myURL: string;
5 daniel-mar 447
  lineno: integer;
7 daniel-mar 448
  p: integer;
5 daniel-mar 449
begin
7 daniel-mar 450
  {$REGION 'Line number references (PHP errors and warnings)'}
8 daniel-mar 451
  if Copy(URL, 1, length(FASTPHP_GOTO_URI_PREFIX)) = FASTPHP_GOTO_URI_PREFIX then
5 daniel-mar 452
  begin
453
    try
8 daniel-mar 454
      s := copy(URL, length(FASTPHP_GOTO_URI_PREFIX)+1, 99);
5 daniel-mar 455
      if not TryStrToInt(s, lineno) then exit;
456
      GotoLineNo(lineno);
457
      SynEditFocusTimer.Enabled := true;
458
    finally
459
      Cancel := true;
460
    end;
8 daniel-mar 461
    Exit;
5 daniel-mar 462
  end;
7 daniel-mar 463
  {$ENDREGION}
464
 
8 daniel-mar 465
  {$REGION 'Intelligent browser (executes PHP scripts)'}
7 daniel-mar 466
  if URL <> 'about:blank' then
467
  begin
468
    myUrl := URL;
469
 
8 daniel-mar 470
    p := Pos('?', myUrl);
471
    if p >= 1 then myURL := copy(myURL, 1, p-1);
7 daniel-mar 472
 
8 daniel-mar 473
    // TODO: myURL urldecode
474
    // TODO: maybe we could even open that file in the editor!
7 daniel-mar 475
 
8 daniel-mar 476
    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 477
    begin
27 daniel-mar 478
      WebBrowser1.LoadHTML(RunPHPScript(myURL), myUrl);
7 daniel-mar 479
      Cancel := true;
480
    end;
481
  end;
482
  {$ENDREGION}
5 daniel-mar 483
end;
2 daniel-mar 484
 
485
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
486
begin
13 daniel-mar 487
  FastPHPConfig.WriteInteger('User', 'FontSize', SynEdit1.Font.Size);
2 daniel-mar 488
end;
489
 
15 daniel-mar 490
procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
491
var
492
  r: integer;
493
begin
494
  if SynEdit1.Modified then
495
  begin
496
    if ParamStr(1) <> '' then
497
    begin
498
      r := MessageDlg('Do you want to save?', mtConfirmation, mbYesNoCancel, 0);
499
      if r = mrCancel then
500
      begin
501
        CanClose := false;
502
        Exit;
503
      end
504
      else if r = mrYes then
505
      begin
27 daniel-mar 506
        SynEdit1.Lines.SaveToFile(GetScrapFile);
15 daniel-mar 507
        CanClose := true;
508
      end;
509
    end
510
    else
511
    begin
27 daniel-mar 512
      SynEdit1.Lines.SaveToFile(GetScrapFile);
15 daniel-mar 513
      CanClose := true;
514
    end;
515
  end;
516
end;
517
 
2 daniel-mar 518
procedure TForm1.FormCreate(Sender: TObject);
519
begin
520
  HlpPrevPageIndex := -1;
521
  CurSearchTerm := '';
13 daniel-mar 522
  Caption := Caption + ' - ' + GetScrapFile;
24 daniel-mar 523
  SrcRep := TSynEditFindReplace.Create(self);
13 daniel-mar 524
  SrcRep.Editor := SynEdit1;
29 daniel-mar 525
  SynEdit1.Gutter.Gradient := HighColorWindows;
30 daniel-mar 526
 
527
  Screen.Cursors[crMouseGutter] := LoadCursor(hInstance, 'MOUSEGUTTER');
528
  SynEdit1.Gutter.Cursor := crMouseGutter;
2 daniel-mar 529
end;
530
 
531
procedure TForm1.FormDestroy(Sender: TObject);
532
begin
533
  if Assigned(ChmIndex) then
534
  begin
535
    FreeAndNil(ChmIndex);
536
  end;
13 daniel-mar 537
  FreeAndNil(SrcRep);
27 daniel-mar 538
 
539
  if Assigned(codeExplorer) then
540
  begin
541
    codeExplorer.Terminate;
542
    codeExplorer.WaitFor;
543
    FreeAndNil(codeExplorer);
544
  end;
2 daniel-mar 545
end;
546
 
547
procedure TForm1.FormShow(Sender: TObject);
548
var
549
  ScrapFile: string;
550
begin
551
  ScrapFile := GetScrapFile;
552
  if ScrapFile = '' then
553
  begin
10 daniel-mar 554
    Application.Terminate; // Close;
2 daniel-mar 555
    exit;
556
  end;
15 daniel-mar 557
  if FileExists(ScrapFile) then
558
    SynEdit1.Lines.LoadFromFile(ScrapFile)
559
  else
560
    SynEdit1.Lines.Clear;
2 daniel-mar 561
 
562
  PageControl1.ActivePage := PlaintextTabSheet;
563
 
20 daniel-mar 564
  PageControl2.ActivePage := CodeTabsheet;
2 daniel-mar 565
  HelpTabsheet.TabVisible := false;
5 daniel-mar 566
 
13 daniel-mar 567
  SynEdit1.Font.Size := FastPHPConfig.ReadInteger('User', 'FontSize', SynEdit1.Font.Size);
5 daniel-mar 568
  SynEdit1.SetFocus;
27 daniel-mar 569
 
570
  DoubleBuffered := true;
571
  StartCodeExplorer;
2 daniel-mar 572
end;
573
 
27 daniel-mar 574
procedure TForm1.StartCodeExplorer;
575
begin
576
  codeExplorer := TRunCodeExplorer.Create(true);
577
  codeExplorer.InputRequestCallback := InputRequestCallback;
578
  codeExplorer.OutputNotifyCallback := OutputNotifyCallback;
579
  codeExplorer.PhpExe := GetPHPExe;
580
  codeExplorer.PhpFile := IncludeTrailingPathDelimiter(ExtractFileDir(Application.ExeName)) + 'codeexplorer.php'; // GetScrapFile;
581
  codeExplorer.WorkDir := ExtractFileDir(Application.ExeName);
582
  codeExplorer.Start;
583
end;
584
 
2 daniel-mar 585
function TForm1.GetScrapFile: string;
586
begin
27 daniel-mar 587
  if FScrapFile <> '' then exit(FScrapFile);
19 daniel-mar 588
 
15 daniel-mar 589
  if ParamStr(1) <> '' then
13 daniel-mar 590
    result := ParamStr(1)
591
  else
592
    result := FastPHPConfig.ReadString('Paths', 'ScrapFile', '');
2 daniel-mar 593
  if not FileExists(result) then
594
  begin
19 daniel-mar 595
    repeat
596
      if not OpenDialog3.Execute then
597
      begin
598
        Application.Terminate;
27 daniel-mar 599
        exit('');
19 daniel-mar 600
      end;
2 daniel-mar 601
 
19 daniel-mar 602
      if not DirectoryExists(ExtractFilePath(OpenDialog3.FileName)) then
603
      begin
604
        ShowMessage('Path does not exist! Please try again.');
605
      end
606
      else
607
      begin
608
        result := OpenDialog3.FileName;
609
      end;
610
    until result <> '';
2 daniel-mar 611
 
4 daniel-mar 612
    SynEdit1.Lines.Clear;
27 daniel-mar 613
    SynEdit1.Lines.SaveToFile(result);
2 daniel-mar 614
 
615
    FastPHPConfig.WriteString('Paths', 'ScrapFile', result);
19 daniel-mar 616
    FScrapFile := result;
2 daniel-mar 617
  end;
618
end;
619
 
620
procedure TForm1.Help;
621
var
19 daniel-mar 622
  IndexFile, chmFile, w, OriginalWord, url: string;
2 daniel-mar 623
  internalHtmlFile: string;
624
begin
625
  if not Assigned(ChmIndex) then
626
  begin
627
    IndexFile := FastPHPConfig.ReadString('Paths', 'HelpIndex', '');
628
    IndexFile := ChangeFileExt(IndexFile, '.ini'); // Just to be sure. Maybe someone wrote manually the ".chm" file in there
629
    if FileExists(IndexFile) then
630
    begin
631
      ChmIndex := TMemIniFile.Create(IndexFile);
632
    end;
633
  end;
634
 
635
  if Assigned(ChmIndex) then
636
  begin
637
    IndexFile := FastPHPConfig.ReadString('Paths', 'HelpIndex', '');
638
    // We don't check if IndexFile still exists. It is not important since we have ChmIndex pre-loaded in memory
639
 
640
    chmFile := ChangeFileExt(IndexFile, '.chm');
641
    if not FileExists(chmFile) then
642
    begin
643
      FreeAndNil(ChmIndex);
644
    end;
645
  end;
646
 
647
  if not Assigned(ChmIndex) then
648
  begin
649
    if not OpenDialog1.Execute then exit;
650
 
651
    chmFile := OpenDialog1.FileName;
652
    if not FileExists(chmFile) then exit;
653
 
654
    IndexFile := ChangeFileExt(chmFile, '.ini');
655
 
656
    if not FileExists(IndexFile) then
657
    begin
658
      Panel1.Align := alClient;
659
      Panel1.Visible := true;
660
      Panel1.BringToFront;
661
      Screen.Cursor := crHourGlass;
662
      Application.ProcessMessages;
663
      try
664
        if not ParseCHM(chmFile) then
665
        begin
666
          ShowMessage('The CHM file is not a valid PHP documentation. Cannot use help.');
667
          exit;
668
        end;
669
      finally
670
        Screen.Cursor := crDefault;
671
        Panel1.Visible := false;
672
      end;
673
 
674
      if not FileExists(IndexFile) then
675
      begin
676
        ShowMessage('Unknown error. Cannot use help.');
677
        exit;
678
      end;
679
    end;
680
 
681
    FastPHPConfig.WriteString('Paths', 'HelpIndex', IndexFile);
682
    FastPHPConfig.UpdateFile;
683
 
684
    ChmIndex := TMemIniFile.Create(IndexFile);
685
  end;
686
 
4 daniel-mar 687
  w := GetWordUnderCaret(SynEdit1);
2 daniel-mar 688
  if w = '' then exit;
8 daniel-mar 689
  if CharInSet(w[1], ['0'..'9']) then exit;
19 daniel-mar 690
 
691
  Originalword := w;
692
//  w := StringReplace(w, '_', '-', [rfReplaceAll]);
2 daniel-mar 693
  w := LowerCase(w);
694
  CurSearchTerm := w;
695
 
696
  internalHtmlFile := ChmIndex.ReadString('_HelpWords_', CurSearchTerm, '');
697
  if internalHtmlFile = '' then
698
  begin
699
    HelpTabsheet.TabVisible := false;
700
    HlpPrevPageIndex := -1;
19 daniel-mar 701
    ShowMessageFmt('No help for "%s" available', [Originalword]);
2 daniel-mar 702
    Exit;
703
  end;
704
 
705
  url := 'mk:@MSITStore:'+ChmFile+'::'+internalHtmlFile;
706
 
707
  HlpPrevPageIndex := PageControl2.ActivePageIndex; // Return by pressing ESC
708
  HelpTabsheet.TabVisible := true;
709
  PageControl2.ActivePage := HelpTabsheet;
8 daniel-mar 710
  WebBrowser2.Navigate(url);
711
  WebBrowser2.Wait;
2 daniel-mar 712
end;
713
 
5 daniel-mar 714
procedure TForm1.GotoLineNo(LineNo:integer);
715
var
716
  line: string;
717
  i: integer;
2 daniel-mar 718
begin
5 daniel-mar 719
  SynEdit1.GotoLineAndCenter(LineNo);
720
 
721
  // Skip indent
722
  line := SynEdit1.Lines[SynEdit1.CaretY];
723
  for i := 1 to Length(line) do
724
  begin
8 daniel-mar 725
    if not CharInSet(line[i], [' ', #9]) then
5 daniel-mar 726
    begin
727
      SynEdit1.CaretX := i-1;
728
      break;
729
    end;
730
  end;
731
 
20 daniel-mar 732
  PageControl2.ActivePage := CodeTabsheet;
5 daniel-mar 733
  if SynEdit1.CanFocus then SynEdit1.SetFocus;
2 daniel-mar 734
end;
735
 
8 daniel-mar 736
procedure TForm1.PageControl2Changing(Sender: TObject;
737
  var AllowChange: Boolean);
738
begin
739
  if PageControl2.ActivePage = HelpTabsheet then
740
    HlpPrevPageIndex := -1
741
  else
742
    HlpPrevPageIndex := PageControl2.ActivePageIndex;
743
 
744
  AllowChange := true;
745
end;
746
 
5 daniel-mar 747
procedure TForm1.Memo2DblClick(Sender: TObject);
748
var
22 daniel-mar 749
  line: string;
750
 
751
  procedure _process(toFind: string);
752
  var
753
    p, lineno: integer;
754
  begin
755
    if FileSystemCaseSensitive then
756
      p := Pos(toFind, line)
757
    else
27 daniel-mar 758
      p := Pos(toFind.ToLower, line.ToLower);
22 daniel-mar 759
    if p <> 0 then
760
    begin
761
      line := copy(line, p+length(toFind), 99);
762
      if not TryStrToInt(line, lineno) then exit;
763
      GotoLineNo(lineno);
764
    end;
765
  end;
766
 
5 daniel-mar 767
begin
768
  line := memo2.Lines.Strings[Memo2.CaretPos.Y];
16 daniel-mar 769
 
18 daniel-mar 770
  {$REGION 'Possibility 1: filename.php:lineno'}
22 daniel-mar 771
  _process(ExtractFileName(GetScrapFile) + ':');
18 daniel-mar 772
  {$ENDREGION}
16 daniel-mar 773
 
18 daniel-mar 774
  {$REGION 'Possibility 2: on line xx'}
22 daniel-mar 775
  _process(ExtractFileName(GetScrapFile) + ' on line ');
18 daniel-mar 776
  {$ENDREGION}
5 daniel-mar 777
end;
778
 
17 daniel-mar 779
procedure TForm1.Memo2KeyDown(Sender: TObject; var Key: Word;
780
  Shift: TShiftState);
781
begin
782
  if ((ssCtrl in Shift) and (Key = 65)) then TMemo(Sender).SelectAll;
783
end;
784
 
5 daniel-mar 785
function TForm1.MarkUpLineReference(cont: string): string;
18 daniel-mar 786
 
787
  procedure _process(toFind: string);
22 daniel-mar 788
  var
789
    p, a, b: integer;
790
    num: integer;
791
    insert_a, insert_b: string;
5 daniel-mar 792
  begin
22 daniel-mar 793
    if FileSystemCaseSensitive then
794
      p := Pos(toFind, cont)
795
    else
27 daniel-mar 796
      p := Pos(toFind.ToLower, cont.ToLower);
18 daniel-mar 797
    while p >= 1 do
5 daniel-mar 798
    begin
22 daniel-mar 799
      a := p;
800
      b := p + length(toFind);
18 daniel-mar 801
      num := 0;
802
      while CharInSet(cont[b], ['0'..'9']) do
803
      begin
804
        num := num*10 + StrToInt(cont[b]);
805
        inc(b);
806
      end;
5 daniel-mar 807
 
18 daniel-mar 808
      insert_b := '</a>';
22 daniel-mar 809
      insert_a := '<a href="' + FASTPHP_GOTO_URI_PREFIX + IntToStr(num) + '">';
5 daniel-mar 810
 
18 daniel-mar 811
      insert(insert_b, cont, b);
812
      insert(insert_a, cont, a);
5 daniel-mar 813
 
18 daniel-mar 814
      p := b + Length(insert_a) + Length(insert_b);
5 daniel-mar 815
 
18 daniel-mar 816
      p := PosEx(toFind, cont, p+1);
817
    end;
5 daniel-mar 818
  end;
22 daniel-mar 819
 
18 daniel-mar 820
begin
821
  {$REGION 'Possibility 1: filename.php:lineno'}
22 daniel-mar 822
  _process(ExtractFileName(GetScrapFile) + ':');
18 daniel-mar 823
  {$ENDREGION}
5 daniel-mar 824
 
18 daniel-mar 825
  {$REGION 'Possibility 2: on line xx'}
22 daniel-mar 826
  _process(ExtractFileName(GetScrapFile) + ' on line ');
18 daniel-mar 827
  {$ENDREGION}
828
 
5 daniel-mar 829
  result := cont;
830
end;
831
 
27 daniel-mar 832
function TForm1.InputRequestCallback: string;
833
begin
834
  result := SynEdit1.Text;
835
end;
836
 
837
procedure TForm1.OutputNotifyCallback(const data: string);
838
begin
839
  TreeView1.FillWithFastPHPData(data);
840
end;
841
 
2 daniel-mar 842
end.