Subversion Repositories fastphp

Rev

Rev 22 | Rev 24 | 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
 
4 daniel-mar 3
(*
4
  This program requires
5
  - Microsoft Internet Controls (TWebBrowser)
6
    If you are using Delphi 10.1 Starter Edition, please import the ActiveX TLB
7
    "Microsoft Internet Controls"
8
  - SynEdit
9
    You can obtain SynEdit via Embarcadero GetIt
10
*)
11
 
2 daniel-mar 12
// TODO: localize
13
// TODO: wieso geht copy paste im twebbrowser nicht???
14
// Wieso dauert webbrowser1 erste kompilierung so lange???
5 daniel-mar 15
// TODO: wieso kommt syntax fehler zweimal? einmal stderr einmal stdout?
16
// TODO: Browser titlebar (link preview)
21 daniel-mar 17
// TODO: todo liste
2 daniel-mar 18
 
19
// Future ideas
21 daniel-mar 20
// - code explorer / code insight
2 daniel-mar 21
// - verschiedene php versionen?
22
// - webbrowser1 nur laden, wenn man den tab anwählt?
23
// - doppelklick auf tab soll diesen schließen
5 daniel-mar 24
// - Onlinehelp (www) aufrufen
13 daniel-mar 25
// - Let all colors be adjustable
21 daniel-mar 26
// - code in bildschirmmitte (horizontal)?
2 daniel-mar 27
 
28
interface
29
 
30
uses
31
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
4 daniel-mar 32
  Dialogs, StdCtrls, OleCtrls, ComCtrls, ExtCtrls, ToolWin, IniFiles,
13 daniel-mar 33
  SynEditHighlighter, SynHighlighterPHP, SynEdit, SHDocVw_TLB, FindReplace,
20 daniel-mar 34
  System.Actions, Vcl.ActnList, System.UITypes;
2 daniel-mar 35
 
23 daniel-mar 36
{.$DEFINE OnlineHelp}
37
 
2 daniel-mar 38
type
39
  TForm1 = class(TForm)
40
    PageControl1: TPageControl;
41
    PlaintextTabSheet: TTabSheet;
42
    HtmlTabSheet: TTabSheet;
43
    Memo2: TMemo;
44
    WebBrowser1: TWebBrowser;
45
    Splitter1: TSplitter;
46
    PageControl2: TPageControl;
20 daniel-mar 47
    CodeTabsheet: TTabSheet;
2 daniel-mar 48
    HelpTabsheet: TTabSheet;
49
    WebBrowser2: TWebBrowser;
50
    OpenDialog1: TOpenDialog;
51
    Panel1: TPanel;
52
    OpenDialog3: TOpenDialog;
4 daniel-mar 53
    SynEdit1: TSynEdit;
54
    SynPHPSyn1: TSynPHPSyn;
5 daniel-mar 55
    Panel2: TPanel;
56
    SynEditFocusTimer: TTimer;
57
    Button1: TButton;
58
    Button2: TButton;
59
    Button3: TButton;
13 daniel-mar 60
    Button4: TButton;
61
    Button5: TButton;
62
    Button6: TButton;
63
    ActionList: TActionList;
64
    ActionFind: TAction;
65
    ActionReplace: TAction;
66
    ActionFindNext: TAction;
67
    ActionGoto: TAction;
68
    ActionSave: TAction;
69
    ActionHelp: TAction;
70
    ActionRun: TAction;
71
    ActionESC: TAction;
72
    Button7: TButton;
15 daniel-mar 73
    ActionOpen: TAction;
74
    Button8: TButton;
22 daniel-mar 75
    Button9: TButton;
76
    ActionFindPrev: TAction;
23 daniel-mar 77
    Timer1: TTimer;
78
    ActionSpaceToTab: TAction;
79
    Button11: TButton;
2 daniel-mar 80
    procedure Run(Sender: TObject);
81
    procedure FormShow(Sender: TObject);
82
    procedure FormCreate(Sender: TObject);
83
    procedure FormDestroy(Sender: TObject);
84
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
85
    procedure PageControl2Changing(Sender: TObject; var AllowChange: Boolean);
