File: idehelpintf.pas

package info (click to toggle)
lazarus 2.0.0%2Bdfsg-2
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 214,460 kB
  • sloc: pascal: 1,862,622; xml: 265,709; cpp: 56,595; sh: 3,008; java: 609; makefile: 535; perl: 297; sql: 222; ansic: 137
file content (482 lines) | stat: -rw-r--r-- 15,824 bytes parent folder | download | duplicates (2)
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
342
343
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
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
{  $Id: helpintf.pas 9271 2006-05-13 12:00:43Z mattias $  }
{
 *****************************************************************************
  See the file COPYING.modifiedLGPL.txt, included in this distribution,
  for details about the license.
 *****************************************************************************

  Author: Mattias Gaertner

  Abstract:
    This unit defines various base classes for the Help System used by the IDE.
}
unit IDEHelpIntf;

{$mode objfpc}{$H+}

interface

uses
  Classes, Types, SysUtils,
  // LCL
  LMessages, LCLType, LCLIntf, Forms, Controls, Graphics, HelpIntfs, LazHelpIntf,
  // IdeIntf
  TextTools;

type
  { THelpDBIRegExprMessage
    Help registration item for matching a message (e.g. a fpc warning) with
    a regular expression.
    For example a line like
      "/usr/share/lazarus/components/synedit/syneditkeycmds.pp(532,10) Warning: Function result does not seem to be set"
     could be matched with
      Expression=') Warning: Function result does not seem to be set'
    }

  THelpDBIRegExprMessage = class(THelpDBIMessage)
  private
    FExpression: string;
    FModifierStr: string;
  public
    constructor Create(TheNode: THelpNode; const RegularExpression,
                       TheModifierStr: string);
    function MessageMatches(const TheMessage: string; {%H-}MessageParts: TStrings
                            ): boolean; override;
    property Expression: string read FExpression write FExpression;
    property ModifierStr: string read FModifierStr write FModifierStr;
  end;

  TIDEHelpManagerCreateHintFlag = (
    ihmchAddFocusHint
    );
  TIDEHelpManagerCreateHintFlags = set of TIDEHelpManagerCreateHintFlag;

  { TBaseHelpManager }

  TBaseHelpManager = class(TComponent)
  private
    FCombineSameIdentifiersInUnit: boolean;
    FShowCodeBrowserOnUnknownIdentifier: boolean;
  public
    procedure ConnectMainBarEvents; virtual; abstract;
    procedure LoadHelpOptions; virtual; abstract;
    procedure SaveHelpOptions; virtual; abstract;

    function ShowHelpForSourcePosition(const Filename: string;
                                       const CodePos: TPoint;
                                       var ErrMsg: string): TShowHelpResult; virtual; abstract;
    procedure ShowHelpForMessage; virtual; abstract;
    procedure ShowHelpForObjectInspector(Sender: TObject); virtual; abstract;
    procedure ShowHelpForIDEControl(Sender: TControl); virtual; abstract;
    function GetHintForSourcePosition(const ExpandedFilename: string;
      const CodePos: TPoint; out BaseURL, HTMLHint: string;
      Flags: TIDEHelpManagerCreateHintFlags = []): TShowHelpResult; virtual; abstract;
    function ConvertSourcePosToPascalHelpContext(const CaretPos: TPoint;
      const Filename: string): TPascalHelpContextList; virtual; abstract;
    // fpdoc
    function GetFPDocFilenameForSource(SrcFilename: string;
      ResolveIncludeFiles: Boolean;
      out AnOwner: TObject// a package or a project or LazarusHelp or nil for user defined
      ): string; virtual; abstract;

    property CombineSameIdentifiersInUnit: boolean
      read FCombineSameIdentifiersInUnit write FCombineSameIdentifiersInUnit;
    property ShowCodeBrowserOnUnknownIdentifier: boolean
      read FShowCodeBrowserOnUnknownIdentifier write FShowCodeBrowserOnUnknownIdentifier;
  end;
  

var
  LazarusHelp: TBaseHelpManager; // initialized by the IDE
  FPCMessagesHelpDB: THelpDatabase; // initialized by the IDE

