1 unit ADLayoutViewer;
2 
3 {$mode objfpc}{$H+}
4 
5 interface
6 
7 uses
8   Classes, SysUtils, types, math, Controls, Graphics, ComCtrls, LCLType,
9   LMessages, LCLIntf, AnchorDockStorage, LazLoggerBase;
10 
11 type
12   TADLTVMonitor = class
13   public
14     Monitor: integer;
15     WorkArea: TRect;
16     Bounds: TRect; // hull of all windows on this monitor
17     X, Y: integer;
18   end;
19 
20   { TADCustomLayoutTreeView }
21 
22   TADCustomLayoutTreeView = class(TCustomControl)
23   private
24     FLayout: TAnchorDockLayoutTree;
25     FScaleMax: double;
26     FScaleMin: double;
27     FScale: double;
28     FBounds: TRect;
29     FScaledBounds: TRect;
30     FScaledScroll: TPoint;
31     FMonitors: array of TADLTVMonitor;
32     FZoomTrackbar: TCustomTrackBar;
33     FOldZoombarOnChange: TNotifyEvent;
34     FIgnoreZoomTrackbarChange: boolean;
GetLayoutMaxXnull35     function GetLayoutMaxX: integer;
GetLayoutMaxYnull36     function GetLayoutMaxY: integer;
GetLayoutMinXnull37     function GetLayoutMinX: integer;
GetLayoutMinYnull38     function GetLayoutMinY: integer;
GetMonitorsnull39     function GetMonitors(Index: integer): TADLTVMonitor;
GetScaledMaxXnull40     function GetScaledMaxX: integer;
GetScaledMaxYnull41     function GetScaledMaxY: integer;
GetScaledMinXnull42     function GetScaledMinX: integer;
GetScaledMinYnull43     function GetScaledMinY: integer;
GetScaledOffsetXnull44     function GetScaledOffsetX: integer;
GetScaledOffsetYnull45     function GetScaledOffsetY: integer;
46     procedure SetScaleMax(AValue: double);
47     procedure SetScaleMin(AValue: double);
48     procedure SetScale(AValue: double);
49     procedure SetScaledOffsetX(AValue: integer);
50     procedure SetScaledOffsetY(AValue: integer);
51     procedure ComputeLayout;
52     procedure ClearMonitors;
FindMonitornull53     function FindMonitor(Monitor: integer): TADLTVMonitor;
54     procedure SetZoomTrackbar(AValue: TCustomTrackBar);
55     procedure UpdateZoomTrackBar;
56     procedure ZoomTrackbarChange(Sender: TObject);
57     procedure WMVScroll(var Msg: TLMScroll); message LM_VSCROLL;
58     procedure WMHScroll(var Msg: TLMScroll); message LM_HSCROLL;
59     procedure WMMouseWheel(var Message: TLMMouseEvent); message LM_MOUSEWHEEL;
60     procedure UpdateScrollBar;
61   protected
62     procedure Notification(AComponent: TComponent; Operation: TOperation);
63       override;
64     procedure CreateWnd; override;
65     procedure DoSetBounds(ALeft, ATop, AWidth, AHeight: integer); override;
66   public
67     constructor Create(AOwner: TComponent); override;
68     destructor Destroy; override;
69     procedure Paint; override;
ScaleRectnull70     function ScaleRect(const r: TRect): TRect;
71     procedure LayoutChanged; virtual;
72     property Layout: TAnchorDockLayoutTree read FLayout;
73     property Scale: double read FScale write SetScale;
74     property ScaledOffsetX: integer read GetScaledOffsetX write SetScaledOffsetX;
75     property ScaledOffsetY: integer read GetScaledOffsetY write SetScaledOffsetY;
ScaledOffsetYMaxnull76     function ScaledOffsetYMax: integer;
ScaledOffsetXMaxnull77     function ScaledOffsetXMax: integer;
78     property LayoutMinX: integer read GetLayoutMinX;
79     property LayoutMinY: integer read GetLayoutMinY;
80     property LayoutMaxX: integer read GetLayoutMaxX;
81     property LayoutMaxY: integer read GetLayoutMaxY;
82     property ScaledMinX: integer read GetScaledMinX;
83     property ScaledMinY: integer read GetScaledMinY;
84     property ScaledMaxX: integer read GetScaledMaxX;
85     property ScaledMaxY: integer read GetScaledMaxY;
MonitorCountnull86     function MonitorCount: integer;
87     property Monitors[Index: integer]: TADLTVMonitor read GetMonitors;
88     property ZoomTrackbar: TCustomTrackBar read FZoomTrackbar write SetZoomTrackbar;
ScaleToZoomTrackBarPosnull89     function ScaleToZoomTrackBarPos(aScale: double): integer;
ZoomTrackBarPosToScalenull90     function ZoomTrackBarPosToScale(p: integer): double;
91     property ScaleMin: double read FScaleMin write SetScaleMin;
92     property ScaleMax: double read FScaleMax write SetScaleMax;
93   end;
94 
95   TADLayoutTreeView = class(TADCustomLayoutTreeView)
96   published
97     property Scale: double read FScale write SetScale;
98     property ScaledOffsetX: integer read GetScaledOffsetX write SetScaledOffsetX;
99     property ScaledOffsetY: integer read GetScaledOffsetY write SetScaledOffsetY;
100     property ZoomTrackbar: TCustomTrackBar read FZoomTrackbar write SetZoomTrackbar;
101     property ScaleMin: double read FScaleMin write SetScaleMin;
102     property ScaleMax: double read FScaleMax write SetScaleMax;
103   end;
104 
105 implementation
106 
107 { TADCustomLayoutTreeView }
108 
109 procedure TADCustomLayoutTreeView.SetScale(AValue: double);
110 begin
111   AValue:=Min(ScaleMax,Max(AValue,ScaleMin));
112   if FScale=AValue then Exit;
113   FScale:=AValue;
114   FScaledBounds:=ScaleRect(FBounds);
115   FScaledScroll.X:=Min(FScaledScroll.X,ScaledMaxX);
116   FScaledScroll.Y:=Min(FScaledScroll.Y,ScaledMaxY);
117   UpdateScrollBar;
118   UpdateZoomTrackBar;
119   Invalidate;
120 end;
121 
122 procedure TADCustomLayoutTreeView.ZoomTrackbarChange(Sender: TObject);
123 begin
124   if not FIgnoreZoomTrackbarChange then
125     Scale:=ZoomTrackBarPosToScale(ZoomTrackbar.Position);
126   if Assigned(FOldZoombarOnChange) then
127     FOldZoombarOnChange(Sender);
128 end;
129 
130 procedure TADCustomLayoutTreeView.WMVScroll(var Msg: TLMScroll);
131 begin
132   case Msg.ScrollCode of
133       // Scrolls to start / end of the text
134     SB_TOP:        ScaledOffsetY := 0;
135     SB_BOTTOM:     ScaledOffsetY := ScaledOffsetYMax;
136       // Scrolls one line up / down
137     SB_LINEDOWN:   ScaledOffsetY := ScaledOffsetY + 10 div 2;
138     SB_LINEUP:     ScaledOffsetY := ScaledOffsetY - 10 div 2;
139       // Scrolls one page of lines up / down
140     SB_PAGEDOWN:   ScaledOffsetY := ScaledOffsetY + ClientHeight - 10;
141     SB_PAGEUP:     ScaledOffsetY := ScaledOffsetY - ClientHeight + 10;
142       // Scrolls to the current scroll bar position
143     SB_THUMBPOSITION,
144     SB_THUMBTRACK: ScaledOffsetY := Msg.Pos;
145       // Ends scrolling
146     SB_ENDSCROLL:  SetCaptureControl(nil); // release scrollbar capture
147   end;
148 end;
149 
150 procedure TADCustomLayoutTreeView.WMHScroll(var Msg: TLMScroll);
151 begin
152   case Msg.ScrollCode of
153       // Scrolls to start / end of the text
154     SB_TOP:        ScaledOffsetX := 0;
155     SB_BOTTOM:     ScaledOffsetX := ScaledOffsetXMax;
156       // Scrolls one line up / down
157     SB_LINEDOWN:   ScaledOffsetX := ScaledOffsetX + 10 div 2;
158     SB_LINEUP:     ScaledOffsetX := ScaledOffsetX - 10 div 2;
159       // Scrolls one page of lines up / down
160     SB_PAGEDOWN:   ScaledOffsetX := ScaledOffsetX + ClientWidth - 10;
161     SB_PAGEUP:     ScaledOffsetX := ScaledOffsetX - ClientWidth + 10;
162       // Scrolls to the current scroll bar position
163     SB_THUMBPOSITION,
164     SB_THUMBTRACK: ScaledOffsetX := Msg.Pos;
165       // Ends scrolling
166     SB_ENDSCROLL:  SetCaptureControl(nil); // release scrollbar capture
167   end;
168 end;
169 
170 procedure TADCustomLayoutTreeView.WMMouseWheel(var Message: TLMMouseEvent);
171 begin
172   if Mouse.WheelScrollLines=-1 then
173   begin
174     // -1 : scroll by page
175     ScaledOffsetY := ScaledOffsetY -
176               (Message.WheelDelta * (ClientHeight - 10)) div 120;
177   end else begin
178     // scrolling one line -> scroll half an item, see SB_LINEDOWN and SB_LINEUP
179     // handler in WMVScroll
180     ScaledOffsetY := ScaledOffsetY -
181         (Message.WheelDelta * Mouse.WheelScrollLines*10) div 240;
182   end;
183   Message.Result := 1;
184 end;
185 
186 procedure TADCustomLayoutTreeView.UpdateScrollBar;
187 var
188   ScrollInfo: TScrollInfo;
189 begin
190   if HandleAllocated then begin
191     ScrollInfo.cbSize := SizeOf(ScrollInfo);
192     ScrollInfo.fMask := SIF_ALL or SIF_DISABLENOSCROLL;
193     ScrollInfo.nMin := 0;
194     ScrollInfo.nTrackPos := 0;
195     ScrollInfo.nMax := Max(1,ScaledMaxY-1);
196     ScrollInfo.nPage := Max(1,ClientHeight-10);
197     ScrollInfo.nPos := ScaledOffsetY;
198     ShowScrollBar(Handle, SB_Vert, True);
199     SetScrollInfo(Handle, SB_Vert, ScrollInfo, True);
200 
201     ScrollInfo.cbSize := SizeOf(ScrollInfo);
202     ScrollInfo.fMask := SIF_ALL or SIF_DISABLENOSCROLL;
203     ScrollInfo.nMin := 0;
204     ScrollInfo.nTrackPos := 0;
205     ScrollInfo.nMax := Max(1,ScaledMaxX-1);
206     ScrollInfo.nPage := Max(1,ClientWidth-10);
207     ScrollInfo.nPos := ScaledOffsetX;
208     ShowScrollBar(Handle, SB_Horz, True);
209     SetScrollInfo(Handle, SB_Horz, ScrollInfo, True);
210   end;
211 end;
212 
TADCustomLayoutTreeView.GetLayoutMaxXnull213 function TADCustomLayoutTreeView.GetLayoutMaxX: integer;
214 begin
215   Result:=FBounds.Right;
216 end;
217 
GetLayoutMaxYnull218 function TADCustomLayoutTreeView.GetLayoutMaxY: integer;
219 begin
220   Result:=FBounds.Bottom;
221 end;
222 
TADCustomLayoutTreeView.GetLayoutMinXnull223 function TADCustomLayoutTreeView.GetLayoutMinX: integer;
224 begin
225   Result:=FBounds.Left;
226 end;
227 
GetLayoutMinYnull228 function TADCustomLayoutTreeView.GetLayoutMinY: integer;
229 begin
230   Result:=FBounds.Top;
231 end;
232 
TADCustomLayoutTreeView.GetMonitorsnull233 function TADCustomLayoutTreeView.GetMonitors(Index: integer): TADLTVMonitor;
234 begin
235   Result:=FMonitors[Index];
236 end;
237 
GetScaledMaxXnull238 function TADCustomLayoutTreeView.GetScaledMaxX: integer;
239 begin
240   Result:=FScaledBounds.Right;
241 end;
242 
TADCustomLayoutTreeView.GetScaledMaxYnull243 function TADCustomLayoutTreeView.GetScaledMaxY: integer;
244 begin
245   Result:=FScaledBounds.Bottom;
246 end;
247 
GetScaledMinXnull248 function TADCustomLayoutTreeView.GetScaledMinX: integer;
249 begin
250   Result:=FScaledBounds.Left;
251 end;
252 
TADCustomLayoutTreeView.GetScaledMinYnull253 function TADCustomLayoutTreeView.GetScaledMinY: integer;
254 begin
255   Result:=FScaledBounds.Top;
256 end;
257 
TADCustomLayoutTreeView.GetScaledOffsetXnull258 function TADCustomLayoutTreeView.GetScaledOffsetX: integer;
259 begin
260   Result:=FScaledScroll.X;
261 end;
262 
TADCustomLayoutTreeView.GetScaledOffsetYnull263 function TADCustomLayoutTreeView.GetScaledOffsetY: integer;
264 begin
265   Result:=FScaledScroll.Y;
266 end;
267 
268 procedure TADCustomLayoutTreeView.SetScaleMax(AValue: double);
269 // must be >=1.0
270 begin
271   AValue:=Max(AValue,1.0);
272   if FScaleMax=AValue then Exit;
273   FScaleMax:=AValue;
274   Scale:=Min(ScaleMax,Max(Scale,ScaleMin));
275   UpdateZoomTrackBar;
276 end;
277 
278 procedure TADCustomLayoutTreeView.SetScaleMin(AValue: double);
279 // must be between 0.00001 and 1.0
280 begin
281   AValue:=Min(1.0,Max(AValue,0.00001));
282   if FScaleMin=AValue then Exit;
283   FScaleMin:=AValue;
284   Scale:=Min(ScaleMax,Max(Scale,ScaleMin));
285   UpdateZoomTrackBar;
286 end;
287 
288 procedure TADCustomLayoutTreeView.SetScaledOffsetX(AValue: integer);
289 begin
290   AValue:=Max(0,Min(ScaledMaxX,AValue));
291   if FScaledScroll.X=AValue then Exit;
292   FScaledScroll.X:=AValue;
293   UpdateScrollBar;
294   Invalidate;
295 end;
296 
297 procedure TADCustomLayoutTreeView.SetScaledOffsetY(AValue: integer);
298 begin
299   AValue:=Max(0,Min(ScaledMaxY,AValue));
300   if FScaledScroll.Y=AValue then Exit;
301   FScaledScroll.Y:=AValue;
302   UpdateScrollBar;
303   Invalidate;
304 end;
305 
306 procedure TADCustomLayoutTreeView.ComputeLayout;
307 
308   procedure ComputeMonitors(Node: TAnchorDockLayoutTreeNode);
309   var
310     i: Integer;
311     Monitor: TADLTVMonitor;
312     r: TRect;
313   begin
314     if Node=nil then exit;
315     if Node.NodeType in [adltnLayout,adltnCustomSite] then begin
316       // top level window
317       Monitor:=FindMonitor(Node.Monitor);
318       if Monitor=nil then begin
319         // first window on this monitor
320         Monitor:=TADLTVMonitor.Create;
321         Monitor.Monitor:=Node.Monitor;
322         Monitor.WorkArea:=Node.WorkAreaRect;
323         Monitor.Bounds:=Node.BoundsRect;
324         SetLength(FMonitors,length(FMonitors)+1);
325         FMonitors[length(FMonitors)-1]:=Monitor;
326       end else begin
327         // another window on this monitor
328         r:=Rect(0,0,0,0);
329         UnionRect(r,Monitor.Bounds,Node.BoundsRect);
330         Monitor.Bounds:=r;
331       end;
332     end else begin
333       for i:=0 to Node.Count-1 do
334         ComputeMonitors(Node[i]);
335     end;
336   end;
337 
338 var
339   i: Integer;
340   Monitor: TADLTVMonitor;
341   TileSize: TPoint;
342   TileCols: Integer;
343   LeftMost: Integer;
344   TopMost: Integer;
345   TileCol: Integer;
346   TileRow: Integer;
347   TileLeft: Integer;
348   TileTop: Integer;
349   r: TRect;
350 begin
351   if FLayout=nil then exit;
352   // clear computed values
353   FBounds:=Rect(0,0,0,0);
354   FScaledBounds:=Rect(0,0,0,0);
355   ClearMonitors;
356   // collect all monitor bounds
357   ComputeMonitors(FLayout.Root);
358   if length(FMonitors)=0 then exit;
359   // get biggest monitor
360   TileSize:=Point(0,0);
361   for i:=0 to length(FMonitors)-1 do begin
362     Monitor:=FMonitors[i];
363     Monitor.Bounds.Right:=Max(Monitor.Bounds.Left,Monitor.Bounds.Right);
364     Monitor.Bounds.Bottom:=Max(Monitor.Bounds.Top,Monitor.Bounds.Bottom);
365     TileSize.X:=Max(TileSize.X,Monitor.Bounds.Right-Monitor.Bounds.Left);
366     TileSize.X:=Max(TileSize.X,Monitor.WorkArea.Right-Monitor.WorkArea.Left);
367     TileSize.Y:=Max(TileSize.Y,Monitor.Bounds.Bottom-Monitor.Bounds.Top);
368     TileSize.Y:=Max(TileSize.Y,Monitor.WorkArea.Bottom-Monitor.WorkArea.Top);
369   end;
370   // put monitors into tiles of same size in a quadratic layout
371   TileCols:=ceil(SQRT(length(FMonitors)));
372   for i:=0 to length(FMonitors)-1 do begin
373     Monitor:=FMonitors[i];
374     LeftMost:=Min(Monitor.WorkArea.Left,Monitor.Bounds.Left);
375     TopMost:=Min(Monitor.WorkArea.Top,Monitor.Bounds.Top);
376     TileCol:=i mod TileCols;
377     TileRow:=i div TileCols;
378     TileLeft:=TileCol*TileSize.X;
379     TileTop:=TileRow*TileSize.Y;
380     // move left/topmost to left,top of tile
381     Monitor.X:=TileLeft-LeftMost;
382     Monitor.Y:=TileTop-TopMost;
383     // compute total bounds
384     r.Left:=Monitor.X+Min(Monitor.WorkArea.Left,Monitor.Bounds.Left);
385     r.Top:=Monitor.Y+Min(Monitor.WorkArea.Top,Monitor.Bounds.Top);
386     r.Right:=Monitor.X+Min(Monitor.WorkArea.Right,Monitor.Bounds.Right);
387     r.Bottom:=Monitor.Y+Min(Monitor.WorkArea.Bottom,Monitor.Bounds.Bottom);
388     if i=0 then
389       FBounds:=r
390     else begin
391       FBounds.Left:=Min(FBounds.Left,r.Left);
392       FBounds.Right:=Max(FBounds.Right,r.Right);
393       FBounds.Top:=Min(FBounds.Top,r.Top);
394       FBounds.Bottom:=Max(FBounds.Bottom,r.Bottom);
395     end;
396     DebugLn(['TADLayoutTreeView.ComputeLayout ',i,'/',length(FMonitors),' WorkArea=',dbgs(Monitor.WorkArea),' Bounds=',dbgs(Monitor.Bounds),' X=',Monitor.X,' Y=',Monitor.Y]);
397   end;
398   FScaledBounds:=ScaleRect(FBounds);
399   UpdateScrollBar;
400 end;
401 
402 procedure TADCustomLayoutTreeView.ClearMonitors;
403 var
404   i: Integer;
405 begin
406   for i:=0 to length(FMonitors)-1 do FreeAndNil(FMonitors[i]);
407   SetLength(FMonitors,0);
408 end;
409 
TADCustomLayoutTreeView.FindMonitornull410 function TADCustomLayoutTreeView.FindMonitor(Monitor: integer): TADLTVMonitor;
411 var
412   i: Integer;
413 begin
414   for i:=0 to length(FMonitors)-1 do begin
415     Result:=FMonitors[i];
416     if Result.Monitor=Monitor then exit;
417   end;
418   Result:=nil;
419 end;
420 
421 procedure TADCustomLayoutTreeView.SetZoomTrackbar(AValue: TCustomTrackBar);
422 begin
423   if FZoomTrackbar=AValue then Exit;
424   if ZoomTrackbar<>nil then begin
425     ZoomTrackbar.OnChange:=FOldZoombarOnChange;
426   end;
427   FZoomTrackbar:=AValue;
428   if ZoomTrackbar<>nil then begin
429     FreeNotification(ZoomTrackbar);
430     fOldZoombarOnChange:=ZoomTrackbar.OnChange;
431     ZoomTrackbar.OnChange:=@ZoomTrackbarChange;
432     UpdateZoomTrackBar;
433   end;
434 end;
435 
436 procedure TADCustomLayoutTreeView.UpdateZoomTrackBar;
437 var
438   OldChange: Boolean;
439 begin
440   OldChange:=FIgnoreZoomTrackbarChange;
441   try
442     FIgnoreZoomTrackbarChange:=true;
443     //debugln(['TADCustomLayoutTreeView.UpdateZoomTrackBar Scale=',Scale,' Zoom=',ScaleToZoomTrackBarPos(Scale)]);
444     ZoomTrackbar.Position:=ScaleToZoomTrackBarPos(Scale);
445   finally
446     FIgnoreZoomTrackbarChange:=OldChange;
447   end;
448   ZoomTrackbar.Caption:='';
449 end;
450 
451 procedure TADCustomLayoutTreeView.Notification(AComponent: TComponent;
452   Operation: TOperation);
453 begin
454   inherited Notification(AComponent, Operation);
455   if Operation=opRemove then begin
456     if AComponent=ZoomTrackbar then
457       ZoomTrackbar:=nil;
458   end;
459 end;
460 
461 procedure TADCustomLayoutTreeView.CreateWnd;
462 begin
463   inherited CreateWnd;
464   UpdateScrollBar;
465 end;
466 
467 procedure TADCustomLayoutTreeView.DoSetBounds(ALeft, ATop, AWidth,
468   AHeight: integer);
469 begin
470   inherited DoSetBounds(ALeft, ATop, AWidth, AHeight);
471   UpdateScrollBar;
472 end;
473 
474 constructor TADCustomLayoutTreeView.Create(AOwner: TComponent);
475 begin
476   inherited Create(AOwner);
477   FLayout:=TAnchorDockLayoutTree.Create;
478   FScale:=0.25;
479   FScaleMin:=1/20;
480   FScaleMax:=5;
481 end;
482 
483 destructor TADCustomLayoutTreeView.Destroy;
484 begin
485   FreeAndNil(FLayout);
486   ClearMonitors;
487   inherited Destroy;
488 end;
489 
490 procedure TADCustomLayoutTreeView.Paint;
491 
492   procedure DrawContent(Node: TAnchorDockLayoutTreeNode;
493     OriginX, OriginY: integer);
494   var
495     r: TRect;
496     i: Integer;
497   begin
498     r:=Node.BoundsRect;
499     OffsetRect(r,OriginX,OriginY);
500     case Node.NodeType of
501     adltnLayout,adltnCustomSite:
502       begin
503         Canvas.Brush.Color:=clWhite;
504         Canvas.Pen.Color:=clBlue;
505       end;
506     adltnControl:
507       begin
508         Canvas.Brush.Color:=clGray;
509         Canvas.Pen.Color:=clBlack;
510       end;
511     adltnSplitterHorizontal:
512       begin
513         Canvas.Brush.Color:=clGreen;
514         Canvas.Pen.Color:=clGreen;
515       end;
516     adltnSplitterVertical:
517       begin
518         Canvas.Brush.Color:=clLime;
519         Canvas.Pen.Color:=clLime;
520       end;
521     adltnPages:
522       begin
523         Canvas.Brush.Color:=clYellow;
524         Canvas.Pen.Color:=clYellow;
525       end;
526     else
527       exit;
528     end;
529     Canvas.Rectangle(ScaleRect(r));
530     if Node.NodeType in [adltnLayout] then begin
531       for i:=0 to Node.Count-1 do
532         DrawContent(Node[i],r.Left,r.Top);
533     end;
534   end;
535 
536   procedure DrawWindows(Node: TAnchorDockLayoutTreeNode);
537   var
538     i: Integer;
539     Monitor: TADLTVMonitor;
540     r: TRect;
541   begin
542     if Node.NodeType in [adltnCustomSite,adltnLayout] then begin
543       // top level window
544       Monitor:=FindMonitor(Node.Monitor);
545       if Monitor=nil then exit;
546       r:=Node.BoundsRect;
547       OffsetRect(r,Monitor.X,Monitor.Y);
548       Canvas.Brush.Color:=clWhite;
549       Canvas.Pen.Color:=clRed;
550       Canvas.Rectangle(ScaleRect(r));
551       for i:=0 to Node.Count-1 do
552         DrawContent(Node[i],r.Left,r.Top);
553     end else begin
554       for i:=0 to Node.Count-1 do
555         DrawWindows(Node[i]);
556     end;
557   end;
558 
559 var
560   i: Integer;
561   Monitor: TADLTVMonitor;
562   r: TRect;
563 begin
564   Canvas.Brush.Color:=clGray;
565   Canvas.FillRect(0,0,ClientWidth,ClientHeight);
566 
567   // draw monitor workareas
568   Canvas.Pen.Color:=clBlue;
569   Canvas.Brush.Color:=RGBToColor(128,128,255);
570   for i:=0 to MonitorCount-1 do begin
571     Monitor:=fMonitors[i];
572     r:=ScaleRect(Monitor.WorkArea);
573     Canvas.Rectangle(r);
574   end;
575 
576   DrawWindows(Layout.Root);
577 
578   // call event
579   inherited Paint;
580 end;
581 
TADCustomLayoutTreeView.ScaleRectnull582 function TADCustomLayoutTreeView.ScaleRect(const r: TRect): TRect;
583 begin
584   Result.Left:=floor(r.Left*Scale)-FScaledScroll.X;
585   Result.Top:=floor(r.Top*Scale)-FScaledScroll.Y;
586   Result.Right:=ceil(r.Right*Scale)-FScaledScroll.X;
587   Result.Bottom:=ceil(r.Bottom*Scale)-FScaledScroll.Y;
588 end;
589 
590 procedure TADCustomLayoutTreeView.LayoutChanged;
591 begin
592   ComputeLayout;
593 end;
594 
ScaledOffsetYMaxnull595 function TADCustomLayoutTreeView.ScaledOffsetYMax: integer;
596 begin
597   Result:=ScaledMaxY-ClientHeight;
598 end;
599 
ScaledOffsetXMaxnull600 function TADCustomLayoutTreeView.ScaledOffsetXMax: integer;
601 begin
602   Result:=ScaledMaxY-ClientWidth;
603 end;
604 
MonitorCountnull605 function TADCustomLayoutTreeView.MonitorCount: integer;
606 begin
607   Result:=length(FMonitors);
608 end;
609 
TADCustomLayoutTreeView.ScaleToZoomTrackBarPosnull610 function TADCustomLayoutTreeView.ScaleToZoomTrackBarPos(aScale: double
611   ): integer;
612 var
613   lnMinPos, lnMaxPos, lnPos, Percent: Double;
614 begin
615   // ZoomTrackbar.Min corresponds to ln(ScaleMin)
616   // ZoomTrackbar.Max corresponds to ln(ScaleMax)
617   lnMinPos:=ln(ScaleMin);
618   lnMaxPos:=ln(ScaleMax);
619   lnPos:=ln(aScale);
620   Percent:=(lnPos-lnMinPos)/(lnMaxPos-lnMinPos);
621   Result:=ZoomTrackbar.Min+round(Percent*(ZoomTrackbar.Max-ZoomTrackbar.Min));
622   //debugln(['TADCustomLayoutTreeView.ScaleToZoomTrackBarPos ScaleMin=',ScaleMin,' ScaleMax=',ScaleMax,' lnMinPos=',lnMinPos,' lnMaxPos=',lnMaxPos,' lnPos=',lnPos,' Percent=',Percent,' TrackBar=Min=',ZoomTrackbar.Min,',Max=',ZoomTrackbar.Max,' Result=',Result]);
623   // avoid out of bounds due to rounding errors
624   Result:=Min(ZoomTrackbar.Max,Max(ZoomTrackbar.Min,Result));
625 end;
626 
TADCustomLayoutTreeView.ZoomTrackBarPosToScalenull627 function TADCustomLayoutTreeView.ZoomTrackBarPosToScale(p: integer): double;
628 var
629   lnMinPos, lnMaxPos, lnPos, Percent: Double;
630 begin
631   // ZoomTrackbar.Min corresponds to ln(ScaleMin)
632   // ZoomTrackbar.Max corresponds to ln(ScaleMax)
633   lnMinPos:=ln(ScaleMin);
634   lnMaxPos:=ln(ScaleMax);
635   Percent:=(p-ZoomTrackbar.Min)/(ZoomTrackbar.Max-ZoomTrackbar.Min);
636   lnPos:=lnMinPos+Percent*(lnMaxPos-lnMinPos);
637   Result:=exp(lnPos);
638   //debugln(['TADCustomLayoutTreeView.ZoomTrackBarPosToScale ScaleMin=',ScaleMin,' ScaleMax=',ScaleMax,' lnMinPos=',lnMinPos,' lnMaxPos=',lnMaxPos,' TrackBar=Min=',ZoomTrackbar.Min,',Max=',ZoomTrackbar.Max,',Position=',ZoomTrackbar.Position,' Percent=',Percent,' lnPos=',lnPos,' Result=',Result]);
639   // avoid out of bounds due to rounding errors
640   Result:=Max(ScaleMin,Min(ScaleMax,Result));
641 end;
642 
643 end.
644 
645