5 daniel-mar 86
    procedure Memo2DblClick(Sender: TObject);
87
    procedure WebBrowser1BeforeNavigate2(ASender: TObject;
88
      const pDisp: IDispatch; const URL, Flags, TargetFrameName, PostData,
89
      Headers: OleVariant; var Cancel: WordBool);
90
    procedure SynEditFocusTimerTimer(Sender: TObject);
13 daniel-mar 91
    procedure ActionFindExecute(Sender: TObject);
92
    procedure ActionReplaceExecute(Sender: TObject);
93
    procedure ActionFindNextExecute(Sender: TObject);
94
    procedure ActionGotoExecute(Sender: TObject);
95
    procedure ActionSaveExecute(Sender: TObject);
96
    procedure ActionHelpExecute(Sender: TObject);
97
    procedure ActionRunExecute(Sender: TObject);
98
    procedure ActionESCExecute(Sender: TObject);
99
    procedure SynEdit1MouseWheelDown(Sender: TObject; Shift: TShiftState;
100
      MousePos: TPoint; var Handled: Boolean);
101
    procedure SynEdit1MouseWheelUp(Sender: TObject; Shift: TShiftState;
102
      MousePos: TPoint; var Handled: Boolean);
15 daniel-mar 103
    procedure ActionOpenExecute(Sender: TObject);
104
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
17 daniel-mar 105
    procedure Memo2KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
22 daniel-mar 106
    procedure ActionFindPrevExecute(Sender: TObject);
23 daniel-mar 107
    procedure SynEdit1MouseCursor(Sender: TObject;
108
      const aLineCharPos: TBufferCoord; var aCursor: TCursor);
109
    procedure Timer1Timer(Sender: TObject);
110
    procedure ActionSpaceToTabExecute(Sender: TObject);
2 daniel-mar 111
  private
112
    CurSearchTerm: string;
113
    HlpPrevPageIndex: integer;
13 daniel-mar 114
    SrcRep: TFindReplace;
23 daniel-mar 115
    {$IFDEF OnlineHelp}
116
    gOnlineHelpWord: string;
117
    {$ENDIF}
2 daniel-mar 118
    procedure Help;
5 daniel-mar 119
    function MarkUpLineReference(cont: string): string;
2 daniel-mar 120
  protected
121
    ChmIndex: TMemIniFile;
19 daniel-mar 122
    FScrapFile: string;
5 daniel-mar 123
    procedure GotoLineNo(LineNo:integer);
2 daniel-mar 124
    function GetScrapFile: string;
125
  end;
126
 
127
var
128
  Form1: TForm1;
129
 
130
implementation
131
 
132
{$R *.dfm}
133
 
134
uses
23 daniel-mar 135
  Functions, StrUtils, WebBrowserUtils, FastPHPUtils, Math, ShellAPI, RichEdit;
2 daniel-mar 136
 
13 daniel-mar 137
// TODO: FindPrev ?
138
procedure TForm1.ActionFindNextExecute(Sender: TObject);
139
begin
140
  SrcRep.FindNext;
141
end;
142
 
22 daniel-mar 143
procedure TForm1.ActionFindPrevExecute(Sender: TObject);
144
begin
145
  SrcRep.FindPrev;
146
end;
147
 
13 daniel-mar 148
procedure TForm1.ActionGotoExecute(Sender: TObject);
5 daniel-mar 149
var
150
  val: string;
151
  lineno: integer;
152
begin
13 daniel-mar 153
  // TODO: VK_LMENU does not work! only works with AltGr but not Alt
154
  // http://stackoverflow.com/questions/16828250/delphi-xe2-how-to-prevent-the-alt-key-stealing-focus ?
5 daniel-mar 155
 
13 daniel-mar 156
  InputQuery('Go to', 'Line number:', val);
157
  if not TryStrToInt(val, lineno) then
158
  begin
159
    if SynEdit1.CanFocus then SynEdit1.SetFocus;
160
    exit;
161
  end;
162
  GotoLineNo(lineno);
163
end;
5 daniel-mar 164
 
13 daniel-mar 165
procedure TForm1.ActionHelpExecute(Sender: TObject);
166
begin
167
  Help;