type
  { TIDEHTMLControlIntf }

  TIDEHTMLControlIntf = interface
    function GetURL: string;
    procedure SetURL(const AValue: string);
    property URL: string read GetURL write SetURL;
    procedure SetHTMLContent(Stream: TStream; const NewURL: string = '');
    procedure GetPreferredControlSize(out AWidth, AHeight: integer);
  end;

  { TAbstractIDEHTMLProvider
    An instance of this class connects 3 parts:
     1. IDE html files  (via implementation)
     2. a html viewer control (via ControlIntf)
     3. IDE or designtime package code
    All three can communicate. }

  TAbstractIDEHTMLProvider = class(TComponent)
  protected
    FBaseURL: string;
    FControlIntf: TIDEHTMLControlIntf;
    procedure SetBaseURL(const AValue: string); virtual;
  public
    constructor Create(TheOwner: TComponent); override;
    destructor Destroy; override;
    function URLHasStream(const URL: string): boolean; virtual; abstract;
      { Open a URL asynchronously
        The standard IDE implementation supports the following for OpenURLAsync:
        source://local-file-name   : open a file local-file-name in the source editor
        openpackage://package-name : open a package editor
        fpdoc://#package-name.unitname.element : this opens the help for the fpdoc entry
        }
    procedure OpenURLAsync(const URL: string); virtual; abstract;
    function GetStream(const URL: string; Shared: boolean
      ): TStream; virtual; abstract; { Shared=true: provider assumes ownership
                  of returned TStream and increases internal reference count.
                  If not found it raises an exception.
                  Shared=false: caller must free stream}
    procedure ReleaseStream(const URL: string); virtual; abstract;
    property BaseURL: string read FBaseURL write SetBaseURL;// fallback for relative URLs
    function MakeURLAbsolute(const aBaseURL, aURL: string): string; virtual;
    property ControlIntf: TIDEHTMLControlIntf read FControlIntf write FControlIntf;
  end;

  TIDEHTMLControlFlag = (
    ihcScrollable,
    ihcWithClipboardMenu
  );
  TIDEHTMLControlFlags = set of TIDEHTMLControlFlag;

  TCreateIDEHTMLControlEvent =
    function(Owner: TComponent; var Provider: TAbstractIDEHTMLProvider;
             Flags: TIDEHTMLControlFlags = []): TControl;
  TCreateIDEHTMLProviderEvent =
    function(Owner: TComponent): TAbstractIDEHTMLProvider;


  { TSolidHintWindowRendered }

  TSolidHintWindowRendered = class(THintWindowRendered)
  protected
    procedure WMNCHitTest(var Message: TLMessage); message LM_NCHITTEST;
    procedure KeyDown(var Key: Word; Shift: TShiftState); override;
  public
    constructor Create(AOwner: TComponent); override;
  end;

  { THintWindowManager }

  THintWindowManager = class
  private
    // 2 HintWindows, one for simple text and one for rendered hint with child control.
    // Only one is visible at a time.
    FHintTextW: THintWindow;
    FHintRenderW: THintWindowRendered;
    FCurrentHintW: THintWindow;  // One of the windows or Nil.
    // Provider for the rendered hint.
    FHtmlHelpProvider: TAbstractIDEHTMLProvider;
    FBaseURL: string;
    FFlags: TIDEHTMLControlFlags;
    FOrigMousePos: TPoint;
    // These will be passed to HintWindow.
    FAutoHide: Boolean;
    FHideInterval: Integer;
    FOnMouseDown: TMouseEvent;
    FWindowName: string;
    function HtmlHelpProvider: TAbstractIDEHTMLProvider;
    function HintTextWindow: THintWindow;
    function HintRenderWindow: THintWindowRendered;
    procedure SetAutoHide(AValue: Boolean);
    procedure SetHideInterval(AValue: Integer);
    procedure SetOnMouseDown(AValue: TMouseEvent);
    procedure SetWindowName(AValue: string);
  protected
  public
    constructor Create; overload;
    destructor Destroy; override;
    function HintIsVisible: boolean;
    function ShowHint(ScreenPos: TPoint; TheHint: string; const MouseOffset: Boolean = True;
      HintFont: TFont = nil): boolean;
    procedure HideHint;
    procedure HideIfVisible;
  public
    property CurHintWindow: THintWindow read FCurrentHintW;
    property BaseURL: string read FBaseURL write FBaseURL;
    property Flags: TIDEHTMLControlFlags read FFlags write FFlags;
    property AutoHide: Boolean read FAutoHide write SetAutoHide;
    property HideInterval: Integer read FHideInterval write SetHideInterval;
    property OnMouseDown: TMouseEvent read FOnMouseDown write SetOnMouseDown;
    property WindowName: string read FWindowName write SetWindowName;
  end;

var
  CreateIDEHTMLControl: TCreateIDEHTMLControlEvent = nil;// will be set by the IDE
    // and can be overidden by a package like turbopoweriprodsgn.lpk
  CreateIDEHTMLProvider: TCreateIDEHTMLProviderEvent = nil;// will be set by the IDE

  FPCKeyWordHelpPrefix: string = 'FPCKeyword_';
  FPCDirectiveHelpPrefix: string = 'FPCDirective_';
  IDEDirectiveHelpPrefix: string = 'IDEDirective_';

