Subversion Repositories fastphp

Rev

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