Check-in [78558a28f2]
Not logged in

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

Overview
Comment:The filter thread does seem to synchronize properly if we wait for it in the main thread, effectively undoing all advantage of threading. We'll go with it for now [8988ba50dd]. Show a waiting cursor while the filter is running, and write the name of the filter in the status bar at that time as well. DetermineFilterName now accepts multiple extensions (separated by commas). It now determines the document's language type name correctly, and it also does this only once per run (if necessary). Added two menu options, to open the settings.ini and the filters.ini.
Timelines: family | ancestors | descendants | both | v1.3
Files: files | file ages | folders
SHA1:78558a28f20e418ae1d939b9ad024e811cb99847
User & Date: Martijn 2013-02-15 22:28:51
Context
2013-03-03
12:44
Get rid of ProcessMessages/Sleep combo in RestoreScrollPos. check-in: 532daeeda1 user: Martijn tags: v1.3
2013-02-15
22:28
The filter thread does seem to synchronize properly if we wait for it in the main thread, effectively undoing all advantage of threading. We'll go with it for now [8988ba50dd]. Show a waiting cursor while the filter is running, and write the name of the filter in the status bar at that time as well. DetermineFilterName now accepts multiple extensions (separated by commas). It now determines the document's language type name correctly, and it also does this only once per run (if necessary). Added two menu options, to open the settings.ini and the filters.ini. check-in: 78558a28f2 user: Martijn tags: v1.3
22:20
TNppPlugin.DoOpen returned the wrong value. Added exception handling to the detach routine. check-in: 58b267bd46 user: Martijn tags: v1.3
Changes

Changes to src/F_PreviewHTML.pas.

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
...
251
252
253
254
255
256
257







258
259
260
261
262
263
264
...
344
345
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
...
450
451
452
453
454
455
456

457
458
459
460
461
462
463
...
649
650
651
652
653
654
655
656


657
658
659
660
661
662
663
664
665
666
  FilterName: string;
begin
  if chkFreeze.Checked then
    Exit;

  try
    tmrAutorefresh.Enabled := False;
    SaveScrollPos;

    if Assigned(FFilterThread) then begin
ODS('FreeAndNil(FFilterThread);');
      FreeAndNil(FFilterThread);
    end;


    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
//MessageBox(Npp.NppData.NppHandle, PChar(Format('FilterName: %s', [FilterName])), 'PreviewHTML', MB_ICONINFORMATION);

      if ExecuteCustomFilter(FilterName, HTML, BufferID) then begin
        Exit;
      end else begin

        HTML := '<pre style="color: darkred">ExecuteCustomFilter returned False</pre>';
      end;
    end else if IsXML then begin
      HTML := TransformXMLToHTML(HTML);
    end;

    DisplayPreview(HTML, BufferID);



  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 else begin
        hScintilla := Self.Npp.NppData.ScintillaSecondHandle;
      end;
      SendMessage(hScintilla, SCI_GRABFOCUS, 0, 0);
    end else begin
      self.UpdateDisplayInfo('');
    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;
................................................................................
function TfrmHTMLPreview.DetermineCustomFilter: string;
var
  DocFileName: nppString;
  Filters: TIniFile;
  Names: TStringList;
  i: Integer;
  Match: Boolean;
  Extension, Language, DocLanguage: string;
  DocLangType, LangType: Integer;

begin
  DocFileName := StringOfChar(#0, MAX_PATH);
  SendMessage(Npp.NppData.NppHandle, NPPM_GETFILENAME, WPARAM(Length(DocFileName)), LPARAM(nppPChar(DocFileName)));
  DocFileName := nppString(nppPChar(DocFileName));




  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





      Match := False;

      {$MESSAGE HINT 'TODO: Test entire file name  MCO 22-01-2013'}

      {--- MCO 22-01-2013: Test extension ---}
      Extension := Filters.ReadString(Names[i], 'Extension', '');
      if (Extension <> '') and SameFileName(Extension, ExtractFileExt(DocFileName)) then begin
        Match := True;








      end;

      {--- MCO 22-01-2013: Test highlighter language ---}
      Language := Filters.ReadString(Names[i], 'Language', '');
      if Language <> '' then begin
        DocLangType := -1;
        SendMessage(Npp.NppData.NppHandle, NPPM_GETCURRENTLANGTYPE, WPARAM(0), LPARAM(@DocLangType));

        if DocLangType > -1 then begin
          if TryStrToInt(Language, LangType) and (LangType = DocLangType) then begin
            Match := True;
          end else begin

            SetLength(DocLanguage, SendMessage(Npp.NppData.NppHandle, NPPM_GETLANGUAGENAME, WPARAM(LangType), LPARAM(nil)));
            SetLength(DocLanguage, SendMessage(Npp.NppData.NppHandle, NPPM_GETLANGUAGENAME, WPARAM(LangType), LPARAM(PChar(DocLanguage))));
            if SameText(Language, DocLanguage) then begin
              Match := True;
            end else begin
              SetLength(DocLanguage, SendMessage(Npp.NppData.NppHandle, NPPM_GETLANGUAGEDESC, WPARAM(LangType), LPARAM(nil)));
              SetLength(DocLanguage, SendMessage(Npp.NppData.NppHandle, NPPM_GETLANGUAGEDESC, WPARAM(LangType), LPARAM(PChar(DocLanguage))));
              if SameText(Language, DocLanguage) then
                Match := True;
            end;
          end;
        end;
      end;

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

................................................................................
  end;

  FilterData.OnTerminate := FilterThreadTerminate;

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

end {TfrmHTMLPreview.ExecuteCustomFilter};

{ ------------------------------------------------------------------------------------------------ }
procedure TfrmHTMLPreview.FilterThreadTerminate(Sender: TObject);
begin
ODS('FilterThreadTerminate');
if (Sender as TThread).FatalException is Exception then
................................................................................
  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.







<
<




>













>
>
|
|
|

|

|
|
|
|
|
|
|

|

>
|
|
|
>
|
|
|
|
|

|
>
>
>







 







>
>
>
>
>
>
>







 







|

>




>
>
>







>
>
>
>
>





|
|
|
>
>
>
>
>
>
>
>





|
|
>




>
|
|
<
|
<
<
<
|
|







 







>







 







|
>
>










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
...
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
...
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
...
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
...
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
  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 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;
................................................................................
function TfrmHTMLPreview.DetermineCustomFilter: string;
var
  DocFileName: nppString;
  Filters: TIniFile;
  Names: TStringList;
  i: Integer;
  Match: Boolean;
  Ext, Language, DocLanguage: string;
  DocLangType, LangType: Integer;
  Extensions: TStringList;
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;

      {$MESSAGE HINT 'TODO: Test entire file name  MCO 22-01-2013'}

      {--- 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 := 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'}

................................................................................
  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
................................................................................
  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.

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
  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';







>
|

<
>
>








>
>







 







>
>
>
>
>









>
|
>
>
>

>
>
>
>
>
>







 







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







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
  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';