implementation

{ TSolidHintWindowRendered }

procedure TSolidHintWindowRendered.WMNCHitTest(var Message: TLMessage);
begin
  Message.Result := HTCLIENT;
end;

procedure TSolidHintWindowRendered.KeyDown(var Key: Word; Shift: TShiftState);
Var
  AOldKey : Word;
begin
  AOldKey := Key;
  inherited KeyDown(Key, Shift);
  if AOldKey=VK_ESCAPE then
    Hide;
end;

constructor TSolidHintWindowRendered.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  KeyPreview := True;
end;

{ THelpDBIRegExprMessage }

constructor THelpDBIRegExprMessage.Create(TheNode: THelpNode;
  const RegularExpression, TheModifierStr: string);
begin
  Node:=TheNode;
  FExpression:=RegularExpression;
  FModifierStr:=TheModifierStr;
end;

function THelpDBIRegExprMessage.MessageMatches(const TheMessage: string;
  MessageParts: TStrings): boolean;
begin
  Result:=REMatches(TheMessage,Expression,ModifierStr);
  //writeln('THelpDBIRegExprMessage.MessageMatches TheMessage="',TheMessage,'" Expression="',Expression,'" Result=',Result);
end;

{ TAbstractIDEHTMLProvider }

procedure TAbstractIDEHTMLProvider.SetBaseURL(const AValue: string);
begin
  if FBaseURL=AValue then exit;
  FBaseURL:=AValue;
end;

constructor TAbstractIDEHTMLProvider.Create(TheOwner: TComponent);
begin
  inherited Create(TheOwner);
end;

destructor TAbstractIDEHTMLProvider.Destroy;
begin
  FControlIntf:=nil; // decrease reference count
  inherited Destroy;
end;

function TAbstractIDEHTMLProvider.MakeURLAbsolute(const aBaseURL, aURL: string): string;
var
  URLType: string;
  URLPath: string;
  URLParams: string;
begin
  Result:=aURL;
  SplitURL(aURL,URLType,URLPath,URLParams);
  //DebugLn(['TAbstractIDEHTMLProvider.BuildURL URL=',aURL,' URLType=',URLType,' URLPath=',URLPath,' URLParams=',URLParams]);
  if URLType='' then begin
    // no URLType => use aURL as URLPath
    Result:=aURL;
    if not URLFilenameIsAbsolute(Result) then begin
      if aBaseURL<>'' then
        Result:=aBaseURL+Result
      else
        Result:=BaseURL+Result;
    end;
  end else begin
    Result:=aURL;
  end;
end;

{ THintWindowManager }

constructor THintWindowManager.Create;
begin
  inherited Create;
  FFlags := [ihcWithClipboardMenu];
  FHideInterval := 3000;
end;

destructor THintWindowManager.Destroy;
begin
  FreeAndNil(FHintRenderW);
  FreeAndNil(FHintTextW);
  inherited Destroy;
end;

function THintWindowManager.HintTextWindow: THintWindow;
begin
  if FHintTextW = nil then
  begin
    FHintTextW := THintWindow.Create(Nil);
    FHintTextW.AutoHide := FAutoHide;
    FHintTextW.HideInterval := FHideInterval;
    FHintTextW.OnMouseDown := FOnMouseDown;
    if FWindowName <> '' then
      FHintTextW.Name := FWindowName;
  end;
  FCurrentHintW := FHintTextW;
  Result := FHintTextW;
end;

function THintWindowManager.HintRenderWindow: THintWindowRendered;
begin
  if FHintRenderW = nil then
  begin
    FHintRenderW := TSolidHintWindowRendered.Create(Nil);
    FHintRenderW.AutoHide := FAutoHide;
    FHintRenderW.HideInterval := FHideInterval;
    FHintRenderW.OnMouseDown := FOnMouseDown;
    if FWindowName <> '' then
      FHintRenderW.Name := FWindowName;
  end;
  FCurrentHintW := FHintRenderW;
  Result := FHintRenderW;
end;

function THintWindowManager.HintIsVisible: boolean;
begin
  Result := Assigned(FCurrentHintW) and FCurrentHintW.Visible;
end;

function THintWindowManager.HtmlHelpProvider: TAbstractIDEHTMLProvider;
var
  HelpControl: TControl;
begin
  if FHtmlHelpProvider = nil then
  begin
    //Include(FFlags, ihcScrollable);  // Debug (memo hint control does not work)
    HelpControl := CreateIDEHTMLControl(HintRenderWindow, FHtmlHelpProvider, FFlags);
    HelpControl.Parent := HintRenderWindow;
    HelpControl.Align := alClient;
  end;
  Result := FHtmlHelpProvider;
