Subversion Repositories fastphp

Rev

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