Check-in [12f8f07a24]
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Updated trunk to match with v1.3
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:12f8f07a24755a81df7b159e9c1cbc4d3a327839
User & Date: Martijn 2013-03-03 13:48:08
Context
2013-07-14
09:44
Release of v1.3.1.0 check-in: 4159519e53 user: Martijn tags: trunk, release, src-1.3.1.0
2013-07-12
20:02
Moved GetSettings to the plugin class. Read eventual version override from settings file (but it doesn't seem to work yet). check-in: ef8f24d6c5 user: Martijn tags: version-override
2013-03-19
20:26
Added menu command 'Check for update'. Currently only displays the plugin's file version, and compares it to the version posted online. check-in: 1a9f9f3c11 user: Martijn tags: updatecheck
2013-03-03
13:48
Updated trunk to match with v1.3 check-in: 12f8f07a24 user: Martijn tags: trunk
13:45
Oops... Forgot to include the units used to run an external process, and capture its output. Closed-Leaf check-in: a62f4ed133 user: Martijn tags: release, v1.3, src-v1.3.0.0
2013-01-29
21:47
Updated trunk to match v1.2 check-in: 0d5c7fa989 user: Martijn tags: trunk
Changes

Changes to src/F_PreviewHTML.dfm.

37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
..
60
61
62
63
64
65
66









67
68
69
70
71
72
73
..
91
92
93
94
95
96
97

98
99
100
101
102
103
104
    object btnClose: TButton
      Left = 421
      Top = 6
      Width = 75
      Height = 25
      Anchors = [akRight, akBottom]
      Caption = 'Close'
      TabOrder = 1
      OnClick = btnCloseClick
    end
    object sbrIE: TStatusBar
      Left = 89
      Top = 10
      Width = 295
      Height = 19
      Align = alNone
      Anchors = [akLeft, akRight, akBottom]
      Panels = <>
      SimplePanel = True
    end
    object btnAbout: TButton
................................................................................
      Width = 25
      Height = 25
      Hint = 'About|About this plugin'
      Anchors = [akTop, akRight]
      Caption = '?'
      TabOrder = 2
      OnClick = btnAboutClick









    end
  end
  object pnlPreview: TPanel
    Left = 0
    Top = 0
    Width = 504
    Height = 379
................................................................................
        Height = 379
        TabStop = False
        Align = alClient
        TabOrder = 0
        OnStatusTextChange = wbIEStatusTextChange
        OnTitleChange = wbIETitleChange
        OnBeforeNavigate2 = wbIEBeforeNavigate2

        OnStatusBar = wbIEStatusBar
        OnNewWindow3 = wbIENewWindow3
        ExplicitLeft = 8
        ExplicitTop = 8
        ExplicitWidth = 288
        ExplicitHeight = 159
        ControlData = {







|



|

|







 







>
>
>
>
>
>
>
>
>







 







>







37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
..
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
...
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
    object btnClose: TButton
      Left = 421
      Top = 6
      Width = 75
      Height = 25
      Anchors = [akRight, akBottom]
      Caption = 'Close'
      TabOrder = 4
      OnClick = btnCloseClick
    end
    object sbrIE: TStatusBar
      Left = 143
      Top = 10
      Width = 241
      Height = 19
      Align = alNone
      Anchors = [akLeft, akRight, akBottom]
      Panels = <>
      SimplePanel = True
    end
    object btnAbout: TButton
................................................................................
      Width = 25
      Height = 25
      Hint = 'About|About this plugin'
      Anchors = [akTop, akRight]
      Caption = '?'
      TabOrder = 2
      OnClick = btnAboutClick
    end
    object chkFreeze: TCheckBox
      Left = 89
      Top = 10
      Width = 48
      Height = 17
      Caption = '&Freeze'
      TabOrder = 1
      OnClick = chkFreezeClick
    end
  end
  object pnlPreview: TPanel
    Left = 0
    Top = 0
    Width = 504
    Height = 379
................................................................................
        Height = 379
        TabStop = False
        Align = alClient
        TabOrder = 0
        OnStatusTextChange = wbIEStatusTextChange
        OnTitleChange = wbIETitleChange
        OnBeforeNavigate2 = wbIEBeforeNavigate2
        OnDocumentComplete = wbIEDocumentComplete
        OnStatusBar = wbIEStatusBar
        OnNewWindow3 = wbIENewWindow3
        ExplicitLeft = 8
        ExplicitTop = 8
        ExplicitWidth = 288
        ExplicitHeight = 159
        ControlData = {

Changes to src/F_PreviewHTML.pas.

2
3
4
5
6
7
8
9

10
11
12
13
14
15
16
17
18
19
20
21

22
23
24
25
26
27
28
..
34
35
36
37
38
39
40


41
42
43
44



45
46
47
48
49
50


51


52
53
54
55

56
57
58
59
60



61
62
63
64
65
66
67
68












69
70
71
72
73
74
75
..
83
84
85
86
87
88
89

90
91
92
93
94
95
96
...
109
110
111
112
113
114
115
116
117
118
119
120
121



122





123
124
125
126
127
128
129
...
130
131
132
133
134
135
136


137
138
139
140
141
142
143
144
145
146
147
148
149
150
151


152
153





154
155
156
157
158


































159
160
161
162
163
164
165
166
167
168
169
170
171
172
173

174
175
176
177
178
179
180
181
182






183
184
185
186







187
188
189
190

191
192
193
194
195
196
197
198
199
200
201
202


203
204
205
206
207
208
209
...
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
...
263
264
265
266
267
268
269





















































































































































270
271
272
273
274
275
276
...
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429















430
431
432
433
434
435
436
...
450
451
452
453
454
455
456
457


458
459
460
461
462
463
464
465
466
467

////////////////////////////////////////////////////////////////////////////////////////////////////
interface

uses
  Windows, Messages, SysUtils, Classes, Variants, Graphics, Controls, Forms,
  Dialogs, StdCtrls, SHDocVw, OleCtrls, ComCtrls, ExtCtrls, IniFiles,
  NppPlugin, NppDockingForms;


type
  TfrmHTMLPreview = class(TNppDockingForm)
    wbIE: TWebBrowser;
    pnlButtons: TPanel;
    btnRefresh: TButton;
    btnClose: TButton;
    sbrIE: TStatusBar;
    pnlPreview: TPanel;
    pnlHTML: TPanel;
    btnAbout: TButton;
    tmrAutorefresh: TTimer;

    procedure btnRefreshClick(Sender: TObject);
    procedure btnCloseClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormKeyPress(Sender: TObject; var Key: Char);
    procedure FormHide(Sender: TObject);
    procedure FormFloat(Sender: TObject);
    procedure FormDock(Sender: TObject);
................................................................................
      dwFlags: Cardinal; const bstrUrlContext, bstrUrl: WideString);
    procedure wbIEStatusTextChange(ASender: TObject; const Text: WideString);
    procedure wbIEStatusBar(ASender: TObject; StatusBar: WordBool);
    procedure btnCloseStatusbarClick(Sender: TObject);
    procedure btnAboutClick(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure tmrAutorefreshTimer(Sender: TObject);


  private
    { Private declarations }
    FBufferID: NativeInt;
    FScrollPositions: TStringList;




    function  GetSettings(const Name: string = 'Settings.ini'): TIniFile;

    procedure SaveScrollPos;
    procedure RestoreScrollPos(const BufferID: NativeInt);



    function TransformXMLToHTML(const XML: WideString): string;


  public
    { Public declarations }
    procedure ResetTimer;
    procedure ForgetBuffer(const BufferID: NativeInt);

  end;

var
  frmHTMLPreview: TfrmHTMLPreview;




////////////////////////////////////////////////////////////////////////////////////////////////////
implementation
uses
  ShellAPI, ComObj, StrUtils, IOUtils,
  RegExpr,
  WebBrowser, SciSupport, U_Npp_PreviewHTML, MSHTML;

{$R *.dfm}













{ ================================================================================================ }

{ ------------------------------------------------------------------------------------------------ }
procedure TfrmHTMLPreview.FormCreate(Sender: TObject);
begin
  FScrollPositions := TStringList.Create;
................................................................................
    tmrAutorefresh.Interval := ReadInteger('Autorefresh', 'Interval', tmrAutorefresh.Interval);
  end;
end {TfrmHTMLPreview.FormCreate};
{ ------------------------------------------------------------------------------------------------ }
procedure TfrmHTMLPreview.FormDestroy(Sender: TObject);
begin
  FreeAndNil(FScrollPositions);

  inherited;
end {TfrmHTMLPreview.FormDestroy};


{ ------------------------------------------------------------------------------------------------ }
procedure TfrmHTMLPreview.btnCloseStatusbarClick(Sender: TObject);
begin
................................................................................
var
  View: Integer;
  BufferID: Integer;
  hScintilla: THandle;
  Lexer: NativeInt;
  IsHTML, IsXML, IsCustom: Boolean;
  Size: WPARAM;
  Filename: nppString;
  Content: UTF8String;
  HTML: string;
  HeadStart: Integer;
  FilterName: string;
begin



  try





    SaveScrollPos;

    SendMessage(Self.Npp.NppData.NppHandle, NPPM_GETCURRENTSCINTILLA, 0, LPARAM(@View));
    if View = 0 then begin
      hScintilla := Self.Npp.NppData.ScintillaMainHandle;
    end else begin
      hScintilla := Self.Npp.NppData.ScintillaSecondHandle;
................................................................................
    end;
    BufferID := SendMessage(Self.Npp.NppData.NppHandle, NPPM_GETCURRENTBUFFERID, 0, 0);

    Lexer := SendMessage(hScintilla, SCI_GETLEXER, 0, 0);
    IsHTML := (Lexer = SCLEX_HTML);
    IsXML := (Lexer = SCLEX_XML);



    {--- MCO 22-01-2013: determine whether the current document matches a custom filter ---}
    FilterName := ''; // DetermineCustomFilter;
    IsCustom := Length(FilterName) > 0;

    {$MESSAGE HINT 'TODO: Find a way to communicate why there is no preview, depending on the situation — MCO 22-01-2013'}

    if IsXML or IsHTML or IsCustom then begin
      Size := SendMessage(hScintilla, SCI_GETTEXT, 0, 0);
      SetLength(Content, Size);
      SendMessage(hScintilla, SCI_GETTEXT, Size, LPARAM(PAnsiChar(Content)));
      Content := UTF8String(PAnsiChar(Content));
      HTML := string(Content);
    end;

    if IsCustom then begin


      HTML := ''; // ExecuteCustomFilter(FilterName, HTML);
      IsHTML := Length(HTML) > 0;





    end else if IsXML then begin
      HTML := TransformXMLToHTML(HTML);
      IsHTML := Length(HTML) > 0;
    end;



































    pnlHTML.Visible := IsHTML;
    sbrIE.Visible := IsHTML and (Length(sbrIE.SimpleText) > 0);
    if IsHTML then begin
      Size := SendMessage(Self.Npp.NppData.NppHandle, NPPM_GETFULLPATHFROMBUFFERID, BufferID, LPARAM(nil));
      SetLength(Filename, Size);
      SetLength(Filename, SendMessage(Self.Npp.NppData.NppHandle, NPPM_GETFULLPATHFROMBUFFERID, BufferID, LPARAM(nppPChar(Filename))));

      if (Pos('<base ', HTML) = 0) and FileExists(Filename) then begin
        HeadStart := Pos('<head>', HTML);
        if HeadStart > 0 then
          Inc(HeadStart, 6)
        else
          HeadStart := 1;
        Insert('<base href="' + Filename + '" />', HTML, HeadStart);
      end;

      wbIE.LoadDocFromString(HTML);

      if wbIE.GetDocument <> nil then
        self.UpdateDisplayInfo(wbIE.GetDocument.title)
      else
        self.UpdateDisplayInfo('');

      {--- 2013-01-26 Martijn: the WebBrowser control has a tendency to steal the focus. We'll let
                                the editor take it back. ---}






      SendMessage(hScintilla, SCI_GRABFOCUS, 0, 0);
    end else begin
      self.UpdateDisplayInfo('');
    end;








    RestoreScrollPos(BufferID);
  except
    on E: Exception do begin

      sbrIE.SimpleText := E.Message;
      sbrIE.Visible := True;
    end;
  end;
end {TfrmHTMLPreview.btnRefreshClick};

{ ------------------------------------------------------------------------------------------------ }
procedure TfrmHTMLPreview.SaveScrollPos;
var
  Index, ScrollTop, ScrollLeft: Integer;
  docEl: IHTMLElement2;
begin


  if FBufferID = -1 then
    Exit;

  if Assigned(wbIE.Document) and Assigned((wbIE.Document as IHTMLDocument3).documentElement) then begin
    docEl := (wbIE.Document as IHTMLDocument3).documentElement AS IHTMLElement2;
    ScrollTop := docEl.scrollTop;
    ScrollLeft := docEl.scrollLeft;
................................................................................
  else
    FScrollPositions[Index] := IntToStr(ScrollTop) + '=' + IntToStr(ScrollLeft);
end {TfrmHTMLPreview.SaveScrollPos};

{ ------------------------------------------------------------------------------------------------ }
procedure TfrmHTMLPreview.RestoreScrollPos(const BufferID: NativeInt);
var
  Index, ScrollTop, ScrollLeft: Integer;
  docEl: IHTMLElement2;
begin
  {--- MCO 22-01-2013: Look up this buffer's scroll position; if we know one, wait for the page
                          to finish loading, then restore the scroll position. ---}
  Index := FScrollPositions.IndexOfObject(TObject(BufferID));
  if Index > -1 then begin
    ScrollTop := StrToInt(FScrollPositions.Names[Index]);
    ScrollLeft := StrToInt(FScrollPositions.ValueFromIndex[Index]);
    if ScrollTop <> -1 then begin
      {$MESSAGE HINT 'TODO: This would be better if done in the browsercontrol's DocumentComplete event,
                            so as to prevent blocking Notepad++ — MCO 22-01-2013'}
      while not wbIE.ReadyState in [READYSTATE_INTERACTIVE, READYSTATE_COMPLETE] do begin
        Forms.Application.ProcessMessages;
        Sleep(0);
      end;
      if Assigned(wbIE.Document) and Assigned((wbIE.Document as IHTMLDocument3).documentElement) then begin
        docEl := (wbIE.Document as IHTMLDocument3).documentElement as IHTMLElement2;
        docEl.scrollTop := ScrollTop;
        docEl.scrollLeft := ScrollLeft;
      end;
    end;
  end;
  FBufferID := BufferID;
end {TfrmHTMLPreview.RestoreScrollPos};

{ ------------------------------------------------------------------------------------------------ }
procedure TfrmHTMLPreview.ForgetBuffer(const BufferID: NativeInt);
................................................................................

{ ------------------------------------------------------------------------------------------------ }
procedure TfrmHTMLPreview.ResetTimer;
begin
  tmrAutorefresh.Enabled := False;
  tmrAutorefresh.Enabled := True;
end {TfrmHTMLPreview.ResetTimer};






















































































































































{ ------------------------------------------------------------------------------------------------ }
procedure TfrmHTMLPreview.btnAboutClick(Sender: TObject);
begin
  (npp as TNppPluginPreviewHTML).CommandShowAbout;
end {TfrmHTMLPreview.btnAboutClick};

................................................................................

{ ------------------------------------------------------------------------------------------------ }
procedure TfrmHTMLPreview.wbIEBeforeNavigate2(ASender: TObject; const pDisp: IDispatch; const URL,
  Flags, TargetFrameName, PostData, Headers: OleVariant; var Cancel: WordBool);
var
  Handle: HWND;
begin
  inherited;
  if not SameText(URL, 'about:blank') and not StartsText('javascript:', URL) then begin
    if Assigned(Npp) then
      Handle := Npp.NppData.NppHandle
    else
      Handle := 0;
    ShellExecute(Handle, nil, PChar(VarToStr(URL)), nil, nil, SW_SHOWDEFAULT);
    Cancel := True;
  end;
end;
















{ ------------------------------------------------------------------------------------------------ }
procedure TfrmHTMLPreview.wbIENewWindow3(ASender: TObject; var ppDisp: IDispatch;
  var Cancel: WordBool; dwFlags: Cardinal; const bstrUrlContext, bstrUrl: WideString);
var
  Handle: HWND;
begin
................................................................................
  sbrIE.Visible := StatusBar;
end;

{ ------------------------------------------------------------------------------------------------ }
procedure TfrmHTMLPreview.wbIEStatusTextChange(ASender: TObject; const Text: WideString);
begin
  sbrIE.SimpleText := Text;
  sbrIE.Visible := Length(Text) > 0;


end;

{ ------------------------------------------------------------------------------------------------ }
procedure TfrmHTMLPreview.wbIETitleChange(ASender: TObject; const Text: WideString);
begin
  inherited;
  self.UpdateDisplayInfo(StringReplace(Text, 'about:blank', '', [rfReplaceAll]));
end;

end.







|
>












>







 







>
>




>
>
>






>
>
|
>
>




>





>
>
>



|

|


>
>
>
>
>
>
>
>
>
>
>
>







 







>







 







<


<


>
>
>

>
>
>
>
>







 







>
>
|
|
|

|

|
|
|
|
|
|
|

|
>
>
|
<
>
>
>
>
>
|
|
<
|

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>






<








>








|
>
>
>
>
>
>




>
>
>
>
>
>
>




>




|







>
>







 







|






|
|
<
<
<
<
<
<
<
|
|
|
|
<







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







<









>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







|
>
>










2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
..
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
...
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
...
137
138
139
140
141
142
143

144
145

146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
...
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190

191
192
193
194
195
196
197

198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239

240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
...
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323







324
325
326
327

328
329
330
331
332
333
334
...
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
...
645
646
647
648
649
650
651

652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
...
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715

////////////////////////////////////////////////////////////////////////////////////////////////////
interface

uses
  Windows, Messages, SysUtils, Classes, Variants, Graphics, Controls, Forms,
  Dialogs, StdCtrls, SHDocVw, OleCtrls, ComCtrls, ExtCtrls, IniFiles,
  NppPlugin, NppDockingForms,
  U_CustomFilter;

type
  TfrmHTMLPreview = class(TNppDockingForm)
    wbIE: TWebBrowser;
    pnlButtons: TPanel;
    btnRefresh: TButton;
    btnClose: TButton;
    sbrIE: TStatusBar;
    pnlPreview: TPanel;
    pnlHTML: TPanel;
    btnAbout: TButton;
    tmrAutorefresh: TTimer;
    chkFreeze: TCheckBox;
    procedure btnRefreshClick(Sender: TObject);
    procedure btnCloseClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormKeyPress(Sender: TObject; var Key: Char);
    procedure FormHide(Sender: TObject);
    procedure FormFloat(Sender: TObject);
    procedure FormDock(Sender: TObject);
................................................................................
      dwFlags: Cardinal; const bstrUrlContext, bstrUrl: WideString);
    procedure wbIEStatusTextChange(ASender: TObject; const Text: WideString);
    procedure wbIEStatusBar(ASender: TObject; StatusBar: WordBool);
    procedure btnCloseStatusbarClick(Sender: TObject);
    procedure btnAboutClick(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure tmrAutorefreshTimer(Sender: TObject);
    procedure chkFreezeClick(Sender: TObject);
    procedure wbIEDocumentComplete(ASender: TObject; const pDisp: IDispatch; const URL: OleVariant);
  private
    { Private declarations }
    FBufferID: NativeInt;
    FScrollPositions: TStringList;
    FFilterThread: TCustomFilterThread;
    FScrollTop: Integer;
    FScrollLeft: Integer;

    function  GetSettings(const Name: string = 'Settings.ini'): TIniFile;

    procedure SaveScrollPos;
    procedure RestoreScrollPos(const BufferID: NativeInt);

    function  DetermineCustomFilter: string;
    function  ExecuteCustomFilter(const FilterName, HTML: string; const BufferID: NativeInt): Boolean;
    function  TransformXMLToHTML(const XML: WideString): string;

    procedure FilterThreadTerminate(Sender: TObject);
  public
    { Public declarations }
    procedure ResetTimer;
    procedure ForgetBuffer(const BufferID: NativeInt);
    procedure DisplayPreview(HTML: string; const BufferID: NativeInt);
  end;

var
  frmHTMLPreview: TfrmHTMLPreview;

procedure ODS(const DebugOutput: string); overload;
procedure ODS(const DebugOutput: string; const Args: array of const); overload;

////////////////////////////////////////////////////////////////////////////////////////////////////
implementation
uses
  ShellAPI, ComObj, StrUtils, IOUtils, Masks, MSHTML,
  RegExpr,
  WebBrowser, SciSupport, U_Npp_PreviewHTML;

{$R *.dfm}

{ ------------------------------------------------------------------------------------------------ }
procedure ODS(const DebugOutput: string); overload;
begin
  OutputDebugString(PChar('PreviewHTML['+IntToHex(GetCurrentThreadId, 4)+']: ' + DebugOutput));
end {ODS};
{ ------------------------------------------------------------------------------------------------ }
procedure ODS(const DebugOutput: string; const Args: array of const); overload;
begin
  ODS(Format(DebugOutput, Args));
end{ODS};


{ ================================================================================================ }

{ ------------------------------------------------------------------------------------------------ }
procedure TfrmHTMLPreview.FormCreate(Sender: TObject);
begin
  FScrollPositions := TStringList.Create;
................................................................................
    tmrAutorefresh.Interval := ReadInteger('Autorefresh', 'Interval', tmrAutorefresh.Interval);
  end;
end {TfrmHTMLPreview.FormCreate};
{ ------------------------------------------------------------------------------------------------ }
procedure TfrmHTMLPreview.FormDestroy(Sender: TObject);
begin
  FreeAndNil(FScrollPositions);
  FreeAndNil(FFilterThread);
  inherited;
end {TfrmHTMLPreview.FormDestroy};


{ ------------------------------------------------------------------------------------------------ }
procedure TfrmHTMLPreview.btnCloseStatusbarClick(Sender: TObject);
begin
................................................................................
var
  View: Integer;
  BufferID: Integer;
  hScintilla: THandle;
  Lexer: NativeInt;
  IsHTML, IsXML, IsCustom: Boolean;
  Size: WPARAM;

  Content: UTF8String;
  HTML: string;

  FilterName: string;
begin
  if chkFreeze.Checked then
    Exit;

  try
    tmrAutorefresh.Enabled := False;
    if Assigned(FFilterThread) then begin
ODS('FreeAndNil(FFilterThread);');
      FreeAndNil(FFilterThread);
    end;
    SaveScrollPos;

    SendMessage(Self.Npp.NppData.NppHandle, NPPM_GETCURRENTSCINTILLA, 0, LPARAM(@View));
    if View = 0 then begin
      hScintilla := Self.Npp.NppData.ScintillaMainHandle;
    end else begin
      hScintilla := Self.Npp.NppData.ScintillaSecondHandle;
................................................................................
    end;
    BufferID := SendMessage(Self.Npp.NppData.NppHandle, NPPM_GETCURRENTBUFFERID, 0, 0);

    Lexer := SendMessage(hScintilla, SCI_GETLEXER, 0, 0);
    IsHTML := (Lexer = SCLEX_HTML);
    IsXML := (Lexer = SCLEX_XML);

    Screen.Cursor := crHourGlass;
    try
      {--- MCO 22-01-2013: determine whether the current document matches a custom filter ---}
      FilterName := DetermineCustomFilter;
      IsCustom := Length(FilterName) > 0;

      {$MESSAGE HINT 'TODO: Find a way to communicate why there is no preview, depending on the situation — MCO 22-01-2013'}

      if IsXML or IsHTML or IsCustom then begin
        Size := SendMessage(hScintilla, SCI_GETTEXT, 0, 0);
        SetLength(Content, Size);
        SendMessage(hScintilla, SCI_GETTEXT, Size, LPARAM(PAnsiChar(Content)));
        Content := UTF8String(PAnsiChar(Content));
        HTML := string(Content);
      end;

      if IsCustom then begin
//MessageBox(Npp.NppData.NppHandle, PChar(Format('FilterName: %s', [FilterName])), 'PreviewHTML', MB_ICONINFORMATION);
        wbIEStatusTextChange(wbIE, Format('Running filter %s...', [FilterName]));
        if ExecuteCustomFilter(FilterName, HTML, BufferID) then begin

          Exit;
        end else begin
          wbIEStatusTextChange(wbIE, Format('Failed filter %s...', [FilterName]));
          HTML := '<pre style="color: darkred">ExecuteCustomFilter returned False</pre>';
        end;
      end else if IsXML then begin
        HTML := TransformXMLToHTML(HTML);

      end;

      DisplayPreview(HTML, BufferID);
    finally
      Screen.Cursor := crDefault;
    end;
  except
    on E: Exception do begin
ODS('btnRefreshClick ### %s: %s', [E.ClassName, StringReplace(E.Message, sLineBreak, '', [rfReplaceAll])]);
      sbrIE.SimpleText := E.Message;
      sbrIE.Visible := True;
    end;
  end;
end {TfrmHTMLPreview.btnRefreshClick};

{ ------------------------------------------------------------------------------------------------ }
procedure TfrmHTMLPreview.chkFreezeClick(Sender: TObject);
begin
  btnRefresh.Enabled := not chkFreeze.Checked;
  if btnRefresh.Enabled then
    btnRefresh.Click;
end {TfrmHTMLPreview.chkFreezeClick};

{ ------------------------------------------------------------------------------------------------ }
procedure TfrmHTMLPreview.DisplayPreview(HTML: string; const BufferID: NativeInt);
var
  IsHTML: Boolean;
  HeadStart: Integer;
  Size: WPARAM;
  Filename: nppString;
  View: Integer;
  hScintilla: THandle;
begin
ODS('DisplayPreview(HTML: "%s"(%d); BufferID: %x)', [StringReplace(Copy(HTML, 1, 10), #13#10, '', [rfReplaceAll]), Length(HTML), BufferID]);
  try
    IsHTML := Length(HTML) > 0;
    pnlHTML.Visible := IsHTML;
    sbrIE.Visible := IsHTML and (Length(sbrIE.SimpleText) > 0);
    if IsHTML then begin
      Size := SendMessage(Self.Npp.NppData.NppHandle, NPPM_GETFULLPATHFROMBUFFERID, BufferID, LPARAM(nil));
      SetLength(Filename, Size);
      SetLength(Filename, SendMessage(Self.Npp.NppData.NppHandle, NPPM_GETFULLPATHFROMBUFFERID, BufferID, LPARAM(nppPChar(Filename))));

      if (Pos('<base ', HTML) = 0) and FileExists(Filename) then begin
        HeadStart := Pos('<head>', HTML);
        if HeadStart > 0 then
          Inc(HeadStart, 6)
        else
          HeadStart := 1;
        Insert('<base href="' + Filename + '" />', HTML, HeadStart);
      end;

      wbIE.LoadDocFromString(HTML);

      if wbIE.GetDocument <> nil then
        self.UpdateDisplayInfo(wbIE.GetDocument.title)
      else
        self.UpdateDisplayInfo('');

      {--- 2013-01-26 Martijn: the WebBrowser control has a tendency to steal the focus. We'll let
                                  the editor take it back. ---}
      SendMessage(Self.Npp.NppData.NppHandle, NPPM_GETCURRENTSCINTILLA, 0, LPARAM(@View));
      if View = 0 then begin
        hScintilla := Self.Npp.NppData.ScintillaMainHandle;
      end else begin
        hScintilla := Self.Npp.NppData.ScintillaSecondHandle;
      end;
      SendMessage(hScintilla, SCI_GRABFOCUS, 0, 0);
    end else begin
      self.UpdateDisplayInfo('');
    end;

    if pnlHTML.Visible then begin
      Self.AlphaBlend := False;
    end else begin
      Self.AlphaBlend := True;
      Self.AlphaBlendValue := 127;
    end;

    RestoreScrollPos(BufferID);
  except
    on E: Exception do begin
ODS('DisplayPreview ### %s: %s', [E.ClassName, StringReplace(E.Message, sLineBreak, '', [rfReplaceAll])]);
      sbrIE.SimpleText := E.Message;
      sbrIE.Visible := True;
    end;
  end;
end {TfrmHTMLPreview.DisplayPreview};

{ ------------------------------------------------------------------------------------------------ }
procedure TfrmHTMLPreview.SaveScrollPos;
var
  Index, ScrollTop, ScrollLeft: Integer;
  docEl: IHTMLElement2;
begin
  FScrollTop := -1;
  FScrollLeft := -1;
  if FBufferID = -1 then
    Exit;

  if Assigned(wbIE.Document) and Assigned((wbIE.Document as IHTMLDocument3).documentElement) then begin
    docEl := (wbIE.Document as IHTMLDocument3).documentElement AS IHTMLElement2;
    ScrollTop := docEl.scrollTop;
    ScrollLeft := docEl.scrollLeft;
................................................................................
  else
    FScrollPositions[Index] := IntToStr(ScrollTop) + '=' + IntToStr(ScrollLeft);
end {TfrmHTMLPreview.SaveScrollPos};

{ ------------------------------------------------------------------------------------------------ }
procedure TfrmHTMLPreview.RestoreScrollPos(const BufferID: NativeInt);
var
  Index: Integer;
  docEl: IHTMLElement2;
begin
  {--- MCO 22-01-2013: Look up this buffer's scroll position; if we know one, wait for the page
                          to finish loading, then restore the scroll position. ---}
  Index := FScrollPositions.IndexOfObject(TObject(BufferID));
  if Index > -1 then begin
    FScrollTop := StrToInt(FScrollPositions.Names[Index]);
    FScrollLeft := StrToInt(FScrollPositions.ValueFromIndex[Index]);







    if (FScrollTop <> -1) and Assigned(wbIE.Document) and Assigned((wbIE.Document as IHTMLDocument3).documentElement) then begin
      docEl := (wbIE.Document as IHTMLDocument3).documentElement as IHTMLElement2;
      docEl.scrollTop := FScrollTop;
      docEl.scrollLeft := FScrollLeft;

    end;
  end;
  FBufferID := BufferID;
end {TfrmHTMLPreview.RestoreScrollPos};

{ ------------------------------------------------------------------------------------------------ }
procedure TfrmHTMLPreview.ForgetBuffer(const BufferID: NativeInt);
................................................................................

{ ------------------------------------------------------------------------------------------------ }
procedure TfrmHTMLPreview.ResetTimer;
begin
  tmrAutorefresh.Enabled := False;
  tmrAutorefresh.Enabled := True;
end {TfrmHTMLPreview.ResetTimer};

{ ------------------------------------------------------------------------------------------------ }
function TfrmHTMLPreview.DetermineCustomFilter: string;
var
  DocFileName: nppString;
  Filters: TIniFile;
  Names: TStringList;
  i: Integer;
  Match: Boolean;
  Ext, Language, DocLanguage: string;
  DocLangType, LangType: Integer;
  Extensions: TStringList;
  Filespec: string;
begin
  DocFileName := StringOfChar(#0, MAX_PATH);
  SendMessage(Npp.NppData.NppHandle, NPPM_GETFILENAME, WPARAM(Length(DocFileName)), LPARAM(nppPChar(DocFileName)));
  DocFileName := nppString(nppPChar(DocFileName));

  DocLangType := -1;
  DocLanguage := '';

  ForceDirectories(Npp.ConfigDir + '\PreviewHTML');
  Filters := TIniFile.Create(Npp.ConfigDir + '\PreviewHTML\Filters.ini');
  Names := TStringList.Create;
  try
    Filters.ReadSections(Names);
    for i := 0 to Names.Count - 1 do begin
      {--- 2013-02-15 Martijn: empty filters should be skipped, and
                      any filter can be disabled by putting a '-' in front of its name. ---}
      if (Length(Names[i]) = 0) or (Names[i][1] = '-') then
        Continue;

      Match := False;

      {--- Martijn 03-03-2013: Test file name ---}
      Filespec := Trim(Filters.ReadString(Names[i], 'Filename', ''));
      if (Filespec <> '') then begin
        // http://docwiki.embarcadero.com/Libraries/XE2/en/System.Masks.MatchesMask#Description
        Match := Match or MatchesMask(ExtractFileName(DocFileName), Filespec);
      end;

      {--- MCO 22-01-2013: Test extension ---}
      Ext := Trim(Filters.ReadString(Names[i], 'Extension', ''));
      if (Ext <> '') then begin
        Extensions := TStringList.Create;
        try
          Extensions.CaseSensitive := False;
          Extensions.Delimiter := ',';
          Extensions.DelimitedText := Ext;
          Match := Match or (Extensions.IndexOf(ExtractFileExt(DocFileName)) > -1);
        finally
          Extensions.Free;
        end;
      end;

      {--- MCO 22-01-2013: Test highlighter language ---}
      Language := Filters.ReadString(Names[i], 'Language', '');
      if Language <> '' then begin
        if DocLangType = -1 then begin
          SendMessage(Npp.NppData.NppHandle, NPPM_GETCURRENTLANGTYPE, WPARAM(0), LPARAM(@DocLangType));
        end;
        if DocLangType > -1 then begin
          if TryStrToInt(Language, LangType) and (LangType = DocLangType) then begin
            Match := True;
          end else begin
            if DocLanguage = '' then begin
              SetLength(DocLanguage, SendMessage(Npp.NppData.NppHandle, NPPM_GETLANGUAGENAME, WPARAM(DocLangType), LPARAM(nil)));
              SetLength(DocLanguage, SendMessage(Npp.NppData.NppHandle, NPPM_GETLANGUAGENAME, WPARAM(DocLangType), LPARAM(PChar(DocLanguage))));
            end;
            if SameText(Language, DocLanguage) then begin
              Match := True;
            end;
          end;
        end;
      end;

      {$MESSAGE HINT 'TODO: Test lexer — MCO 22-01-2013'}

      if Match then
        Exit(Names[i]);
    end;
  finally
    Names.Free;
    Filters.Free;
  end;
end {TfrmHTMLPreview.DetermineCustomFilter};

{ ------------------------------------------------------------------------------------------------ }
function TfrmHTMLPreview.ExecuteCustomFilter(const FilterName, HTML: string; const BufferID: NativeInt): Boolean;
var
  FilterData: TFilterData;
  DocFile: TFileName;
  View: Integer;
  hScintilla: THandle;
  Filters: TIniFile;
  BufferEncoding: NativeInt;
begin
  FilterData.Name := FilterName;
  FilterData.BufferID := BufferID;

  DocFile := StringOfChar(#0, MAX_PATH);
  SendMessage(Npp.NppData.NppHandle, NPPM_GETFULLCURRENTPATH, WPARAM(Length(DocFile)), LPARAM(PChar(DocFile)));
  DocFile := string(PChar(DocFile));
  FilterData.DocFile := DocFile;
  FilterData.Contents := HTML;

  SendMessage(Self.Npp.NppData.NppHandle, NPPM_GETCURRENTSCINTILLA, 0, LPARAM(@View));
  if View = 0 then begin
    hScintilla := Self.Npp.NppData.ScintillaMainHandle;
  end else begin
    hScintilla := Self.Npp.NppData.ScintillaSecondHandle;
  end;
  BufferEncoding := SendMessage(Npp.NppData.NppHandle, NPPM_GETBUFFERENCODING, BufferID, 0);
  case BufferEncoding of
    1, 4: FilterData.Encoding := TEncoding.UTF8;
    2, 6: FilterData.Encoding := TEncoding.BigEndianUnicode;
    3, 7: FilterData.Encoding := TEncoding.Unicode;
    5:    FilterData.Encoding := TEncoding.UTF7;
    else  FilterData.Encoding := TEncoding.ANSI;
  end;
  FilterData.UseBOM := BufferEncoding in [1, 2, 3];
  FilterData.Modified := SendMessage(hScintilla, SCI_GETMODIFY, 0, 0) <> 0;

  Filters := GetSettings('Filters.ini');
  try
    FilterData.FilterInfo := TStringList.Create;
    Filters.ReadSectionValues(FilterName, FilterData.FilterInfo);
  finally
    Filters.Free;
  end;

  FilterData.OnTerminate := FilterThreadTerminate;

  {--- 2013-01-26 Martijn: Create a new TCustomFilterThread ---}
  FFilterThread := TCustomFilterThread.Create(FilterData);
  Result := Assigned(FFilterThread);
  FFilterThread.WaitFor;
end {TfrmHTMLPreview.ExecuteCustomFilter};

{ ------------------------------------------------------------------------------------------------ }
procedure TfrmHTMLPreview.FilterThreadTerminate(Sender: TObject);
begin
ODS('FilterThreadTerminate');
if (Sender as TThread).FatalException is Exception then
  ODS('Fatal %s: "%s"', [((Sender as TThread).FatalException as Exception).ClassName, ((Sender as TThread).FatalException as Exception).Message]);

  FFilterThread := nil;
end {TfrmHTMLPreview.FilterThreadTerminate};


{ ------------------------------------------------------------------------------------------------ }
procedure TfrmHTMLPreview.btnAboutClick(Sender: TObject);
begin
  (npp as TNppPluginPreviewHTML).CommandShowAbout;
end {TfrmHTMLPreview.btnAboutClick};

................................................................................

{ ------------------------------------------------------------------------------------------------ }
procedure TfrmHTMLPreview.wbIEBeforeNavigate2(ASender: TObject; const pDisp: IDispatch; const URL,
  Flags, TargetFrameName, PostData, Headers: OleVariant; var Cancel: WordBool);
var
  Handle: HWND;
begin

  if not SameText(URL, 'about:blank') and not StartsText('javascript:', URL) then begin
    if Assigned(Npp) then
      Handle := Npp.NppData.NppHandle
    else
      Handle := 0;
    ShellExecute(Handle, nil, PChar(VarToStr(URL)), nil, nil, SW_SHOWDEFAULT);
    Cancel := True;
  end;
end;

{ ------------------------------------------------------------------------------------------------ }
procedure TfrmHTMLPreview.wbIEDocumentComplete(ASender: TObject; const pDisp: IDispatch;
  const URL: OleVariant);
var
  docEl: IHTMLElement2;
begin
  if (FScrollTop <> -1) and Assigned(wbIE.Document) and Assigned((wbIE.Document as IHTMLDocument3).documentElement) then begin
    docEl := (wbIE.Document as IHTMLDocument3).documentElement as IHTMLElement2;
    docEl.scrollTop := FScrollTop;
    docEl.scrollLeft := FScrollLeft;
    FScrollTop := -1;
    FScrollLeft := -1;
  end;
end {TfrmHTMLPreview.wbIEDocumentComplete};

{ ------------------------------------------------------------------------------------------------ }
procedure TfrmHTMLPreview.wbIENewWindow3(ASender: TObject; var ppDisp: IDispatch;
  var Cancel: WordBool; dwFlags: Cardinal; const bstrUrlContext, bstrUrl: WideString);
var
  Handle: HWND;
begin
................................................................................
  sbrIE.Visible := StatusBar;
end;

{ ------------------------------------------------------------------------------------------------ }
procedure TfrmHTMLPreview.wbIEStatusTextChange(ASender: TObject; const Text: WideString);
begin
  sbrIE.SimpleText := Text;
//  sbrIE.Visible := Length(Text) > 0;
//  if sbrIE.Visible then
  sbrIE.Refresh;
end;

{ ------------------------------------------------------------------------------------------------ }
procedure TfrmHTMLPreview.wbIETitleChange(ASender: TObject; const Text: WideString);
begin
  inherited;
  self.UpdateDisplayInfo(StringReplace(Text, 'about:blank', '', [rfReplaceAll]));
end;

end.

Added src/U_CustomFilter.pas.













































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
unit U_CustomFilter;

////////////////////////////////////////////////////////////////////////////////////////////////////
interface
uses
  Classes, Windows, SysUtils;

type
  {--- 2013-01-26 Martijn: TFilterData contains all the information needed for the filter to do its
                            job (in a different thread, so the filter processing becomes thread-safe). ---}
  TFilterData = record
    Name: string;
    DocFile: TFileName;
    BufferID: NativeInt;
    Contents: string;
    Encoding: TEncoding;
    UseBOM: Boolean;
    Modified: Boolean;
    FilterInfo: TStringList; // the contents of the filter
    OnTerminate: TNotifyEvent;
  end;

type
  TCustomFilterThread = class(TThread)
  private
    FData: TFilterData;
    function Run(const Command, WorkingDir: string; const Input, Output, Error: TStream): NativeInt;
  public
    constructor Create(const Data: TFilterData); reintroduce;
    destructor  Destroy; override;
    procedure Execute; override;
  end;


////////////////////////////////////////////////////////////////////////////////////////////////////
implementation

uses
  IOUtils,
  process, Pipes,
  F_PreviewHTML;

{ TCustomFilterThread }

{ ------------------------------------------------------------------------------------------------ }
constructor TCustomFilterThread.Create(const Data: TFilterData);
begin
  FData := Data;
  Self.OnTerminate := Data.OnTerminate;
  inherited Create;
end {TCustomFilter.Create};

{ ------------------------------------------------------------------------------------------------ }
destructor TCustomFilterThread.Destroy;
begin
  FreeAndNil(FData.FilterInfo);
  inherited;
end {TCustomFilterThread.Destroy};

{ ------------------------------------------------------------------------------------------------ }
procedure TCustomFilterThread.Execute;
type
  TContentInputType = (citStandardInput, citFile);
  TContentOutputType = (cotStandardOutput, cotInputFile, cotOutputFile);
var
  HTML: string;
  Command: string;
  TempFile, InFile, OutFile, WorkingDir: TFileName;
  SS: TStringStream;
  InputMethod: TContentInputType;
  OutputMethod: TContentOutputType;
  Input, Output, Error: TStringStream;
//  i: Integer;
begin
//ODS('Data.FilterInfo.Count = "%d"', [FData.FilterInfo.Count]);
//for i := 0 to FData.FilterInfo.Count - 1 do begin
//  ODS('Data.FilterInfo[%d] = "%s"', [i, FData.FilterInfo.Strings[i]]);
//end;
  try
    Command := FData.FilterInfo.Values['Command'];
//ODS('Command: "%s"', [Command]);
    if Command = '' then begin
      HTML := FData.Contents;
      Exit;
    end;


    // Decide what the input and output methods are
    if Pos('%1', Command) > 0 then begin
      InputMethod := citFile;
    end else begin
      InputMethod := citStandardInput;
    end;
    if Pos('%2', Command) > 0 then begin
      OutputMethod := cotOutputFile;
    end else begin
      OutputMethod := cotStandardOutput;
    end;

    {$MESSAGE HINT 'TODO: allow for explicit overrides in the filter settings — Martijn 2013-01-26'}

    if Terminated then
      Exit;

    {--- Now we figure out what in- and output files we will need ---}
    if InputMethod = citStandardInput then begin
      InFile := '';
      Input := TStringStream.Create(FData.Contents, FData.Encoding, False);
      if OutputMethod = cotInputFile then begin
        OutputMethod := cotStandardOutput;
        {$MESSAGE HINT 'TODO: warn the user that this filter is misconfigured — Martijn 2013-01-26'}
      end;
      if OutputMethod = cotOutputFile then
        OutFile := TPath.GetTempFileName
      else
        OutFile := '';
    end else begin // ContentInput = citFile
      Input := nil;
      if FData.Modified or (OutputMethod = cotInputFile) then begin
        TempFile := TPath.GetTempFileName;
        // rename the TempFile so it has the proper extension
        InFile := ChangeFileExt(TempFile, ExtractFileExt(FData.DocFile));
        if not RenameFile(TempFile, InFile) then
          InFile := TempFile;
        // Save the contents to the input file
        SS := TStringStream.Create(FData.Contents, FData.Encoding, False);
        try
          SS.SaveToFile(InFile);
        finally
          SS.Free;
        end;
      end else begin
        // Use the original file as input file
        InFile := FData.DocFile;
      end;
      case OutputMethod of
        cotInputFile:   OutFile := InFile;
        cotOutputFile:  OutFile := TPath.GetTempFileName;
      end;
    end;

    if Terminated then
      Exit;

    if InFile = '' then
      WorkingDir := ExtractFilePath(FData.DocFile)
    else
      WorkingDir := ExtractFilePath(InFile);

    // Perform replacements in the command string
    Command := StringReplace(Command, '%1', InFile, [rfReplaceAll]);
    Command := StringReplace(Command, '%2', OutFile, [rfReplaceAll]);
    // TODO: also replace environment strings?

    if OutFile = '' then begin
      Output := TStringStream.Create('', FData.Encoding, False);
    end else begin
      Output := nil;
    end;
    Error := TStringStream.Create('', CP_OEMCP);
    try
ODS('Command="%s"; WorkingDir="%s"; InFile="%s"; OutFile="%s"', [Command, WorkingDir, InFile, OutFile]);

      // Run the command and keep track of the new process
      Run(Command, WorkingDir, Input, Output, Error);

      if Terminated then
        Exit;

      case OutputMethod of
        cotStandardOutput: begin
          // read the output from the process's standard output stream
          HTML := TStringStream(Output).DataString;
          if Length(HTML) = 0 then
            HTML := '<pre style="color: darkred">' + StringReplace(Error.DataString, '<', '&lt;', [rfReplaceAll]) + '</pre>';
        end;
        cotInputFile, cotOutputFile: begin
          SS := TStringStream.Create('', FData.Encoding, False);
          try
            SS.LoadFromFile(OutFile);
            HTML := SS.DataString;
          finally
            SS.Free;
          end;
        end;
      end;
    finally
      FreeAndNil(Input);
      FreeAndNil(Output);
      FreeAndNil(Error);
    end;
  finally
    {--- When finished, we need to populate the webbrowser component, but this needs to happen in
          the main thread. ---}
    if not Terminated then begin
ODS('About to synchronize HTML of length %d in thread ID [%x]', [Length(HTML), GetCurrentThreadID]);
      Synchronize(procedure
                  begin
ODS('Synchronizing HTML of length %d in thread ID [%x]', [Length(HTML), GetCurrentThreadID]);
                    frmHTMLPreview.DisplayPreview(HTML, FData.BufferID);
                  end);
    end;

ODS('Cleaning up...');
    // Delete the temporary files
    if (InFile <> '') and not SameFileName(FData.DocFile, OutFile) then
      DeleteFile(OutFile);
    if (InFile <> '') and not SameFileName(InFile, FData.DocFile) then
      DeleteFile(InFile);
  end;
end {TCustomFilter.Execute};

{ ------------------------------------------------------------------------------------------------ }
function TCustomFilterThread.Run(const Command, WorkingDir: string; const Input, Output, Error: TStream): NativeInt;
const
  READ_BYTES = 2048;
  { - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - }
  {$POINTERMATH ON}
  function CacheStream(Input: TInputPipeStream; Cache: TMemoryStream; var BytesRead: LongInt): LongInt;
  var
    CacheMem: PByte;
  begin
    if Input.NumBytesAvailable > 0 then begin
      // make sure we have room
      Cache.SetSize(BytesRead + READ_BYTES);

      // try reading it
      CacheMem := Cache.Memory;
      Inc(CacheMem, BytesRead);
      Result := Input.Read(CacheMem^, READ_BYTES);
      if Result > 0 then begin
        Inc(BytesRead, Result);
      end;
    end else begin
      Result := 0;
    end;
  end{CacheStream};
  {$POINTERMATH OFF}
  { - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - }
var
  OMS, EMS: TMemoryStream;
  P: TProcess;
  n: LongInt;
  BytesRead, ErrBytesRead: LongInt;
begin
  // We cannot use poWaitOnExit here since we don't
  // know the size of the output. On Linux the size of the
  // output pipe is 2 kB. If the output data is more, we
  // need to read the data. This isn't possible since we are
  // waiting. So we get a deadlock here.
  //
  // A temp Memorystream is used to buffer the output

  BytesRead := 0;
  OMS := TMemoryStream.Create;
  ErrBytesRead := 0;
  EMS := TMemoryStream.Create;
  try
    P := TProcess.Create(nil);
    try
      P.CurrentDirectory := WorkingDir;
      P.CommandLine := Command;
      P.Options := P.Options + [poUsePipes];
      P.StartupOptions := [suoUseShowWindow];
      P.ShowWindow := swoHIDE;

      P.Execute;
      if P.Running and Assigned(Input) then
        P.Input.CopyFrom(Input, Input.Size);

      while P.Running do begin
        n := CacheStream(P.Output, OMS, BytesRead);
        Inc(n, CacheStream(P.Stderr, EMS, ErrBytesRead));
        if n <= 0 then begin
          // no data, wait 100 ms
          Sleep(100);
        end;

        if Self.Terminated then begin
          P.Terminate(-1);
          Exit(-1);
        end;

      end;
      // read last part
      repeat
        n := CacheStream(P.Output, OMS, BytesRead);
        Inc(n, CacheStream(P.Stderr, EMS, ErrBytesRead));
      until n <= 0;

      Result := P.ExitStatus;

      OMS.SetSize(BytesRead);
      EMS.SetSize(ErrBytesRead);
    finally
      P.Free;
    end;

    if Assigned(Output) then
      Output.CopyFrom(OMS, 0);

    if Assigned(Error) then
      Error.CopyFrom(EMS, 0);
  finally
    OMS.Free;
    EMS.Free;
  end;
end {TCustomFilterThread.Run};

end.

Changes to src/U_Npp_PreviewHTML.pas.

9
10
11
12
13
14
15

16
17
18


19
20
21
22
23
24
25
26


27
28
29
30
31
32
33
..
82
83
84
85
86
87
88





89
90
91
92
93
94
95
96
97

98



99






100
101
102
103
104
105
106
...
137
138
139
140
141
142
143
















144
145
146
147
148
149
150
...
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
  F_About, F_PreviewHTML;

type
  TNppPluginPreviewHTML = class(TNppPlugin)
  public
    constructor Create;
    procedure CommandShowPreview;

    procedure CommandReplaceHelloWorld;
    procedure CommandShowAbout;
    procedure CommandSetIEVersion(const BrowserEmulation: Integer);


    procedure DoNppnToolbarModification; override;
    procedure DoNppnFileClosed(const BufferID: Cardinal); override;
    procedure DoNppnBufferActivated(const BufferID: Cardinal); override;
    procedure DoModified(const hwnd: HWND; const modificationType: Integer); override;
  end {TNppPluginPreviewHTML};

procedure _FuncReplaceHelloWorld; cdecl;
procedure _FuncShowPreview; cdecl;


procedure _FuncShowAbout; cdecl;

procedure _FuncSetIE7; cdecl;
procedure _FuncSetIE8; cdecl;
procedure _FuncSetIE9; cdecl;
procedure _FuncSetIE10; cdecl;
procedure _FuncSetIE11; cdecl;
................................................................................
    self.AddFuncItem('View as IE1&1', _FuncSetIE11, EmulatedVersion = 11);
  if MajorIEVersion >= 12 then
    self.AddFuncItem('View as IE1&2', _FuncSetIE12, EmulatedVersion = 12);
  if MajorIEVersion >= 13 then
    self.AddFuncItem('View as IE1&3', _FuncSetIE13, EmulatedVersion = 13);

  self.AddFuncSeparator;






  self.AddFuncItem('&About', _FuncShowAbout);
end {TNppPluginPreviewHTML.Create};

{ ------------------------------------------------------------------------------------------------ }
procedure _FuncReplaceHelloWorld; cdecl;
begin
  Npp.CommandReplaceHelloWorld;
end;

procedure _FuncShowAbout; cdecl;



{ ------------------------------------------------------------------------------------------------ }






begin
  Npp.CommandShowAbout;
end;
{ ------------------------------------------------------------------------------------------------ }
procedure _FuncShowPreview; cdecl;
begin
  Npp.CommandShowPreview;
................................................................................
end;
{ ------------------------------------------------------------------------------------------------ }
procedure _FuncSetIE13; cdecl;
begin
  Npp.CommandSetIEVersion(13000);
end;


















{ ------------------------------------------------------------------------------------------------ }
procedure TNppPluginPreviewHTML.CommandReplaceHelloWorld;
var
  s: UTF8String;
begin
  s := 'Hello World';
................................................................................
end {TNppPluginPreviewHTML.DoNppnToolbarModification};

{ ------------------------------------------------------------------------------------------------ }
procedure TNppPluginPreviewHTML.DoNppnBufferActivated(const BufferID: Cardinal);
begin
  inherited;
  if Assigned(frmHTMLPreview) and frmHTMLPreview.Visible then begin
{$MESSAGE HINT 'TODO: only refresh the preview if it’s configured to follow the active tab'}
    frmHTMLPreview.btnRefresh.Click;
  end;
end {TNppPluginPreviewHTML.DoNppnBufferActivated};

{ ------------------------------------------------------------------------------------------------ }
procedure TNppPluginPreviewHTML.DoNppnFileClosed(const BufferID: Cardinal);
begin







>
|

<
>
>








>
>







 







>
>
>
>
>









>
|
>
>
>

>
>
>
>
>
>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







<







9
10
11
12
13
14
15
16
17
18

19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
..
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
...
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
...
247
248
249
250
251
252
253

254
255
256
257
258
259
260
  F_About, F_PreviewHTML;

type
  TNppPluginPreviewHTML = class(TNppPlugin)
  public
    constructor Create;
    procedure CommandShowPreview;
    procedure CommandSetIEVersion(const BrowserEmulation: Integer);
    procedure CommandOpenFile(const Filename: nppString);
    procedure CommandShowAbout;

    procedure CommandReplaceHelloWorld;

    procedure DoNppnToolbarModification; override;
    procedure DoNppnFileClosed(const BufferID: Cardinal); override;
    procedure DoNppnBufferActivated(const BufferID: Cardinal); override;
    procedure DoModified(const hwnd: HWND; const modificationType: Integer); override;
  end {TNppPluginPreviewHTML};

procedure _FuncReplaceHelloWorld; cdecl;
procedure _FuncShowPreview; cdecl;
procedure _FuncOpenSettings; cdecl;
procedure _FuncOpenFilters; cdecl;
procedure _FuncShowAbout; cdecl;

procedure _FuncSetIE7; cdecl;
procedure _FuncSetIE8; cdecl;
procedure _FuncSetIE9; cdecl;
procedure _FuncSetIE10; cdecl;
procedure _FuncSetIE11; cdecl;
................................................................................
    self.AddFuncItem('View as IE1&1', _FuncSetIE11, EmulatedVersion = 11);
  if MajorIEVersion >= 12 then
    self.AddFuncItem('View as IE1&2', _FuncSetIE12, EmulatedVersion = 12);
  if MajorIEVersion >= 13 then
    self.AddFuncItem('View as IE1&3', _FuncSetIE13, EmulatedVersion = 13);

  self.AddFuncSeparator;

  self.AddFuncItem('Edit &settings', _FuncOpenSettings);
  self.AddFuncItem('Edit &filter definitions', _FuncOpenFilters);

  self.AddFuncSeparator;

  self.AddFuncItem('&About', _FuncShowAbout);
end {TNppPluginPreviewHTML.Create};

{ ------------------------------------------------------------------------------------------------ }
procedure _FuncReplaceHelloWorld; cdecl;
begin
  Npp.CommandReplaceHelloWorld;
end;
{ ------------------------------------------------------------------------------------------------ }
procedure _FuncOpenSettings; cdecl;
begin
  Npp.CommandOpenFile('Settings.ini');
end;
{ ------------------------------------------------------------------------------------------------ }
procedure _FuncOpenFilters; cdecl;
begin
  Npp.CommandOpenFile('Filters.ini');
end;
{ ------------------------------------------------------------------------------------------------ }
procedure _FuncShowAbout; cdecl;
begin
  Npp.CommandShowAbout;
end;
{ ------------------------------------------------------------------------------------------------ }
procedure _FuncShowPreview; cdecl;
begin
  Npp.CommandShowPreview;
................................................................................
end;
{ ------------------------------------------------------------------------------------------------ }
procedure _FuncSetIE13; cdecl;
begin
  Npp.CommandSetIEVersion(13000);
end;


{ ------------------------------------------------------------------------------------------------ }
procedure TNppPluginPreviewHTML.CommandOpenFile(const Filename: nppString);
var
  FullPath: nppString;
begin
  try
    FullPath := Npp.ConfigDir + '\PreviewHTML\' + Filename;
    if not FileExists(FullPath) and FileExists(ChangeFileExt(FullPath, '.sample' + ExtractFileExt(FullPath))) then
      FullPath := ChangeFileExt(FullPath, '.sample' + ExtractFileExt(FullPath));
    if not DoOpen(FullPath) then
      MessageBox(Npp.NppData.NppHandle, PChar(Format('Unable to open "%s".', [FullPath])), PChar(Caption), MB_ICONWARNING);
  except
    ShowException(ExceptObject, ExceptAddr);
  end;
end {TNppPluginPreviewHTML.CommandOpenFilters};

{ ------------------------------------------------------------------------------------------------ }
procedure TNppPluginPreviewHTML.CommandReplaceHelloWorld;
var
  s: UTF8String;
begin
  s := 'Hello World';
................................................................................
end {TNppPluginPreviewHTML.DoNppnToolbarModification};

{ ------------------------------------------------------------------------------------------------ }
procedure TNppPluginPreviewHTML.DoNppnBufferActivated(const BufferID: Cardinal);
begin
  inherited;
  if Assigned(frmHTMLPreview) and frmHTMLPreview.Visible then begin

    frmHTMLPreview.btnRefresh.Click;
  end;
end {TNppPluginPreviewHTML.DoNppnBufferActivated};

{ ------------------------------------------------------------------------------------------------ }
procedure TNppPluginPreviewHTML.DoNppnFileClosed(const BufferID: Cardinal);
begin

Added src/common/pipes.inc.























































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
{
    This file is part of the Free Pascal run time library.
    Copyright (c) 1998 by Michael Van Canneyt

    Win part of pipe stream.

    See the file COPYING.FPC, included in this distribution,
    for details about the copyright.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

 **********************************************************************}

uses windows;

Const piInheritablePipe : TSecurityAttributes = (
                           nlength:SizeOF(TSecurityAttributes);
                           lpSecurityDescriptor:Nil;
                           Binherithandle:True);
      piNonInheritablePipe : TSecurityAttributes = (
                             nlength:SizeOF(TSecurityAttributes);
                             lpSecurityDescriptor:Nil;
                             Binherithandle:False);


      PipeBufSize = 1024;


Function CreatePipeHandles (Var Inhandle,OutHandle : THandle) : Boolean;

begin
  Result := CreatePipe (Inhandle, OutHandle, @piInheritablePipe,PipeBufSize);
end;


Function TInputPipeStream.GetNumBytesAvailable: DWord;
begin
  if not PeekNamedPipe(Handle, nil, 0, nil, @Result, nil) then
    Result := 0;
end;

Added src/common/pipes.pas.



















































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
{
    This file is part of the Free Pascal run time library.
    Copyright (c) 1999-2000 by Michael Van Canneyt

    Implementation of pipe stream.

    See the file COPYING.FPC, included in this distribution,
    for details about the copyright.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

 **********************************************************************}

{$IFDEF FPC}
{$mode objfpc}
{$ENDIF}

Unit Pipes;

Interface

Uses sysutils,Classes;

Type
  EPipeError = Class(EStreamError);
  EPipeSeek = Class (EPipeError);
  EPipeCreation = Class (EPipeError);

  { TInputPipeStream }

  TInputPipeStream = Class(THandleStream)
    Private
      FPos : Int64;
      function GetNumBytesAvailable: Cardinal;
    public
      Function Write (Const Buffer; Count : Longint) :Longint; Override;
      Function Seek (Offset : Longint;Origin : Word) : longint;override;
      Function Read (Var Buffer; Count : Longint) : longint; Override;
      property NumBytesAvailable: Cardinal read GetNumBytesAvailable;
    end;

  TOutputPipeStream = Class(THandleStream)
    Public
      Function Seek (Offset : Longint;Origin : Word) : longint;override;
      Function Read (Var Buffer; Count : Longint) : longint; Override;
    end;

Function CreatePipeHandles (Var Inhandle,OutHandle : THandle) : Boolean;
Procedure CreatePipeStreams (Var InPipe : TInputPipeStream;
                             Var OutPipe : TOutputPipeStream);

Const EPipeMsg = 'Failed to create pipe.';
      ENoSeekMsg = 'Cannot seek on pipes';


Implementation

{$i pipes.inc}

Procedure CreatePipeStreams (Var InPipe : TInputPipeStream;
                             Var OutPipe : TOutputPipeStream);

Var InHandle,OutHandle : THandle;

begin
  if CreatePipeHandles (InHandle, OutHandle) then
    begin
    InPipe:=TInputPipeStream.Create (InHandle);
    OutPipe:=TOutputPipeStream.Create (OutHandle);
    end
  Else
    Raise EPipeCreation.Create (EPipeMsg)
end;

Function TInputPipeStream.Write (Const Buffer; Count : Longint) : longint;

begin
{$ifdef ver2_2_0}
  raise EStreamError.Create( 'Cannot write to InputPipeStream');
{$else}
  //WriteNotImplemented;
  raise EStreamError.Create( 'Cannot write to InputPipeStream');
{$endif}
  Result := 0;
end;

Function TInputPipeStream.Read (Var Buffer; Count : Longint) : longint;

begin
  Result:=Inherited Read(Buffer,Count);
  Inc(FPos,Result);
end;

Function TInputPipeStream.Seek (Offset : Longint;Origin : Word) : longint;

Const BufSize = 100;

Var Buf : array[1..BufSize] of Byte;

begin
  If (Origin=soFromCurrent) and (Offset=0) then
     result:=FPos;
  { Try to fake seek by reading and discarding }
  if Not((Origin=soFromCurrent) and (Offset>=0) or
         ((Origin=soFrombeginning) and (OffSet>=FPos))) then
     Raise EPipeSeek.Create(ENoSeekMSg);
  if Origin=soFromBeginning then
    Dec(Offset,FPos);
  While ((Offset Div BufSize)>0)
        and (Read(Buf,SizeOf(Buf))=BufSize) do
     Dec(Offset,BufSize);
  If (Offset>0) then
    Read(Buf,BufSize);
  Result:=FPos;
end;

Function TOutputPipeStream.Read(Var Buffer; Count : Longint) : longint;

begin
{$ifdef ver2_2_0}
  raise EStreamError.Create( 'Cannot read from OutputPipeStream');
{$else}
//  ReadNotImplemented;
  raise EStreamError.Create( 'Cannot read from OutputPipeStream');
{$endif}
  Result := 0;
end;

Function TOutputPipeStream.Seek (Offset : Longint;Origin : Word) : longint;

begin
  Raise EPipeSeek.Create (ENoSeekMsg);
end;

end.

Added src/common/process.inc.



































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
{
    This file is part of the Free Component Library (FCL)
    Copyright (c) 1999-2008 by the Free Pascal development team

    See the file COPYING.FPC, included in this distribution,
    for details about the copyright.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

 **********************************************************************}

Const
  PriorityConstants : Array [TProcessPriority] of Cardinal =
                      (HIGH_PRIORITY_CLASS,IDLE_PRIORITY_CLASS,
                       NORMAL_PRIORITY_CLASS,REALTIME_PRIORITY_CLASS);

procedure TProcess.CloseProcessHandles;
begin
  if (FProcessHandle<>0) then
    CloseHandle(FProcessHandle);
  if (FThreadHandle<>0) then
    CloseHandle(FThreadHandle);
end;

Function TProcess.PeekExitStatus : Boolean;

begin
  GetExitCodeProcess(ProcessHandle,FExitCode);
  Result:=(FExitCode<>Still_Active);
end;

Function GetStartupFlags (P : TProcess): Cardinal;

begin
  With P do
    begin
    Result:=0;
    if poUsePipes in FProcessOptions then
       Result:=Result or Startf_UseStdHandles;
    if suoUseShowWindow in FStartupOptions then
      Result:=Result or startf_USESHOWWINDOW;
    if suoUSESIZE in FStartupOptions then
      Result:=Result or startf_usesize;
    if suoUsePosition in FStartupOptions then
      Result:=Result or startf_USEPOSITION;
    if suoUSECOUNTCHARS in FStartupoptions then
      Result:=Result or startf_usecountchars;
    if suoUsefIllAttribute in FStartupOptions then
      Result:=Result or startf_USEFILLATTRIBUTE;
    end;
end;

Function GetCreationFlags(P : TProcess) : Cardinal;

begin
  With P do
    begin
    Result:=0;
    if poNoConsole in FProcessOptions then
      Result:=Result or Detached_Process;
    if poNewConsole in FProcessOptions then
      Result:=Result or Create_new_console;
    if poNewProcessGroup in FProcessOptions then
      Result:=Result or CREATE_NEW_PROCESS_GROUP;
    If poRunSuspended in FProcessOptions Then
      Result:=Result or Create_Suspended;
    if poDebugProcess in FProcessOptions Then
      Result:=Result or DEBUG_PROCESS;
    if poDebugOnlyThisProcess in FProcessOptions Then
      Result:=Result or DEBUG_ONLY_THIS_PROCESS;
    if poDefaultErrorMode in FProcessOptions Then
      Result:=Result or CREATE_DEFAULT_ERROR_MODE;
    result:=result or PriorityConstants[FProcessPriority];
    end;
end;

Function StringsToPChars(List : TStrings): pointer;

var
  EnvBlock: string;
  I: Integer;

begin
  EnvBlock := '';
  For I:=0 to List.Count-1 do
    EnvBlock := EnvBlock + List[i] + #0;
  EnvBlock := EnvBlock + #0;
  GetMem(Result, Length(EnvBlock));
  CopyMemory(Result, @EnvBlock[1], Length(EnvBlock));
end;

Procedure InitProcessAttributes(P : TProcess; Var PA : TSecurityAttributes);

begin
  FillChar(PA,SizeOf(PA),0);
  PA.nLength := SizeOf(PA);
end;

Procedure InitThreadAttributes(P : TProcess; Var TA : TSecurityAttributes);

begin
  FillChar(TA,SizeOf(TA),0);
  TA.nLength := SizeOf(TA);
end;

Procedure InitStartupInfo(P : TProcess; Var SI : STARTUPINFO);

Const
  SWC : Array [TShowWindowOptions] of Cardinal =
             (0,SW_HIDE,SW_Maximize,SW_Minimize,SW_Restore,SW_Show,
             SW_ShowDefault,SW_ShowMaximized,SW_ShowMinimized,
               SW_showMinNOActive,SW_ShowNA,SW_ShowNoActivate,SW_ShowNormal);

begin
  FillChar(SI,SizeOf(SI),0);
  With SI do
    begin
    dwFlags:=GetStartupFlags(P);
    if P.FShowWindow<>swoNone then
     dwFlags:=dwFlags or Startf_UseShowWindow
    else
      dwFlags:=dwFlags and not Startf_UseShowWindow;
    wShowWindow:=SWC[P.FShowWindow];
    if (poUsePipes in P.Options) then
      begin
      dwFlags:=dwFlags or Startf_UseStdHandles;
      end;
    if P.FillAttribute<>0 then
      begin
      dwFlags:=dwFlags or Startf_UseFillAttribute;
      dwFillAttribute:=P.FillAttribute;
      end;
     dwXCountChars:=P.WindowColumns;
     dwYCountChars:=P.WindowRows;
     dwYsize:=P.WindowHeight;
     dwXsize:=P.WindowWidth;
     dwy:=P.WindowTop;
     dwX:=P.WindowLeft;
     end;
end;

Procedure CreatePipes(Var HI,HO,HE : Thandle; Var SI : TStartupInfo; CE : Boolean);

begin
  CreatePipeHandles(SI.hStdInput,HI);
  CreatePipeHandles(HO,Si.hStdOutput);
  if CE then
    CreatePipeHandles(HE,SI.hStdError)
  else
    begin
    SI.hStdError:=SI.hStdOutput;
    HE:=HO;
    end;
end;


Procedure TProcess.Execute;
Var
  PName,PDir,PCommandLine : PChar;
  FEnv: pointer;
  FCreationFlags : Cardinal;
  FProcessAttributes : TSecurityAttributes;
  FThreadAttributes : TSecurityAttributes;
  FProcessInformation : TProcessInformation;
  FStartupInfo : STARTUPINFO;
  HI,HO,HE : THandle;
begin
  FInheritHandles:=True;
  PName:=Nil;
  PCommandLine:=Nil;
  PDir:=Nil;
    
  if (FApplicationName='') then
    begin
      If (FCommandLine='') then
        Raise EProcess.Create(SNoCommandline);
      PCommandLine:=Pchar(FCommandLine)
    end
  else
    begin
      PName:=Pchar(FApplicationName);
      If (FCommandLine='') then
        PCommandLine:=Pchar(FApplicationName)
      else
        PCommandLine:=Pchar(FCommandLine)
    end;

  If FCurrentDirectory<>'' then
    PDir:=Pchar(FCurrentDirectory);
  if FEnvironment.Count<>0 then
    FEnv:=StringsToPChars(FEnvironment)
  else
    FEnv:=Nil;
  Try
    FCreationFlags:=GetCreationFlags(Self);
    InitProcessAttributes(Self,FProcessAttributes);
    InitThreadAttributes(Self,FThreadAttributes);
    InitStartupInfo(Self,FStartUpInfo);
    If poUsePipes in FProcessOptions then
      CreatePipes(HI,HO,HE,FStartupInfo,Not(poStdErrToOutPut in FProcessOptions));
    Try
      If Not CreateProcess (PName,PCommandLine,@FProcessAttributes,@FThreadAttributes,
                   FInheritHandles,FCreationFlags,FEnv,PDir,FStartupInfo,
                   fProcessInformation) then
        Raise EProcess.CreateFmt('Failed to execute %s : %s',[FCommandLine, SysErrorMessage(GetLastError)]);
      FProcessHandle:=FProcessInformation.hProcess;
      FThreadHandle:=FProcessInformation.hThread;
      FProcessID:=FProcessINformation.dwProcessID;
    Finally
      if POUsePipes in FProcessOptions then
        begin
        FileClose(FStartupInfo.hStdInput);
        FileClose(FStartupInfo.hStdOutput);
        if Not (poStdErrToOutPut in FProcessOptions) then
          FileClose(FStartupInfo.hStdError);
        CreateStreams(HI,HO,HE);
        end;
    end;
    FRunning:=True;
  Finally
    If FEnv<>Nil then
      FreeMem(FEnv);
  end;
  if not (csDesigning in ComponentState) and // This would hang the IDE !
     (poWaitOnExit in FProcessOptions) and
      not (poRunSuspended in FProcessOptions) then
    WaitOnExit;
end;

Function TProcess.WaitOnExit : Boolean;

Var
  R : DWord;

begin
  R:=WaitForSingleObject (FProcessHandle,Infinite);
  Result:=(R<>Wait_Failed);
  If Result then
    GetExitStatus;
  FRunning:=False;
end;

Function TProcess.Suspend : Longint;

begin
  Result:=SuspendThread(ThreadHandle);
end;

Function TProcess.Resume : LongInt;

begin
  Result:=ResumeThread(ThreadHandle);
end;

Function TProcess.Terminate(AExitCode : Integer) : Boolean;

begin
  Result:=False;
  If ExitStatus=Still_active then
    Result:=TerminateProcess(Handle,AexitCode);
end;

Procedure TProcess.SetShowWindow (Value : TShowWindowOptions);


begin
  FShowWindow:=Value;
end;



Added src/common/process.pas.











































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
{
    This file is part of the Free Component Library (FCL)
    Copyright (c) 1999-2000 by the Free Pascal development team

    See the file COPYING.FPC, included in this distribution,
    for details about the copyright.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

 **********************************************************************}
{$IFDEF FPC}
{$mode objfpc}
{$h+}
{$ENDIF}
unit process;

interface

Uses Classes,
     pipes,
     SysUtils, Types;

Type
  TProcessOption = (poRunSuspended,poWaitOnExit,
                    poUsePipes,poStderrToOutPut,
                    poNoConsole,poNewConsole,
                    poDefaultErrorMode,poNewProcessGroup,
                    poDebugProcess,poDebugOnlyThisProcess);

  TShowWindowOptions = (swoNone,swoHIDE,swoMaximize,swoMinimize,swoRestore,swoShow,
                        swoShowDefault,swoShowMaximized,swoShowMinimized,
                        swoshowMinNOActive,swoShowNA,swoShowNoActivate,swoShowNormal);

  TStartupOption = (suoUseShowWindow,suoUseSize,suoUsePosition,
                    suoUseCountChars,suoUseFillAttribute);

  TProcessPriority = (ppHigh,ppIdle,ppNormal,ppRealTime);

  TProcessOptions = set of TProcessOption;
  TStartupOptions = set of TStartupOption;


Type
  TProcess = Class (TComponent)
  Private
    FProcessOptions : TProcessOptions;
    FStartupOptions : TStartupOptions;
    FProcessID : Integer;
    FThreadID : Integer;
    FProcessHandle : Thandle;
    FThreadHandle : Thandle;
    FFillAttribute : Cardinal;
    FApplicationName : string;
    FConsoleTitle : String;
    FCommandLine : String;
    FCurrentDirectory : String;
    FDesktop : String;
    FEnvironment : Tstrings;
    FShowWindow : TShowWindowOptions;
    FInherithandles : Boolean;
    FProcessPriority : TProcessPriority;
    dwXCountchars,
    dwXSize,
    dwYsize,
    dwx,
    dwYcountChars,
    dwy : Cardinal;
    Procedure FreeStreams;
    Function  GetExitStatus : Integer;
    Function  GetRunning : Boolean;
    Function  GetWindowRect : TRect;
    Procedure SetWindowRect (Value : TRect);
    Procedure SetShowWindow (Value : TShowWindowOptions);
    Procedure SetWindowColumns (Value : Cardinal);
    Procedure SetWindowHeight (Value : Cardinal);
    Procedure SetWindowLeft (Value : Cardinal);
    Procedure SetWindowRows (Value : Cardinal);
    Procedure SetWindowTop (Value : Cardinal);
    Procedure SetWindowWidth (Value : Cardinal);
    procedure SetApplicationName(const Value: String);
    procedure SetProcessOptions(const Value: TProcessOptions);
    procedure SetActive(const Value: Boolean);
    procedure SetEnvironment(const Value: TStrings);
    function  PeekExitStatus: Boolean;
  Protected  
    FRunning : Boolean;
    FExitCode : Cardinal;
    FInputStream  : TOutputPipeStream;
    FOutputStream : TInputPipeStream;
    FStderrStream : TInputPipeStream;
    procedure CloseProcessHandles; virtual;
    Procedure CreateStreams(InHandle,OutHandle,ErrHandle : Longint);virtual;
    procedure FreeStream(var AStream: THandleStream);
  Public
    Constructor Create (AOwner : TComponent);override;
    Destructor Destroy; override;
    Procedure Execute; virtual;
    procedure CloseInput; virtual;
    procedure CloseOutput; virtual;
    procedure CloseStderr; virtual;
    Function Resume : Integer; virtual;
    Function Suspend : Integer; virtual;
    Function Terminate (AExitCode : Integer): Boolean; virtual;
    Function WaitOnExit : Boolean;
    Property WindowRect : Trect Read GetWindowRect Write SetWindowRect;
    Property Handle : THandle Read FProcessHandle;
    Property ProcessHandle : THandle Read FProcessHandle;
    Property ThreadHandle : THandle Read FThreadHandle;
    Property ProcessID : Integer Read FProcessID;
    Property ThreadID : Integer Read FThreadID;
    Property Input  : TOutputPipeStream Read FInputStream;
    Property Output : TInputPipeStream  Read FOutputStream;
    Property Stderr : TinputPipeStream  Read FStderrStream;
    Property ExitStatus : Integer Read GetExitStatus;
    Property InheritHandles : Boolean Read FInheritHandles Write FInheritHandles;
  Published
    Property Active : Boolean Read GetRunning Write SetActive;
    Property ApplicationName : String Read FApplicationName Write SetApplicationName;
    Property CommandLine : String Read FCommandLine Write FCommandLine;
    Property ConsoleTitle : String Read FConsoleTitle Write FConsoleTitle;
    Property CurrentDirectory : String Read FCurrentDirectory Write FCurrentDirectory;
    Property Desktop : String Read FDesktop Write FDesktop;
    Property Environment : TStrings Read FEnvironment Write SetEnvironment;
    Property Options : TProcessOptions Read FProcessOptions Write SetProcessOptions;
    Property Priority : TProcessPriority Read FProcessPriority Write FProcessPriority;
    Property StartupOptions : TStartupOptions Read FStartupOptions Write FStartupOptions;
    Property Running : Boolean Read GetRunning;
    Property ShowWindow : TShowWindowOptions Read FShowWindow Write SetShowWindow;
    Property WindowColumns : Cardinal Read dwXCountChars Write SetWindowColumns;
    Property WindowHeight : Cardinal Read dwYSize Write SetWindowHeight;
    Property WindowLeft : Cardinal Read dwX Write SetWindowLeft;
    Property WindowRows : Cardinal Read dwYCountChars Write SetWindowRows;
    Property WindowTop : Cardinal Read dwY Write SetWindowTop ;
    Property WindowWidth : Cardinal Read dwXSize Write SetWindowWidth;
    Property FillAttribute : Cardinal read FFillAttribute Write FFillAttribute;
  end;
  
  EProcess = Class(Exception);

implementation

{ $ifdef WINDOWS}
Uses
  Windows;
{ $endif WINDOWS}
{$ifdef UNIX}
uses
   Unix,
   Baseunix;
{$endif UNIX}

Resourcestring
  SNoCommandLine = 'Cannot execute empty command-line';
  SErrNoSuchProgram = 'Executable not found: "%s"';

{$i process.inc}

Constructor TProcess.Create (AOwner : TComponent);
begin
  Inherited;
  FProcessPriority:=ppNormal;
  FShowWindow:=swoNone;
  FInheritHandles:=True;
  FEnvironment:=TStringList.Create;
end;

Destructor TProcess.Destroy;

begin
  FEnvironment.Free;
  FreeStreams;
  CloseProcessHandles;
  Inherited Destroy;
end;

Procedure TProcess.FreeStreams;
begin
  If FStderrStream<>FOutputStream then
    FreeStream(THandleStream(FStderrStream));
  FreeStream(THandleStream(FOutputStream));
  FreeStream(THandleStream(FInputStream));
end;


Function TProcess.GetExitStatus : Integer;

begin
  If FRunning then
    PeekExitStatus;
  Result:=FExitCode;
end;


Function TProcess.GetRunning : Boolean;

begin
  IF FRunning then
    FRunning:=Not PeekExitStatus;
  Result:=FRunning;
end;


Procedure TProcess.CreateStreams(InHandle,OutHandle,ErrHandle : Longint);

begin
  FreeStreams;
  FInputStream:=TOutputPipeStream.Create (InHandle);
  FOutputStream:=TInputPipeStream.Create (OutHandle);
  if Not (poStderrToOutput in FProcessOptions) then
    FStderrStream:=TInputPipeStream.Create(ErrHandle);
end;

procedure TProcess.FreeStream(var AStream: THandleStream);
begin
  if AStream = nil then exit;
  FileClose(AStream.Handle);
  FreeAndNil(AStream);
end;

procedure TProcess.CloseInput;
begin
  FreeStream(THandleStream(FInputStream));
end;

procedure TProcess.CloseOutput;
begin
  FreeStream(THandleStream(FOutputStream));
end;

procedure TProcess.CloseStderr;
begin
  FreeStream(THandleStream(FStderrStream));
end;

Procedure TProcess.SetWindowColumns (Value : Cardinal);

begin
  if Value<>0 then
    Include(FStartupOptions,suoUseCountChars);
  dwXCountChars:=Value;
end;


Procedure TProcess.SetWindowHeight (Value : Cardinal);

begin
  if Value<>0 then
    include(FStartupOptions,suoUsePosition);
  dwYSize:=Value;
end;

Procedure TProcess.SetWindowLeft (Value : Cardinal);

begin
  if Value<>0 then
    Include(FStartupOptions,suoUseSize);
  dwx:=Value;
end;

Procedure TProcess.SetWindowTop (Value : Cardinal);

begin
  if Value<>0 then
    Include(FStartupOptions,suoUsePosition);
  dwy:=Value;
end;

Procedure TProcess.SetWindowWidth (Value : Cardinal);
begin
  If (Value<>0) then
    Include(FStartupOptions,suoUseSize);
  dwXSize:=Value;
end;

Function TProcess.GetWindowRect : TRect;
begin
  With Result do
    begin
    Left:=dwx;
    Right:=dwx+dwxSize;
    Top:=dwy;
    Bottom:=dwy+dwysize;
    end;
end;

Procedure TProcess.SetWindowRect (Value : Trect);
begin
  Include(FStartupOptions,suoUseSize);
  Include(FStartupOptions,suoUsePosition);
  With Value do
    begin
    dwx:=Left;
    dwxSize:=Right-Left;
    dwy:=Top;
    dwySize:=Bottom-top;
    end;
end;


Procedure TProcess.SetWindowRows (Value : Cardinal);

begin
  if Value<>0 then
    Include(FStartupOptions,suoUseCountChars);
  dwYCountChars:=Value;
end;

procedure TProcess.SetApplicationName(const Value: String);
begin
  FApplicationName := Value;
  If (csDesigning in ComponentState) and
     (FCommandLine='') then
    FCommandLine:=Value;
end;

procedure TProcess.SetProcessOptions(const Value: TProcessOptions);
begin
  FProcessOptions := Value;
  If poNewConsole in FProcessOptions then
    Exclude(FProcessOptions,poNoConsole);
  if poRunSuspended in FProcessOptions then
    Exclude(FProcessOptions,poWaitOnExit);
end;

procedure TProcess.SetActive(const Value: Boolean);
begin
  if (Value<>GetRunning) then
    If Value then
      Execute
    else
      Terminate(0);
end;

procedure TProcess.SetEnvironment(const Value: TStrings);
begin
  FEnvironment.Assign(Value);
end;

end.

Changes to src/lib/NppPluginInclude.pas.

4
5
6
7
8
9
10

11
12



13
14
15
16
17
18
19
  DLL_PROCESS_ATTACH:
  begin
    // create the main object
    //Npp := TDbgpNppPlugin.Create;
  end;
  DLL_PROCESS_DETACH:
  begin

    if Assigned(Npp) then
      Npp.Destroy;



  end;
  //DLL_THREAD_ATTACH: MessageBeep(0);
  //DLL_THREAD_DETACH: MessageBeep(0);
  end;
end;

procedure setInfo(NppData: TNppData); cdecl; export;







>
|
|
>
>
>







4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
  DLL_PROCESS_ATTACH:
  begin
    // create the main object
    //Npp := TDbgpNppPlugin.Create;
  end;
  DLL_PROCESS_DETACH:
  begin
    try
      if Assigned(Npp) then
        Npp.Free;
    except
      ShowException(ExceptObject, ExceptAddr);
    end;
  end;
  //DLL_THREAD_ATTACH: MessageBeep(0);
  //DLL_THREAD_DETACH: MessageBeep(0);
  end;
end;

procedure setInfo(NppData: TNppData); cdecl; export;

Changes to src/lib/nppplugin.pas.

241
242
243
244
245
246
247





248
249
250
251
252
253
254
...
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732

	NPPM_SETBUFFERFORMAT = (NOTEPADPLUS_USER + 69);
	//wParam: BufferID to set format of
	//lParam: format
	//Returns TRUE on success, FALSE otherwise
	//use int, see formatType






  // http://sourceforge.net/p/notepad-plus/discussion/482781/thread/c430f474
  NPPM_GETLANGUAGENAME = (NOTEPADPLUS_USER + 83);
   // INT NPPM_GETLANGUAGENAME(int langType, TCHAR *langName)
   // Get programing language name from the given language type (LangType)
   // Return value is the number of copied character / number of character to copy (\0 is not included)
   // You should call this function 2 times - the first time you pass langName as NULL to get the number of characters to copy.
       // You allocate a buffer of the length of (the number of characters + 1) then call NPPM_GETLANGUAGENAME function the 2nd time
................................................................................
  // ask if we are not already opened
  SetLength(s, 500);
  {r := }SendMessage(self.NppData.NppHandle, NPPM_GETFULLCURRENTPATH, 0, LPARAM(PChar(s)));
  SetString(s, PChar(s), strlen(PChar(s)));
  Result := true;
  if (s = filename) then exit;
  r := SendMessage(self.NppData.NppHandle, WM_DOOPEN, 0, LPARAM(PChar(filename)));
  Result := (r=0);
end;

function TNppPlugin.DoOpen(filename: String; Line: Integer): boolean;
var
  r: boolean;
begin
  r := self.DoOpen(filename);







>
>
>
>
>







 







|







241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
...
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737

	NPPM_SETBUFFERFORMAT = (NOTEPADPLUS_USER + 69);
	//wParam: BufferID to set format of
	//lParam: format
	//Returns TRUE on success, FALSE otherwise
	//use int, see formatType

	NPPM_DOOPEN = (NOTEPADPLUS_USER + 77);
	// BOOL NPPM_DOOPEN(0, const TCHAR *fullPathName2Open)
	// fullPathName2Open indicates the full file path name to be opened.
	// The return value is TRUE (1) if the operation is successful, otherwise FALSE (0).

  // http://sourceforge.net/p/notepad-plus/discussion/482781/thread/c430f474
  NPPM_GETLANGUAGENAME = (NOTEPADPLUS_USER + 83);
   // INT NPPM_GETLANGUAGENAME(int langType, TCHAR *langName)
   // Get programing language name from the given language type (LangType)
   // Return value is the number of copied character / number of character to copy (\0 is not included)
   // You should call this function 2 times - the first time you pass langName as NULL to get the number of characters to copy.
       // You allocate a buffer of the length of (the number of characters + 1) then call NPPM_GETLANGUAGENAME function the 2nd time
................................................................................
  // ask if we are not already opened
  SetLength(s, 500);
  {r := }SendMessage(self.NppData.NppHandle, NPPM_GETFULLCURRENTPATH, 0, LPARAM(PChar(s)));
  SetString(s, PChar(s), strlen(PChar(s)));
  Result := true;
  if (s = filename) then exit;
  r := SendMessage(self.NppData.NppHandle, WM_DOOPEN, 0, LPARAM(PChar(filename)));
  Result := (r<>0);
end;

function TNppPlugin.DoOpen(filename: String; Line: Integer): boolean;
var
  r: boolean;
begin
  r := self.DoOpen(filename);

Changes to src/prj/PreviewHTML.dpr.

24
25
26
27
28
29
30
31

32
33
34
35
36
37
38
39
40
41
42
  NppDockingForms in '..\lib\NppDockingForms.pas' {NppDockingForm},
  U_Npp_PreviewHTML in '..\U_Npp_PreviewHTML.pas',
  F_About in '..\F_About.pas' {AboutForm},
  F_PreviewHTML in '..\F_PreviewHTML.pas' {HelloWorldDockingForm},
  WebBrowser in '..\lib\WebBrowser.pas',
  L_VersionInfoW in '..\common\L_VersionInfoW.pas',
  L_SpecialFolders in '..\common\L_SpecialFolders.pas',
  RegExpr in '..\common\RegExpr.pas';


{$R *.res}

{$Include '..\lib\NppPluginInclude.pas'}

begin
  { First, assign the procedure to the DLLProc variable }
  DllProc := @DLLEntryPoint;
  { Now invoke the procedure to reflect that the DLL is attaching to the process }
  DLLEntryPoint(DLL_PROCESS_ATTACH);
end.







|
>











24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
  NppDockingForms in '..\lib\NppDockingForms.pas' {NppDockingForm},
  U_Npp_PreviewHTML in '..\U_Npp_PreviewHTML.pas',
  F_About in '..\F_About.pas' {AboutForm},
  F_PreviewHTML in '..\F_PreviewHTML.pas' {HelloWorldDockingForm},
  WebBrowser in '..\lib\WebBrowser.pas',
  L_VersionInfoW in '..\common\L_VersionInfoW.pas',
  L_SpecialFolders in '..\common\L_SpecialFolders.pas',
  RegExpr in '..\common\RegExpr.pas',
  U_CustomFilter in '..\U_CustomFilter.pas';

{$R *.res}

{$Include '..\lib\NppPluginInclude.pas'}

begin
  { First, assign the procedure to the DLLProc variable }
  DllProc := @DLLEntryPoint;
  { Now invoke the procedure to reflect that the DLL is attaching to the process }
  DLLEntryPoint(DLL_PROCESS_ATTACH);
end.

Changes to src/prj/PreviewHTML.dproj.

24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
..
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96

97
98
99
100
101
102
103
104
105
...
120
121
122
123
124
125
126

127
128
129
130
131
132
133
...
223
224
225
226
227
228
229
230








231
			<Base>true</Base>
		</PropertyGroup>
		<PropertyGroup Condition="'$(Config)'=='Release' or '$(Cfg_1)'!=''">
			<Cfg_1>true</Cfg_1>
			<CfgParent>Base</CfgParent>
			<Base>true</Base>
		</PropertyGroup>
		<PropertyGroup Condition="('$(Platform)'=='Win32' and '$(Cfg_1)'=='true') or '$(Cfg_1_Win32)'!=''">
			<Cfg_1_Win32>true</Cfg_1_Win32>
			<CfgParent>Cfg_1</CfgParent>
			<Cfg_1>true</Cfg_1>
			<Base>true</Base>
		</PropertyGroup>
		<PropertyGroup Condition="'$(Config)'=='Debug' or '$(Cfg_2)'!=''">
			<Cfg_2>true</Cfg_2>
			<CfgParent>Base</CfgParent>
			<Base>true</Base>
		</PropertyGroup>
		<PropertyGroup Condition="('$(Platform)'=='Win32' and '$(Cfg_2)'=='true') or '$(Cfg_2_Win32)'!=''">
			<Cfg_2_Win32>true</Cfg_2_Win32>
			<CfgParent>Cfg_2</CfgParent>
			<Cfg_2>true</Cfg_2>
			<Base>true</Base>
		</PropertyGroup>
		<PropertyGroup Condition="'$(Base)'!=''">
			<VerInfo_MinorVer>2</VerInfo_MinorVer>
			<VerInfo_Release>1</VerInfo_Release>
			<PostBuildEventCancelOnError>false</PostBuildEventCancelOnError>
			<PostBuildEvent><![CDATA["C:\MC\Run\Util\Compression\UPX\upx.exe" --best -q "$(OUTPUTPATH)"
$(PostBuildEvent)]]></PostBuildEvent>
			<VerInfo_AutoGenVersion>true</VerInfo_AutoGenVersion>
			<VerInfo_DLL>true</VerInfo_DLL>
			<DCC_ImageBase>00400000</DCC_ImageBase>
			<VerInfo_Keys>CompanyName=Voronwë;FileDescription=HTML Preview plugin for Notepad++;FileVersion=1.2.0.0;InternalName=PreviewHTML;LegalCopyright=© Martijn Coppoolse;LegalTrademarks=;OriginalFilename=PreviewHTML.dll;ProductName=Notepad++;ProductVersion=6.0.0.0;Comments=http://martijn.coppoolse.com/software</VerInfo_Keys>
			<DCC_UsePackage>vcl;rtl;dbrtl;adortl;vcldb;vclx;bdertl;vcldbx;ibxpress;dsnap;cds;bdecds;qrpt;teeui;teedb;tee;dss;teeqr;visualclx;visualdbclx;dsnapcrba;dsnapcon;VclSmp;vclshlctrls;vclie;xmlrtl;inet;inetdbbde;inetdbxpress;inetdb;nmfast;webdsnap;websnap;soaprtl;dbexpress;dbxcds;indy;dclOffice2k;dOCI6;CoolTrayIcon_D6plus;curlpkg;ThemeManagerD6;VirtualTreesD6;Jcl;JclVcl;JvCoreD6R;JvSystemD6R;JvStdCtrlsD6R;JvAppFrmD6R;JvBandsD6R;JvDBD6R;JvDlgsD6R;JvBDED6R;JvCmpD6R;JvCryptD6R;JvCtrlsD6R;JvCustomD6R;JvDockingD6R;JvDotNetCtrlsD6R;JvEDID6R;JvGlobusD6R;JvHMID6R;JvInterpreterD6R;JvJansD6R;JvManagedThreadsD6R;JvMMD6R;JvNetD6R;JvPageCompsD6R;JvPluginD6R;JvPrintPreviewD6R;JvRuntimeDesignD6R;JvTimeFrameworkD6R;JvUIBD6R;JvValidatorsD6R;JvWizardD6R;JvXPCtrlsD6R;$(DCC_UsePackage)</DCC_UsePackage>
			<DCC_ExeOutput>..\..\out\$(PLATFORM)\$(CONFIG)</DCC_ExeOutput>
			<DCC_Define>NPPUNICODE;$(DCC_Define)</DCC_Define>
			<DCC_Namespace>System;Xml;Data;Datasnap;Web;Soap;Winapi;Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;$(DCC_Namespace)</DCC_Namespace>
			<VerInfo_IncludeVerInfo>true</VerInfo_IncludeVerInfo>
			<DCC_UnitSearchPath>$(DELPHI)\Lib\Debug;C:\PROGRA~1\Jedi\jcl\lib\d6\debug;$(DCC_UnitSearchPath)</DCC_UnitSearchPath>
			<Manifest_File>$(BDS)\bin\default_app.manifest</Manifest_File>
			<DCC_N>true</DCC_N>
			<DCC_K>false</DCC_K>
			<GenDll>true</GenDll>
			<DCC_S>false</DCC_S>
			<DCC_F>false</DCC_F>
			<VerInfo_Locale>1033</VerInfo_Locale>
................................................................................
		</PropertyGroup>
		<PropertyGroup Condition="'$(Cfg_1)'!=''">
			<DCC_LocalDebugSymbols>false</DCC_LocalDebugSymbols>
			<DCC_DebugInformation>false</DCC_DebugInformation>
			<DCC_SymbolReferenceInfo>0</DCC_SymbolReferenceInfo>
			<DCC_Define>RELEASE;$(DCC_Define)</DCC_Define>
		</PropertyGroup>
		<PropertyGroup Condition="'$(Cfg_1_Win32)'!=''">
			<VerInfo_Keys>CompanyName=Voronwë;FileDescription=HTML Preview plugin for Notepad++;FileVersion=1.2.1.0;InternalName=PreviewHTML;LegalCopyright=© Martijn Coppoolse;LegalTrademarks=;OriginalFilename=PreviewHTML.dll;ProductName=Notepad++;ProductVersion=6.0.0.0;Comments=http://martijn.coppoolse.com/software</VerInfo_Keys>
		</PropertyGroup>
		<PropertyGroup Condition="'$(Cfg_2)'!=''">
			<DCC_SymbolReferenceInfo>2</DCC_SymbolReferenceInfo>
			<DCC_Define>DEBUG;$(DCC_Define)</DCC_Define>
			<DCC_Optimize>false</DCC_Optimize>
			<DCC_GenerateStackFrames>true</DCC_GenerateStackFrames>
		</PropertyGroup>
		<PropertyGroup Condition="'$(Cfg_2_Win32)'!=''">

			<VerInfo_Debug>true</VerInfo_Debug>
			<VerInfo_Keys>CompanyName=Voronwë;FileDescription=HTML Preview plugin for Notepad++;FileVersion=1.2.1.0;InternalName=PreviewHTML;LegalCopyright=© Martijn Coppoolse;LegalTrademarks=;OriginalFilename=PreviewHTML.dll;ProductName=Notepad++;ProductVersion=6.0.0.0;Comments=http://martijn.coppoolse.com/software</VerInfo_Keys>
			<PostBuildEvent><![CDATA[]]></PostBuildEvent>
		</PropertyGroup>
		<ItemGroup>
			<DelphiCompile Include="$(MainSource)">
				<MainSource>MainSource</MainSource>
			</DelphiCompile>
			<RcCompile Include="PreviewHTML_TB.rc">
................................................................................
			<DCCReference Include="..\F_PreviewHTML.pas">
				<Form>HelloWorldDockingForm</Form>
			</DCCReference>
			<DCCReference Include="..\lib\WebBrowser.pas"/>
			<DCCReference Include="..\common\L_VersionInfoW.pas"/>
			<DCCReference Include="..\common\L_SpecialFolders.pas"/>
			<DCCReference Include="..\common\RegExpr.pas"/>

			<BuildConfiguration Include="Debug">
				<Key>Cfg_2</Key>
				<CfgParent>Base</CfgParent>
			</BuildConfiguration>
			<BuildConfiguration Include="Base">
				<Key>Base</Key>
			</BuildConfiguration>
................................................................................
		<PropertyGroup Condition="'$(Config)'=='Debug' And '$(Platform)'=='OSX32'">
			<PreBuildEvent/>
			<PreBuildEventIgnoreExitCode>False</PreBuildEventIgnoreExitCode>
			<PreLinkEvent/>
			<PreLinkEventIgnoreExitCode>False</PreLinkEventIgnoreExitCode>
			<PostBuildEvent>&quot;C:\MC\Run\Util\Compression\UPX\upx.exe&quot; --best -q &quot;$(OUTPUTPATH)&quot; </PostBuildEvent>
			<PostBuildEventIgnoreExitCode>True</PostBuildEventIgnoreExitCode>
		</PropertyGroup>








	</Project>







<
<
<
<
<
<












|
<






|





|







 







<
<
<







>

|







 







>







 








>
>
>
>
>
>
>
>

24
25
26
27
28
29
30






31
32
33
34
35
36
37
38
39
40
41
42
43

44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
..
73
74
75
76
77
78
79



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
...
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
...
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
			<Base>true</Base>
		</PropertyGroup>
		<PropertyGroup Condition="'$(Config)'=='Release' or '$(Cfg_1)'!=''">
			<Cfg_1>true</Cfg_1>
			<CfgParent>Base</CfgParent>
			<Base>true</Base>
		</PropertyGroup>






		<PropertyGroup Condition="'$(Config)'=='Debug' or '$(Cfg_2)'!=''">
			<Cfg_2>true</Cfg_2>
			<CfgParent>Base</CfgParent>
			<Base>true</Base>
		</PropertyGroup>
		<PropertyGroup Condition="('$(Platform)'=='Win32' and '$(Cfg_2)'=='true') or '$(Cfg_2_Win32)'!=''">
			<Cfg_2_Win32>true</Cfg_2_Win32>
			<CfgParent>Cfg_2</CfgParent>
			<Cfg_2>true</Cfg_2>
			<Base>true</Base>
		</PropertyGroup>
		<PropertyGroup Condition="'$(Base)'!=''">
			<VerInfo_MinorVer>3</VerInfo_MinorVer>

			<PostBuildEventCancelOnError>false</PostBuildEventCancelOnError>
			<PostBuildEvent><![CDATA["C:\MC\Run\Util\Compression\UPX\upx.exe" --best -q "$(OUTPUTPATH)"
$(PostBuildEvent)]]></PostBuildEvent>
			<VerInfo_AutoGenVersion>true</VerInfo_AutoGenVersion>
			<VerInfo_DLL>true</VerInfo_DLL>
			<DCC_ImageBase>00400000</DCC_ImageBase>
			<VerInfo_Keys>CompanyName=Voronwë;FileDescription=HTML Preview plugin for Notepad++;FileVersion=1.3.0.0;InternalName=PreviewHTML;LegalCopyright=© Martijn Coppoolse;LegalTrademarks=;OriginalFilename=PreviewHTML.dll;ProductName=Notepad++;ProductVersion=6.0.0.0;Comments=http://martijn.coppoolse.com/software</VerInfo_Keys>
			<DCC_UsePackage>vcl;rtl;dbrtl;adortl;vcldb;vclx;bdertl;vcldbx;ibxpress;dsnap;cds;bdecds;qrpt;teeui;teedb;tee;dss;teeqr;visualclx;visualdbclx;dsnapcrba;dsnapcon;VclSmp;vclshlctrls;vclie;xmlrtl;inet;inetdbbde;inetdbxpress;inetdb;nmfast;webdsnap;websnap;soaprtl;dbexpress;dbxcds;indy;dclOffice2k;dOCI6;CoolTrayIcon_D6plus;curlpkg;ThemeManagerD6;VirtualTreesD6;Jcl;JclVcl;JvCoreD6R;JvSystemD6R;JvStdCtrlsD6R;JvAppFrmD6R;JvBandsD6R;JvDBD6R;JvDlgsD6R;JvBDED6R;JvCmpD6R;JvCryptD6R;JvCtrlsD6R;JvCustomD6R;JvDockingD6R;JvDotNetCtrlsD6R;JvEDID6R;JvGlobusD6R;JvHMID6R;JvInterpreterD6R;JvJansD6R;JvManagedThreadsD6R;JvMMD6R;JvNetD6R;JvPageCompsD6R;JvPluginD6R;JvPrintPreviewD6R;JvRuntimeDesignD6R;JvTimeFrameworkD6R;JvUIBD6R;JvValidatorsD6R;JvWizardD6R;JvXPCtrlsD6R;$(DCC_UsePackage)</DCC_UsePackage>
			<DCC_ExeOutput>..\..\out\$(PLATFORM)\$(CONFIG)</DCC_ExeOutput>
			<DCC_Define>NPPUNICODE;$(DCC_Define)</DCC_Define>
			<DCC_Namespace>System;Xml;Data;Datasnap;Web;Soap;Winapi;Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;$(DCC_Namespace)</DCC_Namespace>
			<VerInfo_IncludeVerInfo>true</VerInfo_IncludeVerInfo>
			<DCC_UnitSearchPath>$(DELPHI)\Lib\Debug;C:\PROGRA~1\Jedi\jcl\lib\d6\debug;..\;..\lib;..\common;$(DCC_UnitSearchPath)</DCC_UnitSearchPath>
			<Manifest_File>$(BDS)\bin\default_app.manifest</Manifest_File>
			<DCC_N>true</DCC_N>
			<DCC_K>false</DCC_K>
			<GenDll>true</GenDll>
			<DCC_S>false</DCC_S>
			<DCC_F>false</DCC_F>
			<VerInfo_Locale>1033</VerInfo_Locale>
................................................................................
		</PropertyGroup>
		<PropertyGroup Condition="'$(Cfg_1)'!=''">
			<DCC_LocalDebugSymbols>false</DCC_LocalDebugSymbols>
			<DCC_DebugInformation>false</DCC_DebugInformation>
			<DCC_SymbolReferenceInfo>0</DCC_SymbolReferenceInfo>
			<DCC_Define>RELEASE;$(DCC_Define)</DCC_Define>
		</PropertyGroup>



		<PropertyGroup Condition="'$(Cfg_2)'!=''">
			<DCC_SymbolReferenceInfo>2</DCC_SymbolReferenceInfo>
			<DCC_Define>DEBUG;$(DCC_Define)</DCC_Define>
			<DCC_Optimize>false</DCC_Optimize>
			<DCC_GenerateStackFrames>true</DCC_GenerateStackFrames>
		</PropertyGroup>
		<PropertyGroup Condition="'$(Cfg_2_Win32)'!=''">
			<VerInfo_Release>0</VerInfo_Release>
			<VerInfo_Debug>true</VerInfo_Debug>
			<VerInfo_Keys>CompanyName=Voronwë;FileDescription=HTML Preview plugin for Notepad++;FileVersion=1.3.0.0;InternalName=PreviewHTML;LegalCopyright=© Martijn Coppoolse;LegalTrademarks=;OriginalFilename=PreviewHTML.dll;ProductName=Notepad++;ProductVersion=6.0.0.0;Comments=http://martijn.coppoolse.com/software</VerInfo_Keys>
			<PostBuildEvent><![CDATA[]]></PostBuildEvent>
		</PropertyGroup>
		<ItemGroup>
			<DelphiCompile Include="$(MainSource)">
				<MainSource>MainSource</MainSource>
			</DelphiCompile>
			<RcCompile Include="PreviewHTML_TB.rc">
................................................................................
			<DCCReference Include="..\F_PreviewHTML.pas">
				<Form>HelloWorldDockingForm</Form>
			</DCCReference>
			<DCCReference Include="..\lib\WebBrowser.pas"/>
			<DCCReference Include="..\common\L_VersionInfoW.pas"/>
			<DCCReference Include="..\common\L_SpecialFolders.pas"/>
			<DCCReference Include="..\common\RegExpr.pas"/>
			<DCCReference Include="..\U_CustomFilter.pas"/>
			<BuildConfiguration Include="Debug">
				<Key>Cfg_2</Key>
				<CfgParent>Base</CfgParent>
			</BuildConfiguration>
			<BuildConfiguration Include="Base">
				<Key>Base</Key>
			</BuildConfiguration>
................................................................................
		<PropertyGroup Condition="'$(Config)'=='Debug' And '$(Platform)'=='OSX32'">
			<PreBuildEvent/>
			<PreBuildEventIgnoreExitCode>False</PreBuildEventIgnoreExitCode>
			<PreLinkEvent/>
			<PreLinkEventIgnoreExitCode>False</PreLinkEventIgnoreExitCode>
			<PostBuildEvent>&quot;C:\MC\Run\Util\Compression\UPX\upx.exe&quot; --best -q &quot;$(OUTPUTPATH)&quot; </PostBuildEvent>
			<PostBuildEventIgnoreExitCode>True</PostBuildEventIgnoreExitCode>
		</PropertyGroup>
		<PropertyGroup Condition="'$(Config)'=='Debug' And '$(Platform)'=='Win32'">
			<PreBuildEvent/>
			<PreBuildEventIgnoreExitCode>False</PreBuildEventIgnoreExitCode>
			<PreLinkEvent/>
			<PreLinkEventIgnoreExitCode>False</PreLinkEventIgnoreExitCode>
			<PostBuildEvent><![CDATA[]]></PostBuildEvent>
			<PostBuildEventIgnoreExitCode>True</PostBuildEventIgnoreExitCode>
		</PropertyGroup>
	</Project>