end;

function THintWindowManager.ShowHint(ScreenPos: TPoint; TheHint: string;
  const MouseOffset: Boolean; HintFont: TFont): boolean;

  procedure DoText;
  var
    HintWinRect: TRect;
  begin
    if HintFont<>nil then
      HintTextWindow.Font := HintFont;
    HintWinRect := HintTextWindow.CalcHintRect(Screen.Width, TheHint, Nil);
    HintTextWindow.HintRect := HintWinRect;      // Adds borders.
    if MouseOffset then
      HintTextWindow.OffsetHintRect(ScreenPos)
    else                   // shrink height only for fixed (no MouseOffset) hints
      HintTextWindow.OffsetHintRect(ScreenPos, 0, True, False);
    HintTextWindow.ActivateHint(TheHint);
  end;

  procedure DoHtml;
  var
    ms: TMemoryStream;
    NewWidth, NewHeight: integer;
    R1, R2: TRect;
  begin
    if HintFont<>nil then
      HintRenderWindow.Font := HintFont;
    HtmlHelpProvider.BaseURL:=FBaseURL;
    ms:=TMemoryStream.Create;
    try                               // TheHint<>'' is checked earlier.
      Assert(TheHint<>'', 'THintWindowManager.ShowHint: TheHint is empty');
      ms.Write(TheHint[1],length(TheHint));
      ms.Position:=0;
      HtmlHelpProvider.ControlIntf.SetHTMLContent(ms,'');
    finally
      ms.Free;
    end;
    HtmlHelpProvider.ControlIntf.GetPreferredControlSize(NewWidth,NewHeight);

    if NewWidth <= 0 then
      NewWidth := 500;
    if NewHeight <= 0 then
      NewHeight := 200;

    HintRenderWindow.HintRectAdjust := Rect(0, 0, NewWidth, NewHeight);
    if MouseOffset then
      HintRenderWindow.OffsetHintRect(ScreenPos)
    else
    begin
      R1 := HintRenderWindow.HintRect;
      HintRenderWindow.OffsetHintRect(ScreenPos, 0, True, False); // shrink height only for fixed (no MouseOffset) hints
      R2 := HintRenderWindow.HintRect;
      if R1.Bottom-R1.Top>R2.Bottom-R2.Top then // the height was decreased -> scrollbar will be shown, increase width
      begin
        Inc(R2.Right, GetSystemMetrics(SM_CXVSCROLL));
        HintRenderWindow.HintRect := R2;
        HintRenderWindow.OffsetHintRect(Point(0, 0), 0);
      end;
    end;
    HintRenderWindow.ActivateRendered;
  end;

begin
  if TheHint = '' then Exit(False);
  FOrigMousePos := Mouse.CursorPos;
  if FHintTextW <> nil then
    FHintTextW.Visible := false;
  if FHintRenderW <> nil then
    FHintRenderW.Visible := false;
  if CompareText(copy(TheHint,1,6),'<HTML>')=0 then // Text is HTML
    DoHtml
  else                                              // Plain text
    DoText;
  Result:=True;
end;

procedure THintWindowManager.HideHint;
begin
  if Assigned(FCurrentHintW) then
    FCurrentHintW.Visible := False;
end;

procedure THintWindowManager.HideIfVisible;
begin
  if HintIsVisible then
    FCurrentHintW.Visible := False;
end;

// Setters

procedure THintWindowManager.SetAutoHide(AValue: Boolean);
begin
  FAutoHide := AValue;
  if Assigned(FHintTextW) then FHintTextW.AutoHide := FAutoHide;
  if Assigned(FHintRenderW) then FHintRenderW.AutoHide := FAutoHide;
end;

procedure THintWindowManager.SetHideInterval(AValue: Integer);
begin
  FHideInterval := AValue;
  if Assigned(FHintTextW) then FHintTextW.HideInterval := FHideInterval;
  if Assigned(FHintRenderW) then FHintRenderW.HideInterval := FHideInterval;
end;

procedure THintWindowManager.SetOnMouseDown(AValue: TMouseEvent);
begin
  FOnMouseDown:=AValue;
  if Assigned(FHintTextW) then FHintTextW.OnMouseDown := FOnMouseDown;
  if Assigned(FHintRenderW) then FHintRenderW.OnMouseDown := FOnMouseDown;
end;

procedure THintWindowManager.SetWindowName(AValue: string);
begin
  FWindowName := AValue;
  if Assigned(FHintTextW) then FHintTextW.Name := FWindowName;
  if Assigned(FHintRenderW) then FHintRenderW.Name := FWindowName;
end;

end.