168
  if PageControl2.ActivePage = HelpTabsheet then
169
    WebBrowser2.SetFocus
20 daniel-mar 170
  else if PageControl2.ActivePage = CodeTabsheet then
13 daniel-mar 171
    SynEdit1.SetFocus;
172
end;
8 daniel-mar 173
 
15 daniel-mar 174
procedure TForm1.ActionOpenExecute(Sender: TObject);
175
begin
176
  If OpenDialog3.Execute then
177
  begin
178
    ShellExecute(0, 'open', PChar(ParamStr(0)), PChar(OpenDialog3.FileName), '', SW_NORMAL);
179
  end;
180
end;
181
 
13 daniel-mar 182
procedure TForm1.ActionReplaceExecute(Sender: TObject);
183
begin
184
  SrcRep.ReplaceExecute;
185
end;
5 daniel-mar 186
 
13 daniel-mar 187
procedure TForm1.ActionRunExecute(Sender: TObject);
188
begin
189
  Run(Sender);
190
  SynEdit1.SetFocus;
191
end;
5 daniel-mar 192
 
13 daniel-mar 193
procedure TForm1.ActionSaveExecute(Sender: TObject);
194
begin
195
  SynEdit1.Lines.SaveToFile(GetScrapFile);
16 daniel-mar 196
  SynEdit1.Modified := false;
13 daniel-mar 197
end;
198
 
23 daniel-mar 199
procedure TForm1.ActionSpaceToTabExecute(Sender: TObject);
200
 
201
    function SpacesAtBeginning(line: string): integer;
202
    begin
203
      if line.Trim = '' then exit(0);
204
      result := 0;
205
      while line[result+1] = ' ' do
206
      begin
207
        inc(result);
208
      end;
209
    end;
210
 
211
    function GuessIndent(lines: TStrings): integer;
212
      function _Check(indent: integer): boolean;
213
      var
214
        i: integer;
215
      begin
216
        result := true;
217
        for i := 0 to lines.Count-1 do
218
          if SpacesAtBeginning(lines.Strings[i]) mod indent <> 0 then
219
          begin
220
            // ShowMessageFmt('Zeile "%s" nicht durch %d teilbar!', [lines.strings[i], indent]);
221
            exit(false);
222
          end;
223
      end;
224
    var
225
      i: integer;
226
    begin
227
      for i := 8 downto 2 do
228
      begin
229
        if _Check(i) then exit(i);
230
      end;
231
      result := -1;
232
    end;
233
 
234
    procedure SpaceToTab(lines: TStrings; indent: integer);
235
    var
236
      i, spaces: integer;
237
    begin
238
      for i := 0 to lines.Count-1 do
239
      begin
240
        spaces := SpacesAtBeginning(lines.Strings[i]);
