Subversion Repositories fastphp

Rev

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