241
        lines.Strings[i] := StringOfChar(#9, spaces div indent) + StringOfChar(' ', spaces mod indent) + Copy(lines.Strings[i], spaces+1, Length(lines.Strings[i])-spaces);
242
      end;
243
    end;
244
 
245
    function SpacesAvailable(lines: TStrings): boolean;
246
    var
247
      i, spaces: integer;
248
    begin
249
      for i := 0 to lines.Count-1 do
250
      begin
251
        spaces := SpacesAtBeginning(lines.Strings[i]);
252
        if spaces > 0 then exit(true);
253
      end;
254
      exit(false);
255
    end;
256
 
257
var
258
  val: string;
259
  ind: integer;
260
resourcestring
261
  SNoLinesAvailable = 'No lines with spaces at the beginning available';
262
begin
263
  // TODO: if something is selected, only process the selected part
264
 
265
  if not SpacesAvailable(SynEdit1.Lines) then
266
  begin
267
    ShowMessage(SNoLinesAvailable);
268
    exit;
269
  end;
270
 
271
  ind := GuessIndent(SynEdit1.Lines);
272
  if ind <> -1 then val := IntToStr(ind);
273
 
274
  InputQuery('Spaces to tabs', 'Indent:', val); // TODO: handle CANCEL correctly...
275
  if TryStrToInt(val.Trim, ind) then
276
  begin
277
    if ind = 0 then exit;
278
    SpaceToTab(SynEdit1.Lines, ind);
279
  end;
280
 
281
  if SynEdit1.CanFocus then SynEdit1.SetFocus;
282
end;
283
 
13 daniel-mar 284
procedure TForm1.ActionESCExecute(Sender: TObject);
285
begin
286
  if (HlpPrevPageIndex <> -1) and (PageControl2.ActivePage = HelpTabSheet) and
287
     (HelpTabsheet.TabVisible) then
288
  begin
289
    PageControl2.ActivePageIndex := HlpPrevPageIndex;
290
    HelpTabsheet.TabVisible := false;
2 daniel-mar 291
  end;
13 daniel-mar 292
 
293
  // Dirty hack...
22 daniel-mar 294
  SrcRep.CloseDialogs;
2 daniel-mar 295
end;
296
 
13 daniel-mar 297
procedure TForm1.ActionFindExecute(Sender: TObject);
298
begin
299
  SrcRep.FindExecute;
300
end;
301
 
16 daniel-mar 302
var
303
  firstTimeBrowserLoad: boolean = true;
2 daniel-mar 304
procedure TForm1.Run(Sender: TObject);
16 daniel-mar 305
var
306
  bakTS: TTabSheet;
2 daniel-mar 307
begin
5 daniel-mar 308
  memo2.Lines.Text := '';
16 daniel-mar 309
 
310
  if firstTimeBrowserLoad then
311
  begin
312
    bakTS := PageControl1.ActivePage;
313
    try
314
      PageControl1.ActivePage := HtmlTabSheet; // Required for the first time, otherwise, WebBrowser1.Clear will hang
315
      Webbrowser1.Clear;
316
    finally
317
      PageControl1.ActivePage := bakTS;
318
    end;
319
    firstTimeBrowserLoad := false;
320
  end
321
  else
322
    Webbrowser1.Clear;
323
 
5 daniel-mar 324
  Screen.Cursor := crHourGlass;
325
  Application.ProcessMessages;
326
 
327
  try
328
    SynEdit1.Lines.SaveToFile(GetScrapFile);
329
 
8 daniel-mar 330
    memo2.Lines.Text := RunPHPScript(GetScrapFile);
5 daniel-mar 331
 
8 daniel-mar 332
    Webbrowser1.LoadHTML(MarkUpLineReference(memo2.Lines.Text), GetScrapFile);
5 daniel-mar 333
 
334
    if IsTextHTML(memo2.lines.text) then
335
      PageControl1.ActivePage := HtmlTabSheet
336
    else
337
      PageControl1.ActivePage := PlaintextTabSheet;
338
  finally
339
    Screen.Cursor := crDefault;
2 daniel-mar 340
  end;
5 daniel-mar 341
end;
2 daniel-mar 342
 
23 daniel-mar 343
procedure TForm1.SynEdit1MouseCursor(Sender: TObject; const aLineCharPos: TBufferCoord; var aCursor: TCursor);
344
{$IFDEF OnlineHelp}
345
var
346
  Line: Integer;
347
  Column: Integer;
348
  word: string;
349
begin
350
  Line  := aLineCharPos.Line-1;
351
  Column := aLineCharPos.Char-1;
352
  word := GetWordUnderPos(TSynEdit(Sender), Line, Column);
353
  if word <> gOnlineHelpWord then
354
  begin
355
    gOnlineHelpWord := word;
356
    Timer1.Enabled := false;
357
    Timer1.Enabled := true;
358
  end;
359
{$ELSE}
360
begin
361
{$ENDIF}
362
end;
363
 
13 daniel-mar 364
procedure TForm1.SynEdit1MouseWheelDown(Sender: TObject; Shift: TShiftState;
365
  MousePos: TPoint; var Handled: Boolean);
366
begin
367
  if ssCtrl in Shift then
368
  begin
369
    SynEdit1.Font.Size := Max(SynEdit1.Font.Size - 1, 5);
23 daniel-mar 370
    Handled := true;
371
  end
372
  else Handled := false;
13 daniel-mar 373
end;
374
 
375
procedure TForm1.SynEdit1MouseWheelUp(Sender: TObject; Shift: TShiftState;
376
  MousePos: TPoint; var Handled: Boolean);
377
begin
378
  if ssCtrl in Shift then
379
  begin
380
    SynEdit1.Font.Size := SynEdit1.Font.Size + 1;
23 daniel-mar 381
    Handled := true;
382
  end
383
  else Handled := false;
13 daniel-mar 384
end;
385
 
5 daniel-mar 386
procedure TForm1.SynEditFocusTimerTimer(Sender: TObject);
387
begin
388
  SynEditFocusTimer.Enabled := false;
389
  Button1.SetFocus; // Workaround for weird bug... This (and the timer) is necessary to get the focus to SynEdit1
390
  SynEdit1.SetFocus;
391
end;
2 daniel-mar 392
 
23 daniel-mar 393
procedure TForm1.Timer1Timer(Sender: TObject);
394
begin
395
  {$IFDEF OnlineHelp}
396
  Timer1.Enabled := false;
397
 
398
  // TODO: Insert a small online help hint
399
  //Caption := gOnlineHelpWord;
400
  {$ENDIF}
401
end;
402
 
5 daniel-mar 403
procedure TForm1.WebBrowser1BeforeNavigate2(ASender: TObject;
404
  const pDisp: IDispatch; const URL, Flags, TargetFrameName, PostData,
405
  Headers: OleVariant; var Cancel: WordBool);
406
var
8 daniel-mar 407
  s, myURL: string;
5 daniel-mar 408
  lineno: integer;
7 daniel-mar 409
  p: integer;
5 daniel-mar 410
begin
7 daniel-mar 411
  {$REGION 'Line number references (PHP errors and warnings)'}
8 daniel-mar 412
  if Copy(URL, 1, length(FASTPHP_GOTO_URI_PREFIX)) = FASTPHP_GOTO_URI_PREFIX then
5 daniel-mar 413
  begin
414
    try
8 daniel-mar 415
      s := copy(URL, length(FASTPHP_GOTO_URI_PREFIX)+1, 99);
5 daniel-mar 416
      if not TryStrToInt(s, lineno) then exit;
417
      GotoLineNo(lineno);
418
      SynEditFocusTimer.Enabled := true;
419
    finally
420
      Cancel := true;
421
    end;
8 daniel-mar 422
    Exit;
5 daniel-mar 423
  end;
7 daniel-mar 424
  {$ENDREGION}
425
 
8 daniel-mar 426
  {$REGION 'Intelligent browser (executes PHP scripts)'}
7 daniel-mar 427
  if URL <> 'about:blank' then
428
  begin
429
    myUrl := URL;
430
 
8 daniel-mar 431
    p := Pos('?', myUrl);
432
    if p >= 1 then myURL := copy(myURL, 1, p-1);
7 daniel-mar 433
 
8 daniel-mar 434
    // TODO: myURL urldecode
435
    // TODO: maybe we could even open that file in the editor!
7 daniel-mar 436
 
8 daniel-mar 437
    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 438
    begin
8 daniel-mar 439
      WebBrowser1.LoadHTML(GetDosOutput('"'+GetPHPExe+'" "'+myURL+'"', ExtractFileDir(Application.ExeName)), myUrl);
7 daniel-mar 440
      Cancel := true;
441
    end;
442
  end;
443
  {$ENDREGION}
5 daniel-mar 444
end;
2 daniel-mar 445
 
446
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
447
begin
13 daniel-mar 448
  FastPHPConfig.WriteInteger('User', 'FontSize', SynEdit1.Font.Size);
2 daniel-mar 449
end;
450
 
15 daniel-mar 451
procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
452
var
453
  r: integer;
454
begin
455
  if SynEdit1.Modified then
456
  begin
457
    if ParamStr(1) <> '' then
458
    begin
459
      r := MessageDlg('Do you want to save?', mtConfirmation, mbYesNoCancel, 0);
460
      if r = mrCancel then
461
      begin
462
        CanClose := false;
463
        Exit;
464
      end
465
      else if r = mrYes then
466
      begin
467
        SynEdit1.Lines.SaveToFile(GetScrapFile);
468
        CanClose := true;
469
      end;
470
    end
471
    else
472
    begin
473
      SynEdit1.Lines.SaveToFile(GetScrapFile);
474
      CanClose := true;
475
    end;
476
  end;
477
end;
478
 
2 daniel-mar 479
procedure TForm1.FormCreate(Sender: TObject);
480
begin
481
  HlpPrevPageIndex := -1;
482
  CurSearchTerm := '';
13 daniel-mar 483
  Caption := Caption + ' - ' + GetScrapFile;
484
  SrcRep := TFindReplace.Create(self);
485
  SrcRep.Editor := SynEdit1;
2 daniel-mar 486
end;
487
 
488
procedure TForm1.FormDestroy(Sender: TObject);
489
begin
490
  if Assigned(ChmIndex) then
491
  begin
492
    FreeAndNil(ChmIndex);
493
  end;
13 daniel-mar 494
  FreeAndNil(SrcRep);
2 daniel-mar 495
end;
496
 
497
procedure TForm1.FormShow(Sender: TObject);
498
var
499
  ScrapFile: string;
500
begin
501
  ScrapFile := GetScrapFile;
502
  if ScrapFile = '' then
503
  begin
10 daniel-mar 504
    Application.Terminate; // Close;
2 daniel-mar 505
    exit;
506
  end;
15 daniel-mar 507
  if FileExists(ScrapFile) then
508
    SynEdit1.Lines.LoadFromFile(ScrapFile)
509
  else
510
    SynEdit1.Lines.Clear;
2 daniel-mar 511
 
512
  PageControl1.ActivePage := PlaintextTabSheet;
513
 
20 daniel-mar 514
  PageControl2.ActivePage := CodeTabsheet;
2 daniel-mar 515
  HelpTabsheet.TabVisible := false;
5 daniel-mar 516
 
13 daniel-mar 517
  SynEdit1.Font.Size := FastPHPConfig.ReadInteger('User', 'FontSize', SynEdit1.Font.Size);
5 daniel-mar 518
  SynEdit1.SetFocus;
2 daniel-mar 519
end;
520
 
521
function TForm1.GetScrapFile: string;
522
begin
19 daniel-mar 523
  if FScrapFile <> '' then exit(FScrapFile);
524
 
15 daniel-mar 525
  if ParamStr(1) <> '' then
13 daniel-mar 526
    result := ParamStr(1)
527
  else
528
    result := FastPHPConfig.ReadString('Paths', 'ScrapFile', '');
2 daniel-mar 529
  if not FileExists(result) then
530
  begin
19 daniel-mar 531
    repeat
532
      if not OpenDialog3.Execute then
533
      begin
534
        Application.Terminate;
535
        exit('');
536
      end;
2 daniel-mar 537
 
19 daniel-mar 538
      if not DirectoryExists(ExtractFilePath(OpenDialog3.FileName)) then
539
      begin
540
        ShowMessage('Path does not exist! Please try again.');
541
      end
542
      else
543
      begin
544
        result := OpenDialog3.FileName;
545
      end;
546
    until result <> '';
2 daniel-mar 547
 
4 daniel-mar 548
    SynEdit1.Lines.Clear;
549
    SynEdit1.Lines.SaveToFile(result);
2 daniel-mar 550
 
551
    FastPHPConfig.WriteString('Paths', 'ScrapFile', result);
19 daniel-mar 552
    FScrapFile := result;
2 daniel-mar 553
  end;
554
end;
555
 
556
procedure TForm1.Help;
557
var
19 daniel-mar 558
  IndexFile, chmFile, w, OriginalWord, url: string;
2 daniel-mar 559
  internalHtmlFile: string;
560
begin
561
  if not Assigned(ChmIndex) then
562
  begin
563
    IndexFile := FastPHPConfig.ReadString('Paths', 'HelpIndex', '');
564
    IndexFile := ChangeFileExt(IndexFile, '.ini'); // Just to be sure. Maybe someone wrote manually the ".chm" file in there
565
    if FileExists(IndexFile) then
566
    begin
567
      ChmIndex := TMemIniFile.Create(IndexFile);
568
    end;
569
  end;
570
 
571
  if Assigned(ChmIndex) then
572
  begin
573
    IndexFile := FastPHPConfig.ReadString('Paths', 'HelpIndex', '');
574
    // We don't check if IndexFile still exists. It is not important since we have ChmIndex pre-loaded in memory
575
 
576
    chmFile := ChangeFileExt(IndexFile, '.chm');
577
    if not FileExists(chmFile) then
578
    begin
579
      FreeAndNil(ChmIndex);
580
    end;
581
  end;
582
 
583
  if not Assigned(ChmIndex) then
584
  begin
585
    if not OpenDialog1.Execute then exit;
586
 
587
    chmFile := OpenDialog1.FileName;
588
    if not FileExists(chmFile) then exit;
589
 
590
    IndexFile := ChangeFileExt(chmFile, '.ini');
591
 
592
    if not FileExists(IndexFile) then
593
    begin
594
      Panel1.Align := alClient;
595
      Panel1.Visible := true;
596
      Panel1.BringToFront;
597
      Screen.Cursor := crHourGlass;
598
      Application.ProcessMessages;
599
      try
600
        if not ParseCHM(chmFile) then
601
        begin
602
          ShowMessage('The CHM file is not a valid PHP documentation. Cannot use help.');
603
          exit;
604
        end;
605
      finally
606
        Screen.Cursor := crDefault;
607
        Panel1.Visible := false;
608
      end;
609
 
610
      if not FileExists(IndexFile) then
611
      begin
612
        ShowMessage('Unknown error. Cannot use help.');
613
        exit;
614
      end;
615
    end;
616
 
617
    FastPHPConfig.WriteString('Paths', 'HelpIndex', IndexFile);
618
    FastPHPConfig.UpdateFile;
619
 
620
    ChmIndex := TMemIniFile.Create(IndexFile);
621
  end;
622
 
4 daniel-mar 623
  w := GetWordUnderCaret(SynEdit1);
2 daniel-mar 624
  if w = '' then exit;
8 daniel-mar 625
  if CharInSet(w[1], ['0'..'9']) then exit;
19 daniel-mar 626
 
627
  Originalword := w;
628
//  w := StringReplace(w, '_', '-', [rfReplaceAll]);
2 daniel-mar 629
  w := LowerCase(w);
630
  CurSearchTerm := w;
631
 
632
  internalHtmlFile := ChmIndex.ReadString('_HelpWords_', CurSearchTerm, '');
633
  if internalHtmlFile = '' then
634
  begin
635
    HelpTabsheet.TabVisible := false;
636
    HlpPrevPageIndex := -1;
19 daniel-mar 637
    ShowMessageFmt('No help for "%s" available', [Originalword]);
2 daniel-mar 638
    Exit;
639
  end;
640
 
641
  url := 'mk:@MSITStore:'+ChmFile+'::'+internalHtmlFile;
642
 
643
  HlpPrevPageIndex := PageControl2.ActivePageIndex; // Return by pressing ESC
644
  HelpTabsheet.TabVisible := true;
645
  PageControl2.ActivePage := HelpTabsheet;
8 daniel-mar 646
  WebBrowser2.Navigate(url);
647
  WebBrowser2.Wait;
2 daniel-mar 648
end;
649
 
5 daniel-mar 650
procedure TForm1.GotoLineNo(LineNo:integer);
651
var
652
  line: string;
653
  i: integer;
2 daniel-mar 654
begin
5 daniel-mar 655
  SynEdit1.GotoLineAndCenter(LineNo);
656
 
657
  // Skip indent
658
  line := SynEdit1.Lines[SynEdit1.CaretY];
659
  for i := 1 to Length(line) do
660
  begin
8 daniel-mar 661
    if not CharInSet(line[i], [' ', #9]) then
5 daniel-mar 662
    begin
663
      SynEdit1.CaretX := i-1;
664
      break;
665
    end;
666
  end;
667
 
20 daniel-mar 668
  PageControl2.ActivePage := CodeTabsheet;
5 daniel-mar 669
  if SynEdit1.CanFocus then SynEdit1.SetFocus;
2 daniel-mar 670
end;
671
 
8 daniel-mar 672
procedure TForm1.PageControl2Changing(Sender: TObject;
673
  var AllowChange: Boolean);
674
begin
675
  if PageControl2.ActivePage = HelpTabsheet then
676
    HlpPrevPageIndex := -1
677
  else
678
    HlpPrevPageIndex := PageControl2.ActivePageIndex;
679
 
680
  AllowChange := true;
681
end;
682
 
5 daniel-mar 683
procedure TForm1.Memo2DblClick(Sender: TObject);
684
var
22 daniel-mar 685
  line: string;
686
 
687
  procedure _process(toFind: string);
688
  var
689
    p, lineno: integer;
690
  begin
691
    if FileSystemCaseSensitive then
692
      p := Pos(toFind, line)
693
    else
694
      p := Pos(toFind.ToLower, line.ToLower);
695
    if p <> 0 then
696
    begin
697
      line := copy(line, p+length(toFind), 99);
698
      if not TryStrToInt(line, lineno) then exit;
699
      GotoLineNo(lineno);
700
    end;
701
  end;
702
 
5 daniel-mar 703
begin
704
  line := memo2.Lines.Strings[Memo2.CaretPos.Y];
16 daniel-mar 705
 
18 daniel-mar 706
  {$REGION 'Possibility 1: filename.php:lineno'}
22 daniel-mar 707
  _process(ExtractFileName(GetScrapFile) + ':');
18 daniel-mar 708
  {$ENDREGION}
16 daniel-mar 709
 
18 daniel-mar 710
  {$REGION 'Possibility 2: on line xx'}
22 daniel-mar 711
  _process(ExtractFileName(GetScrapFile) + ' on line ');
18 daniel-mar 712
  {$ENDREGION}
5 daniel-mar 713
end;
714
 
17 daniel-mar 715
procedure TForm1.Memo2KeyDown(Sender: TObject; var Key: Word;
716
  Shift: TShiftState);
717
begin
718
  if ((ssCtrl in Shift) and (Key = 65)) then TMemo(Sender).SelectAll;
719
end;
720
 
5 daniel-mar 721
function TForm1.MarkUpLineReference(cont: string): string;
18 daniel-mar 722
 
723
  procedure _process(toFind: string);
22 daniel-mar 724
  var
725
    p, a, b: integer;
726
    num: integer;
727
    insert_a, insert_b: string;
5 daniel-mar 728
  begin
22 daniel-mar 729
    if FileSystemCaseSensitive then
730
      p := Pos(toFind, cont)
731
    else
732
      p := Pos(toFind.ToLower, cont.ToLower);
18 daniel-mar 733
    while p >= 1 do
5 daniel-mar 734
    begin
22 daniel-mar 735
      a := p;
736
      b := p + length(toFind);
18 daniel-mar 737
      num := 0;
738
      while CharInSet(cont[b], ['0'..'9']) do
739
      begin
740
        num := num*10 + StrToInt(cont[b]);
741
        inc(b);
742
      end;
5 daniel-mar 743
 
18 daniel-mar 744
      insert_b := '</a>';
22 daniel-mar 745
      insert_a := '<a href="' + FASTPHP_GOTO_URI_PREFIX + IntToStr(num) + '">';
5 daniel-mar 746
 
18 daniel-mar 747
      insert(insert_b, cont, b);
748
      insert(insert_a, cont, a);
5 daniel-mar 749
 
18 daniel-mar 750
      p := b + Length(insert_a) + Length(insert_b);
5 daniel-mar 751
 
18 daniel-mar 752
      p := PosEx(toFind, cont, p+1);
753
    end;
5 daniel-mar 754
  end;
22 daniel-mar 755
 
18 daniel-mar 756
begin
757
  {$REGION 'Possibility 1: filename.php:lineno'}
22 daniel-mar 758
  _process(ExtractFileName(GetScrapFile) + ':');
18 daniel-mar 759
  {$ENDREGION}
5 daniel-mar 760
 
18 daniel-mar 761
  {$REGION 'Possibility 2: on line xx'}
22 daniel-mar 762
  _process(ExtractFileName(GetScrapFile) + ' on line ');
18 daniel-mar 763
  {$ENDREGION}
764
 
5 daniel-mar 765
  result := cont;
766
end;
767
 
2 daniel-mar 768
end.