home *** CD-ROM | disk | FTP | other *** search
/ PC Pro 1999 February / DPPCPRO0299.ISO / February / Delphi / Install / DATA.Z / TABS.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1996-06-09  |  35.3 KB  |  1,322 lines

  1.  
  2. {*******************************************************}
  3. {                                                       }
  4. {       Delphi Visual Component Library                 }
  5. {                                                       }
  6. {       Copyright (c) 1995,96 Borland International     }
  7. {                                                       }
  8. {*******************************************************}
  9.  
  10. {****************************************************************************}
  11. {                                                                            }
  12. { Limitation on Distribution of Programs Created with this Source Code File: }
  13. { ========================================================================== }
  14. {                                                                            }
  15. { For distribution of an application which you create with this Source       }
  16. { Code File, your application may not be a general-purpose, interactive      }
  17. { spreadsheet program, or a substitute for or generally competitive          }
  18. { with Quattro Pro.                                                          }
  19. {                                                                            }
  20. {****************************************************************************}
  21.  
  22. { Implements tab control }
  23.  
  24. unit Tabs;
  25.  
  26. interface
  27.  
  28. uses Windows, Classes, Graphics, Forms, Controls, Messages;
  29.  
  30. type
  31.   TScrollBtn = (sbLeft, sbRight);
  32.  
  33.   TScroller = class(TCustomControl)
  34.   private
  35.     { property usage }
  36.     FMin: Longint;
  37.     FMax: Longint;
  38.     FPosition: Longint;
  39.     FOnClick: TNotifyEvent;
  40.     FChange: Integer;
  41.  
  42.     { private usage }
  43.     Bitmap: TBitmap;
  44.     Pressed: Boolean;
  45.     Down: Boolean;
  46.     Current: TScrollBtn;
  47.     pWidth: Integer;
  48.     pHeight: Integer;
  49.  
  50.     { property access methods }
  51.     procedure SetMin(Value: Longint);
  52.     procedure SetMax(Value: Longint);
  53.     procedure SetPosition(Value: Longint);
  54.  
  55.     { private methods }
  56.     function CanScrollLeft: Boolean;
  57.     function CanScrollRight: Boolean;
  58.     procedure DoMouseDown(X: Integer);
  59.     procedure WMLButtonDown(var Message: TWMLButtonDown);
  60.       message WM_LBUTTONDOWN;
  61.     procedure WMLButtonDblClk(var Message: TWMLButtonDblClk);
  62.       message WM_LBUTTONDBLCLK;
  63.     procedure WMMouseMove(var Message: TWMMouseMove);
  64.       message WM_MOUSEMOVE;
  65.     procedure WMLButtonUp(var Message: TWMLButtonUp);
  66.       message WM_LBUTTONUP;
  67.     procedure WMSize(var Message: TWMSize);
  68.       message WM_SIZE;
  69.   public
  70.     constructor Create(AOwner: TComponent); override;
  71.     destructor Destroy; override;
  72.     procedure Paint; override;
  73.   published
  74.     property OnClick: TNotifyEvent read FOnClick write FOnClick;
  75.     property Min: Longint read FMin write SetMin default 0;
  76.     property Max: Longint read FMax write SetMax default 0;
  77.     property Position: Longint read FPosition write SetPosition default 0;
  78.     property Change: Integer read FChange write FChange default 1;
  79.   end;
  80.  
  81.   TTabSet = class;
  82.  
  83.   TTabList = class(TStringList)
  84.   private
  85.     Tabs: TTabSet;
  86.   public
  87.     procedure Insert(Index: Integer; const S: string); override;
  88.     procedure Delete(Index: Integer); override;
  89.     function Add(const S: string): Integer; override;
  90.     procedure Put(Index: Integer; const S: string); override;
  91.     procedure Clear; override;
  92.     procedure AddStrings(Strings: TStrings); override;
  93.   end;
  94.  
  95.   { eash TEdgeType is made up of one or two of these parts }
  96.   TEdgePart = (epSelectedLeft, epUnselectedLeft, epSelectedRight,
  97.     epUnselectedRight);
  98.  
  99.   { represents the intersection between two tabs, or the edge of a tab }
  100.   TEdgeType = (etNone, etFirstIsSel, etFirstNotSel, etLastIsSel, etLastNotSel,
  101.     etNotSelToSel, etSelToNotSel, etNotSelToNotSel);
  102.  
  103.   TTabStyle = (tsStandard, tsOwnerDraw);
  104.  
  105.   TMeasureTabEvent = procedure(Sender: TObject; Index: Integer;
  106.     var TabWidth: Integer) of object;
  107.   TDrawTabEvent = procedure(Sender: TObject; TabCanvas: TCanvas; R: TRect;
  108.     Index: Integer; Selected: Boolean) of object;
  109.   TTabChangeEvent = procedure(Sender: TObject; NewTab: Integer;
  110.     var AllowChange: Boolean) of object;
  111.  
  112.   TTabSet = class(TCustomControl)
  113.   private
  114.     { property instance variables }
  115.     FStartMargin: Integer;
  116.     FEndMargin: Integer;
  117.     FTabs: TStrings;
  118.     FTabIndex: Integer;
  119.     FFirstIndex: Integer;
  120.     FVisibleTabs: Integer;
  121.     FSelectedColor: TColor;
  122.     FUnselectedColor: TColor;
  123.     FBackgroundColor: TColor;
  124.     FDitherBackground: Boolean;
  125.     FAutoScroll: Boolean;
  126.     FStyle: TTabStyle;
  127.     FOwnerDrawHeight: Integer;
  128.     FOnMeasureTab: TMeasureTabEvent;
  129.     FOnDrawTab: TDrawTabEvent;
  130.     FOnChange: TTabChangeEvent;
  131.  
  132.     { private instance variables }
  133.  
  134.     ImageList: TImageList;
  135.     MemBitmap: TBitmap;   { used for off-screen drawing }
  136.     BrushBitmap: TBitmap; { used for background pattern }
  137.  
  138.     TabPositions: TList;
  139.     FTabHeight: Integer;
  140.     Scroller: TScroller;
  141.     TabVersion: Integer;
  142.     FDoFix: Boolean;
  143.     FReserved: Byte;
  144.  
  145.     { property access methods }
  146.     procedure SetSelectedColor(Value: TColor);
  147.     procedure SetUnselectedColor(Value: TColor);
  148.     procedure SetBackgroundColor(Value: TColor);
  149.     procedure SetDitherBackground(Value: Boolean);
  150.     procedure SetAutoScroll(Value: Boolean);
  151.     procedure SetStartMargin(Value: Integer);
  152.     procedure SetEndMargin(Value: Integer);
  153.     procedure SetTabIndex(Value: Integer);
  154.     procedure SetFirstIndex(Value: Integer);
  155.     procedure SetTabList(Value: TStrings);
  156.     function GetTabCount: Integer;
  157.     function GetTabName(Value: Integer): String;
  158.     procedure SetTabName(Value: Integer; const AName: String);
  159.     procedure SetTabStyle(Value: TTabStyle);
  160.     procedure SetTabHeight(Value: Integer);
  161.  
  162.     { private methods }
  163.     procedure CreateParams(var Params: TCreateParams); override;
  164.     procedure MouseDown(Button: TMouseButton; Shift: TShiftState;
  165.       X, Y: Integer); override;
  166.     procedure WMSize(var Message: TWMSize); message WM_SIZE;
  167.     procedure CMSysColorChange(var Message: TMessage); message CM_SYSCOLORCHANGE;
  168.     procedure CMFontChanged(var Message: TMessage); message CM_FONTCHANGED;
  169.     procedure PaintEdge(X, Y, H: Integer; Edge: TEdgeType);
  170.     procedure CreateBrushPattern(Bitmap: TBitmap);
  171.     function CalcTabPositions(Start, Stop: Integer; Canvas: TCanvas;
  172.       First: Integer): Integer;
  173.     procedure CreateScroller;
  174.     procedure InitBitmaps;
  175.     procedure DoneBitmaps;
  176.     procedure CreateEdgeParts;
  177.     procedure FixTabPos;
  178.     procedure WMGetDlgCode(var Message: TWMGetDlgCode); message WM_GETDLGCODE;
  179.     procedure CMDialogChar(var Message: TCMDialogChar); message CM_DIALOGCHAR;
  180.     procedure ScrollClick(Sender: TObject);
  181.     procedure ReadIntData(Reader: TReader);
  182.     procedure ReadBoolData(Reader: TReader);
  183.   protected
  184.     procedure Paint; override;
  185.     procedure DrawTab(TabCanvas: TCanvas; R: TRect; Index: Integer;
  186.       Selected: Boolean); virtual;
  187.     function CanChange(NewIndex: Integer): Boolean;
  188.     procedure GetChildren(Proc: TGetChildProc); override;
  189.     procedure MeasureTab(Index: Integer; var TabWidth: Integer); virtual;
  190.     procedure DefineProperties(Filer: TFiler); override;
  191.   public
  192.     constructor Create(AOwner: TComponent); override;
  193.     destructor Destroy; override;
  194.     function ItemAtPos(Pos: TPoint): Integer;
  195.     function ItemRect(Item: Integer): TRect;
  196.     procedure SelectNext(Direction: Boolean);
  197.     property Canvas;
  198.     property FirstIndex: Integer read FFirstIndex write SetFirstIndex default 0;
  199.   published
  200.     property Align;
  201.     property AutoScroll: Boolean read FAutoScroll write SetAutoScroll default True;
  202.     property BackgroundColor: TColor read FBackgroundColor write SetBackgroundColor default clBtnFace;
  203.     property DitherBackground: Boolean read FDitherBackground write SetDitherBackground default True;
  204.     property DragMode;
  205.     property Enabled;
  206.     property EndMargin: Integer read FEndMargin write SetEndMargin default 5;
  207.     property Font;
  208.     property ParentShowHint;
  209.     property PopupMenu;
  210.     property ShowHint;
  211.     property StartMargin: Integer read FStartMargin write SetStartMargin default 5;
  212.     property SelectedColor: TColor read FSelectedColor write SetSelectedColor default clBtnFace;
  213.     property Style: TTabStyle read FStyle write SetTabStyle default tsStandard;
  214.     property TabHeight: Integer read FOwnerDrawHeight write SetTabHeight default 20;
  215.     property Tabs: TStrings read FTabs write SetTabList;
  216.     property TabIndex: Integer read FTabIndex write SetTabIndex default -1;
  217.     property UnselectedColor: TColor read FUnselectedColor write SetUnselectedColor default clWindow;
  218.     property Visible;
  219.     property VisibleTabs: Integer read FVisibleTabs;
  220.     property OnClick;
  221.     property OnChange: TTabChangeEvent read FOnChange write FOnChange;
  222.     property OnDragDrop;
  223.     property OnDragOver;
  224.     property OnDrawTab: TDrawTabEvent read FOnDrawTab write FOnDrawTab;
  225.     property OnEndDrag;
  226.     property OnEnter;
  227.     property OnExit;
  228.     property OnMouseDown;
  229.     property OnMouseMove;
  230.     property OnMouseUp;
  231.     property OnMeasureTab: TMeasureTabEvent read FOnMeasureTab write FOnMeasureTab;
  232.     property OnStartDrag;
  233.   end;
  234.  
  235. implementation
  236.  
  237. uses Consts, SysUtils;
  238.  
  239. {$R TABS.RES}
  240.  
  241. const
  242.   EdgeWidth = 9;  { This controls the angle of the tab edges }
  243.  
  244. type
  245.   TTabPos = record
  246.     Size, StartPos: Word;
  247.   end;
  248.  
  249. { TScroller }
  250.  
  251. constructor TScroller.Create(AOwner: TComponent);
  252. begin
  253.   inherited Create(AOwner);
  254.   ControlStyle := ControlStyle + [csOpaque];
  255.   Bitmap := TBitmap.Create;
  256.   pWidth := 24;
  257.   pHeight := 13;
  258.   FMin := 0;
  259.   FMax := 0;
  260.   FPosition := 0;
  261.   FChange := 1;
  262. end;
  263.  
  264. destructor TScroller.Destroy;
  265. begin
  266.   Bitmap.Free;
  267.   inherited Destroy;
  268. end;
  269.  
  270. procedure TScroller.Paint;
  271. begin
  272.   with Canvas do
  273.   begin
  274.     { paint left button }
  275.     if CanScrollLeft then
  276.     begin
  277.       if Down and (Current = sbLeft) then
  278.         Bitmap.Handle := LoadBitmap(HInstance, 'SBLEFTDN')
  279.       else Bitmap.Handle := LoadBitmap(HInstance, 'SBLEFT');
  280.     end
  281.     else
  282.       Bitmap.Handle := LoadBitmap(HInstance, 'SBLEFTDIS');
  283.     Draw(0, 0, Bitmap);
  284.  
  285.     { paint right button }
  286.     if CanScrollRight then
  287.     begin
  288.       if Down and (Current = sbRight) then
  289.         Bitmap.Handle := LoadBitmap(HInstance, 'SBRIGHTDN')
  290.       else Bitmap.Handle := LoadBitmap(HInstance, 'SBRIGHT');
  291.     end
  292.     else
  293.       Bitmap.Handle := LoadBitmap(HInstance, 'SBRIGHTDIS');
  294.     Draw((pWidth div 2) - 1, 0, Bitmap);
  295.   end;
  296. end;
  297.  
  298. procedure TScroller.WMSize(var Message: TWMSize);
  299. begin
  300.   inherited;
  301.   Width := pWidth - 1;
  302.   Height := pHeight;
  303. end;
  304.  
  305. procedure TScroller.SetMin(Value: Longint);
  306. begin
  307.   if Value < FMax then FMin := Value;
  308. end;
  309.  
  310. procedure TScroller.SetMax(Value: Longint);
  311. begin
  312.   if Value > FMin then FMax := Value;
  313. end;
  314.  
  315. procedure TScroller.SetPosition(Value: Longint);
  316. begin
  317.   if Value <> FPosition then
  318.   begin
  319.     if Value < Min then Value := Min;
  320.     if Value > Max then Value := Max;
  321.     FPosition := Value;
  322.     Invalidate;
  323.     if Assigned(FOnClick) then
  324.       FOnClick(Self);
  325.   end;
  326. end;
  327.  
  328. function TScroller.CanScrollLeft: Boolean;
  329. begin
  330.   Result := Position > Min;
  331. end;
  332.  
  333. function TScroller.CanScrollRight: Boolean;
  334. begin
  335.   Result := Position < Max;
  336. end;
  337.  
  338. procedure TScroller.DoMouseDown(X: Integer);
  339. begin
  340.   if X < pWidth div 2 then Current := sbLeft
  341.   else Current := sbRight;
  342.   case Current of
  343.     sbLeft: if not CanScrollLeft then Exit;
  344.     sbRight: if not CanScrollRight then Exit;
  345.   end;
  346.   Pressed := True;
  347.   Down := True;
  348.   Invalidate;
  349.   SetCapture(Handle);
  350. end;
  351.  
  352. procedure TScroller.WMLButtonDown(var Message: TWMLButtonDown);
  353. begin
  354.   DoMouseDown(Message.XPos);
  355. end;
  356.  
  357. procedure TScroller.WMLButtonDblClk(var Message: TWMLButtonDblClk);
  358. begin
  359.   DoMouseDown(Message.XPos);
  360. end;
  361.  
  362. procedure TScroller.WMMouseMove(var Message: TWMMouseMove);
  363. var
  364.   P: TPoint;
  365.   R: TRect;
  366. begin
  367.   if Pressed then
  368.   begin
  369.     P := Point(Message.XPos, Message.YPos);
  370.     R := Rect(0, 0, pWidth div 2, pHeight);
  371.     if Current = sbRight then OffsetRect(R, pWidth div 2, 0);
  372.     if PtInRect(R, P) <> Down then
  373.     begin
  374.       Down := not Down;
  375.       Invalidate;
  376.     end;
  377.   end;
  378. end;
  379.  
  380. procedure TScroller.WMLButtonUp(var Message: TWMLButtonUp);
  381. var
  382.   NewPos: Longint;
  383. begin
  384.   ReleaseCapture;
  385.   Pressed := False;
  386.  
  387.   if Down then
  388.   begin
  389.     Down := False;
  390.     NewPos := Position;
  391.     case Current of
  392.       sbLeft: Dec(NewPos, Change);
  393.       sbRight: Inc(NewPos, Change);
  394.     end;
  395.     Position := NewPos;
  396.   end;
  397. end;
  398.  
  399.  
  400. { TTabList }
  401.  
  402. function TTabList.Add(const S: string): Integer;
  403. begin
  404.   Result := inherited Add(S);
  405.   if Tabs <> nil then
  406.     Tabs.Invalidate;
  407. end;
  408.  
  409. procedure TTabList.Insert(Index: Integer; const S: string);
  410. begin
  411.   inherited Insert(Index, S);
  412.   if Tabs <> nil then
  413.   begin
  414.     if Index <= Tabs.FTabIndex then Inc(Tabs.FTabIndex);
  415.     Tabs.Invalidate;
  416.   end;
  417. end;
  418.  
  419. procedure TTabList.Delete(Index: Integer);
  420. var
  421.   OldIndex: Integer;
  422. begin
  423.   OldIndex := Tabs.Tabindex;
  424.   inherited Delete(Index);
  425.  
  426.   if OldIndex < Count then Tabs.FTabIndex := OldIndex
  427.   else Tabs.FTabIndex := Count - 1;
  428.   Tabs.Invalidate;
  429.   Tabs.Invalidate;
  430.   if OldIndex = Index then Tabs.Click;  { deleted selected tab }
  431. end;
  432.  
  433. procedure TTabList.Put(Index: Integer; const S: string);
  434. begin
  435.   inherited Put(Index, S);
  436.   if Tabs <> nil then
  437.     Tabs.Invalidate;
  438. end;
  439.  
  440. procedure TTabList.Clear;
  441. begin
  442.   inherited Clear;
  443.   Tabs.FTabIndex := -1;
  444.   Tabs.Invalidate;
  445. end;
  446.  
  447. procedure TTabList.AddStrings(Strings: TStrings);
  448. begin
  449.   SendMessage(Tabs.Handle, WM_SETREDRAW, 0, 0);
  450.   inherited AddStrings(Strings);
  451.   SendMessage(Tabs.Handle, WM_SETREDRAW, 1, 0);
  452.   Tabs.Invalidate;
  453. end;
  454.  
  455. { TTabSet }
  456.  
  457. constructor TTabSet.Create(AOwner: TComponent);
  458. begin
  459.   inherited Create(AOwner);
  460.   ControlStyle := [csCaptureMouse, csDoubleClicks, csOpaque];
  461.   Width := 185;
  462.   Height := 21;
  463.  
  464.   TabPositions := TList.Create;
  465.   FTabHeight := 20;
  466.  
  467.   FTabs := TTabList.Create;
  468.   TTabList(FTabs).Tabs := Self;
  469.   InitBitmaps;
  470.  
  471.   CreateScroller;
  472.  
  473.   FTabIndex := -1;
  474.   FFirstIndex := 0;
  475.   FVisibleTabs := 0;  { set by draw routine }
  476.   FStartMargin := 5;
  477.   FEndMargin := 5;
  478.  
  479.   { initialize default values }
  480.   FSelectedColor := clBtnFace;
  481.   FUnselectedColor := clWindow;
  482.   FBackgroundColor := clBtnFace;
  483.   FDitherBackground := True;
  484.   CreateBrushPattern(BrushBitmap);
  485.   FAutoScroll := True;
  486.   FStyle := tsStandard;
  487.   FOwnerDrawHeight := 20;
  488.  
  489.   ParentFont := False;
  490.   Font.Name := 'MS Sans Serif';
  491.   Font.Size := 8;
  492.   Font.Style := [];
  493.  
  494.   { create the edge bitmaps }
  495.   CreateEdgeParts;
  496. end;
  497.  
  498. procedure TTabSet.CreateParams(var Params: TCreateParams);
  499. begin
  500.   inherited CreateParams(Params);
  501.   with Params.WindowClass do
  502.     style := style and not (CS_VREDRAW or CS_HREDRAW);
  503. end;
  504.  
  505. procedure TTabSet.CreateScroller;
  506. begin
  507.   Scroller := TScroller.Create(Self);
  508.   with Scroller do
  509.   begin
  510.     Parent := Self;
  511.     Top := 3;
  512.     Min := 0;
  513.     Max := 0;
  514.     Position := 0;
  515.     Visible := False;
  516.     OnClick := ScrollClick;
  517.   end;
  518. end;
  519.  
  520. procedure TTabSet.InitBitmaps;
  521. begin
  522.   MemBitmap := TBitmap.Create;
  523.   BrushBitmap := TBitmap.Create;
  524. end;
  525.  
  526. destructor TTabSet.Destroy;
  527. begin
  528.   FTabs.Free;
  529.   TabPositions.Free;
  530.   DoneBitmaps;
  531.   inherited Destroy;
  532. end;
  533.  
  534. procedure TTabSet.DoneBitmaps;
  535. begin
  536.   MemBitmap.Free;
  537.   BrushBitmap.Free;
  538.   ImageList.Free;
  539. end;
  540.  
  541. procedure TTabSet.ScrollClick(Sender: TObject);
  542. begin
  543.   FirstIndex := TScroller(Sender).Position;
  544. end;
  545.  
  546. { cache the tab position data, and return number of visible tabs }
  547. function TTabSet.CalcTabPositions(Start, Stop: Integer; Canvas: TCanvas;
  548.   First: Integer): Integer;
  549. var
  550.   Index: Integer;
  551.   TabPos: TTabPos;
  552.   W: Integer;
  553. begin
  554.   TabPositions.Count := 0;  { erase all previously cached data }
  555.   Index := First;
  556.   while (Start < Stop) and (Index < Tabs.Count) do
  557.     with Canvas do
  558.     begin
  559.       TabPos.StartPos := Start;
  560.       W := TextWidth(Tabs[Index]);
  561.  
  562.       { Owner }
  563.       if (FStyle = tsOwnerDraw) then MeasureTab(Index, W);
  564.  
  565.       TabPos.Size := W;
  566.       Inc(Start, TabPos.Size + EdgeWidth);    { next usable position }
  567.  
  568.       if Start <= Stop then
  569.       begin
  570.         TabPositions.Add(Pointer(TabPos));    { add to list }
  571.         Inc(Index);
  572.       end;
  573.     end;
  574.   Result := Index - First;
  575. end;
  576.  
  577. function TTabSet.ItemAtPos(Pos: TPoint): Integer;
  578. var
  579.   TabPos: TTabPos;
  580.   I: Integer;
  581. begin
  582.   Result := -1;
  583.   if (Pos.Y < 0) or (Pos.Y > ClientHeight) then Exit;
  584.   for I := 0 to TabPositions.Count - 1 do
  585.   begin
  586.     Pointer(TabPos) := TabPositions[I];
  587.     if (Pos.X >= TabPos.StartPos) and (Pos.X <= TabPos.StartPos + TabPos.Size) then
  588.     begin
  589.       Result := I;
  590.       Exit;
  591.     end;
  592.   end;
  593. end;
  594.  
  595. function TTabSet.ItemRect(Item: Integer): TRect;
  596. var
  597.   TabPos: TTabPos;
  598. begin
  599.   if (TabPositions.Count > 0) and (Item >= 0) and (Item < TabPositions.Count) then
  600.   begin
  601.     Pointer(TabPos) := TabPositions[Item];
  602.     Result := Rect(TabPos.StartPos, 0, TabPos.StartPos + TabPos.Size, FTabHeight);
  603.     InflateRect(Result, 1, -2);
  604.   end
  605.   else
  606.     Result := Rect(0, 0, 0, 0);
  607. end;
  608.  
  609. procedure TTabSet.Paint;
  610. var
  611.   TabStart, LastTabPos: Integer;
  612.   TabPos: TTabPos;
  613.   Tab: Integer;
  614.   Leading: TEdgeType;
  615.   Trailing: TEdgeType;
  616.   isFirst, isLast, isSelected, isPrevSelected: Boolean;
  617.   R: TRect;
  618. begin
  619.   if not HandleAllocated then Exit;
  620.  
  621.   { Set the size of the off-screen bitmap.  Make sure that it is tall enough to
  622.     display the entire tab, even if the screen won't display it all.  This is
  623.     required to avoid problems with using FloodFill. }
  624.   MemBitmap.Width := ClientWidth;
  625.   if ClientHeight < FTabHeight + 5 then MemBitmap.Height := FTabHeight + 5
  626.   else MemBitmap.Height := ClientHeight;
  627.  
  628.   MemBitmap.Canvas.Font := Self.Canvas.Font;
  629.  
  630.   TabStart := StartMargin + EdgeWidth;        { where does first text appear? }
  631.   LastTabPos := Width - EndMargin;            { tabs draw until this position }
  632.   Scroller.Left := Width - Scroller.Width - 2;
  633.  
  634.   { do initial calculations for how many tabs are visible }
  635.   FVisibleTabs := CalcTabPositions(TabStart, LastTabPos, MemBitmap.Canvas,
  636.     FirstIndex);
  637.  
  638.   { enable the scroller if FAutoScroll = True and not all tabs are visible }
  639.   if AutoScroll and (FVisibleTabs < Tabs.Count) then
  640.   begin
  641.     Dec(LastTabPos, Scroller.Width - 4);
  642.     { recalc the tab positions }
  643.     FVisibleTabs := CalcTabPositions(TabStart, LastTabPos, MemBitmap.Canvas,
  644.       FirstIndex);
  645.  
  646.     { set the scroller's range }
  647.     Scroller.Visible := True;
  648.     ShowWindow(Scroller.Handle, SW_SHOW);
  649.     Scroller.Min := 0;
  650.     Scroller.Max := Tabs.Count - VisibleTabs;
  651.     Scroller.Position := FirstIndex;
  652.   end
  653.   else
  654.     if VisibleTabs >= Tabs.Count then
  655.     begin
  656.       Scroller.Visible := False;
  657.       ShowWindow(Scroller.Handle, SW_HIDE);
  658.     end;
  659.  
  660.   if FDoFix then
  661.   begin
  662.     FixTabPos;
  663.     FVisibleTabs := CalcTabPositions(TabStart, LastTabPos, MemBitmap.Canvas,
  664.       FirstIndex);
  665.   end;
  666.   FDoFix := False;
  667.  
  668.   { draw background of tab area }
  669.   with MemBitmap.Canvas do
  670.   begin
  671.     Brush.Bitmap := BrushBitmap;
  672.     FillRect(Rect(0, 0, MemBitmap.Width, MemBitmap.Height));
  673.  
  674.     Pen.Width := 1;
  675.     Pen.Color := clBtnShadow;
  676.     MoveTo(0, 0);
  677.     LineTo(MemBitmap.Width + 1, 0);
  678.  
  679.     Pen.Color := clWindowFrame;
  680.     MoveTo(0, 1);
  681.     LineTo(MemBitmap.Width + 1, 1);
  682.   end;
  683.  
  684.   for Tab := 0 to TabPositions.Count - 1 do
  685.   begin
  686.     Pointer(TabPos) := TabPositions[Tab];
  687.  
  688.     isFirst := Tab = 0;
  689.     isLast := Tab = VisibleTabs - 1;
  690.     isSelected := Tab + FirstIndex = TabIndex;
  691.     isPrevSelected := (Tab + FirstIndex) - 1 = TabIndex;
  692.  
  693.     { Rule: every tab paints its leading edge, only the last tab paints a
  694.       trailing edge }
  695.     Trailing := etNone;
  696.  
  697.     if isLast then
  698.     begin
  699.       if isSelected then Trailing := etLastIsSel
  700.       else Trailing := etLastNotSel;
  701.     end;
  702.  
  703.     if isFirst then
  704.     begin
  705.       if isSelected then Leading := etFirstIsSel
  706.       else Leading := etFirstNotSel;
  707.     end
  708.     else  { not first }
  709.     begin
  710.       if isPrevSelected then Leading := etSelToNotSel
  711.       else
  712.         if isSelected then Leading := etNotSelToSel
  713.         else Leading := etNotSelToNotSel;
  714.     end;
  715.  
  716.     { draw leading edge }
  717.     if Leading <> etNone then
  718.       PaintEdge(TabPos.StartPos - EdgeWidth, 0, FTabHeight - 1, Leading);
  719.  
  720.     { set up the canvas }
  721.     R := Rect(TabPos.StartPos, 0, TabPos.StartPos + TabPos.Size, FTabHeight);
  722.     with MemBitmap.Canvas do
  723.     begin
  724.       if isSelected then Brush.Color := SelectedColor
  725.       else Brush.Color := UnselectedColor;
  726.       ExtTextOut(Handle, TabPos.StartPos, 2, ETO_OPAQUE, @R,
  727.         nil, 0, nil);
  728.     end;
  729.  
  730.     { restore font for drawing the text }
  731.     MemBitmap.Canvas.Font := Self.Canvas.Font;
  732.  
  733.     { Owner }
  734.     if (FStyle = tsOwnerDraw) then
  735.       DrawTab(MemBitmap.Canvas, R, Tab + FirstIndex, isSelected)
  736.     else
  737.     begin
  738.       with MemBitmap.Canvas do
  739.       begin
  740.         Inc(R.Top, 2);
  741.         DrawText(Handle, PChar(Tabs[Tab + FirstIndex]),
  742.           Length(Tabs[Tab + FirstIndex]), R, DT_CENTER);
  743.       end;
  744.     end;
  745.  
  746.     { draw trailing edge  }
  747.     if Trailing <> etNone then
  748.       PaintEdge(TabPos.StartPos + TabPos.Size, 0, FTabHeight - 1, Trailing);
  749.  
  750.     { draw connecting lines above and below the text }
  751.  
  752.     with MemBitmap.Canvas do
  753.     begin
  754.       Pen.Color := clWindowFrame;
  755.       MoveTo(TabPos.StartPos, FTabHeight - 1);
  756.       LineTo(TabPos.StartPos + TabPos.Size, FTabHeight - 1);
  757.  
  758.       if isSelected then
  759.       begin
  760.         Pen.Color := clBtnShadow;
  761.         MoveTo(TabPos.StartPos, FTabHeight - 2);
  762.         LineTo(TabPos.StartPos + TabPos.Size, FTabHeight - 2);
  763.       end
  764.       else
  765.       begin
  766.         Pen.Color := clWindowFrame;
  767.         MoveTo(TabPos.StartPos, 1);
  768.         LineTo(TabPos.StartPos + TabPos.Size, 1);
  769.  
  770.         Pen.Color := clBtnShadow;
  771.         MoveTo(TabPos.StartPos, 0);
  772.         LineTo(TabPos.StartPos + TabPos.Size + 1, 0);
  773.       end;
  774.     end;
  775.   end;
  776.  
  777.   { draw onto the screen }
  778.   Canvas.Draw(0, 0, MemBitmap);
  779. end;
  780.  
  781. procedure TTabSet.CreateEdgeParts;
  782. var
  783.   H: Integer;
  784.   Working: TBitmap;
  785.   EdgePart: TEdgePart;
  786.   MaskColor: TColor;
  787.  
  788.   procedure DrawUL(Canvas: TCanvas);
  789.   begin
  790.     with Canvas do
  791.     begin
  792.       Pen.Color := clBtnShadow;
  793.       PolyLine([Point(0, 0), Point(EdgeWidth + 1, 0)]);
  794.  
  795.       Pen.Color := UnselectedColor;
  796.       Brush.Color := UnselectedColor;
  797.       Polygon([Point(3,1), Point(EdgeWidth - 1, H), Point(EdgeWidth, H),
  798.         Point(EdgeWidth, 1), Point(3, 1)]);
  799.  
  800.       Pen.Color := clWindowFrame;
  801.       PolyLine([Point(0, 1), Point(EdgeWidth + 1, 1), Point(3, 1),
  802.         Point(EdgeWidth - 1, H), Point(EdgeWidth, H)]);
  803.     end;
  804.   end;
  805.  
  806.   procedure DrawSR(Canvas: TCanvas);
  807.   begin
  808.     with Canvas do
  809.     begin
  810.       Pen.Color := SelectedColor;
  811.       Brush.Color := SelectedColor;
  812.       Polygon([Point(EdgeWidth - 3, 1), Point(2, H), Point(0, H),
  813.         Point(0, 0), Point(EdgeWidth + 1, 0)]);
  814.  
  815.       Pen.Color := clBtnShadow;
  816.       PolyLine([Point(EdgeWidth - 3, 0), Point(EdgeWidth + 1, 0),
  817.         Point(EdgeWidth - 3, 1), Point(1, H), Point(0, H - 2)]);
  818.  
  819.       Pen.Color := clWindowFrame;
  820.       PolyLine([Point(EdgeWidth, 1), Point(EdgeWidth - 2, 1), Point(2, H),
  821.         Point(-1, H)]);
  822.     end;
  823.   end;
  824.  
  825.   procedure DrawSL(Canvas: TCanvas);
  826.   begin
  827.     with Canvas do
  828.     begin
  829.       Pen.Color := SelectedColor;
  830.       Brush.Color := SelectedColor;
  831.       Polygon([Point(3, 0), Point(EdgeWidth - 1, H), Point(EdgeWidth, H),
  832.         Point(EdgeWidth, 0), Point(3, 0)]);
  833.  
  834.       Pen.Color := clBtnShadow;
  835.       PolyLine([Point(0, 0), Point(4, 0)]);
  836.  
  837.       Pen.Color := clBtnHighlight;
  838.       PolyLine([Point(4, 1), Point(EdgeWidth, H + 1)]);
  839.  
  840.       Pen.Color := clWindowFrame;
  841.       PolyLine([Point(0, 1), Point(3, 1), Point(EdgeWidth - 1, H),
  842.         Point(EdgeWidth, H)]);
  843.     end;
  844.   end;
  845.  
  846.   procedure DrawUR(Canvas: TCanvas);
  847.   begin
  848.     with Canvas do
  849.     begin
  850.       Pen.Color := clBtnShadow;
  851.       PolyLine([Point(-1, 0), Point(EdgeWidth + 1, 0)]);
  852.  
  853.       Pen.Color := UnselectedColor;
  854.       Brush.Color := UnselectedColor;
  855.       Polygon([Point(EdgeWidth - 3, 1), Point(1, H), Point(0, H),
  856.         Point(0, 1), Point(EdgeWidth - 3, 1)]);
  857.  
  858.       { workaround for bug in S3 driver }
  859.       Pen.Color := clBtnShadow;
  860.       PolyLine([Point(-1, 0), Point(EdgeWidth + 1, 0)]);
  861.  
  862.       Pen.Color := clWindowFrame;
  863.       PolyLine([Point(0, 1), Point(EdgeWidth + 1, 1), Point(EdgeWidth - 2, 1),
  864.         Point(2, H), Point(-1, H)]);
  865.     end;
  866.   end;
  867.  
  868. var
  869.   TempList: TImageList;
  870.   SaveHeight: Integer;
  871. begin
  872.   MemBitmap.Canvas.Font := Font;
  873.  
  874.   { Owner }
  875.   SaveHeight := FTabHeight;
  876.   try
  877.     if FStyle = tsOwnerDraw then FTabHeight := FOwnerDrawHeight
  878.     else FTabHeight := MemBitmap.Canvas.TextHeight('T') + 4;
  879.  
  880.     H := FTabHeight - 1;
  881.  
  882.     TempList := TImageList.CreateSize(EdgeWidth, FTabHeight); {exceptions}
  883.   except
  884.     FTabHeight := SaveHeight;
  885.     raise;
  886.   end;
  887.   ImageList.Free;
  888.   ImageList := TempList;
  889.  
  890.   Working := TBitmap.Create;
  891.   try
  892.     Working.Width := EdgeWidth;
  893.     Working.Height := FTabHeight;
  894.     MaskColor := clOlive;
  895.  
  896.     for EdgePart := Low(TEdgePart) to High(TEdgePart) do
  897.     begin
  898.       with Working.Canvas do
  899.       begin
  900.         Brush.Color := MaskColor;
  901.         Brush.Style := bsSolid;
  902.         FillRect(Rect(0, 0, EdgeWidth, FTabHeight));
  903.       end;
  904.       case EdgePart of
  905.         epSelectedLeft: DrawSL(Working.Canvas);
  906.         epUnselectedLeft: DrawUL(Working.Canvas);
  907.         epSelectedRight: DrawSR(Working.Canvas);
  908.         epUnselectedRight: DrawUR(Working.Canvas);
  909.       end;
  910.       ImageList.AddMasked(Working, MaskColor);
  911.     end;
  912.   finally
  913.     Working.Free;
  914.   end;
  915. end;
  916.  
  917. procedure TTabSet.PaintEdge(X, Y, H: Integer; Edge: TEdgeType);
  918. begin
  919.   MemBitmap.Canvas.Brush.Color := clWhite;
  920.   MemBitmap.Canvas.Font.Color := clBlack;
  921.   case Edge of
  922.     etFirstIsSel:
  923.       ImageList.Draw(MemBitmap.Canvas, X, Y, Ord(epSelectedLeft));
  924.     etLastIsSel:
  925.       ImageList.Draw(MemBitmap.Canvas, X, Y, Ord(epSelectedRight));
  926.     etFirstNotSel:
  927.       ImageList.Draw(MemBitmap.Canvas, X, Y, Ord(epUnselectedLeft));
  928.     etLastNotSel:
  929.       ImageList.Draw(MemBitmap.Canvas, X, Y, Ord(epUnselectedRight));
  930.     etNotSelToSel:
  931.       begin
  932.         ImageList.Draw(MemBitmap.Canvas, X, Y, Ord(epUnselectedRight));
  933.          ImageList.Draw(MemBitmap.Canvas, X, Y, Ord(epSelectedLeft));
  934.       end;
  935.     etSelToNotSel:
  936.       begin
  937.         ImageList.Draw(MemBitmap.Canvas, X, Y, Ord(epUnselectedLeft));
  938.          ImageList.Draw(MemBitmap.Canvas, X, Y, Ord(epSelectedRight));
  939.       end;
  940.     etNotSelToNotSel:
  941.       begin
  942.         ImageList.Draw(MemBitmap.Canvas, X, Y, Ord(epUnselectedLeft));
  943.          ImageList.Draw(MemBitmap.Canvas, X, Y, Ord(epUnselectedRight));
  944.       end;
  945.   end;
  946. end;
  947.  
  948. procedure TTabSet.CreateBrushPattern(Bitmap: TBitmap);
  949. var
  950.   X, Y: Integer;
  951. begin
  952.   Bitmap.Width := 8;
  953.   Bitmap.Height := 8;
  954.   with Bitmap.Canvas do
  955.   begin
  956.     Brush.Style := bsSolid;
  957.     Brush.Color := FBackgroundColor;
  958.     FillRect(Rect(0, 0, Width, Height));
  959.     if FDitherBackground then
  960.       for Y := 0 to 7 do
  961.         for X := 0 to 7 do
  962.           if (Y mod 2) = (X mod 2) then  { toggles between even/odd pixles }
  963.             Pixels[X, Y] := clWhite;     { on even/odd rows }
  964.   end;
  965. end;
  966.  
  967. procedure TTabSet.FixTabPos;
  968. var
  969.   FLastVisibleTab: Integer;
  970.  
  971.   function GetRightSide: Integer;
  972.   begin
  973.     Result := Width - EndMargin;
  974.     if AutoScroll and (FVisibleTabs < Tabs.Count - 1) then
  975.       Dec(Result, Scroller.Width + 4);
  976.   end;
  977.  
  978.   function ReverseCalcNumTabs(Start, Stop: Integer; Canvas: TCanvas;
  979.     Last: Integer): Integer;
  980.   var
  981.     W: Integer;
  982.   begin
  983.     if HandleAllocated then
  984.     begin
  985.       Result := Last;
  986.       while (Start >= Stop) and (Result >= 0) do
  987.         with Canvas do
  988.         begin
  989.           W := TextWidth(Tabs[Result]);
  990.           if (FStyle = tsOwnerDraw) then MeasureTab(Result, W);
  991.           Dec(Start, W + EdgeWidth);    { next usable position }
  992.           if Start >= Stop then Dec(Result);
  993.         end;
  994.      if (Start < Stop) or (Result < 0) then Inc(Result);
  995.     end else Result := FFirstIndex;
  996.   end;
  997.  
  998. begin
  999.   if Tabs.Count > 0 then
  1000.   begin
  1001.     FLastVisibleTab := FFirstIndex + FVisibleTabs - 1;
  1002.     if FTabIndex > FLastVisibleTab then
  1003.       FFirstIndex := ReverseCalcNumTabs(GetRightSide, StartMargin + EdgeWidth,
  1004.         Canvas, FTabIndex)
  1005.     else if (FTabIndex >= 0) and (FTabIndex < FFirstIndex) then
  1006.       FFirstIndex := FTabIndex;
  1007.   end;
  1008. end;
  1009.  
  1010. procedure TTabSet.SetSelectedColor(Value: TColor);
  1011. begin
  1012.   if Value <> FSelectedColor then
  1013.   begin
  1014.     FSelectedColor := Value;
  1015.     CreateEdgeParts;
  1016.     Invalidate;
  1017.   end;
  1018. end;
  1019.  
  1020. procedure TTabSet.SetUnselectedColor(Value: TColor);
  1021. begin
  1022.   if Value <> FUnselectedColor then
  1023.   begin
  1024.     FUnselectedColor := Value;
  1025.     CreateEdgeParts;
  1026.     Invalidate;
  1027.   end;
  1028. end;
  1029.  
  1030. procedure TTabSet.SetBackgroundColor(Value: TColor);
  1031. begin
  1032.   if Value <> FBackgroundColor then
  1033.   begin
  1034.     FBackgroundColor := Value;
  1035.     CreateBrushPattern(BrushBitmap);
  1036.     MemBitmap.Canvas.Brush.Style := bsSolid;
  1037.     Invalidate;
  1038.   end;
  1039. end;
  1040.  
  1041. procedure TTabSet.SetDitherBackground(Value: Boolean);
  1042. begin
  1043.   if Value <> FDitherBackground then
  1044.   begin
  1045.     FDitherBackground := Value;
  1046.     CreateBrushPattern(BrushBitmap);
  1047.     MemBitmap.Canvas.Brush.Style := bsSolid;
  1048.     Invalidate;
  1049.   end;
  1050. end;
  1051.  
  1052. procedure TTabSet.SetAutoScroll(Value: Boolean);
  1053. begin
  1054.   if Value <> FAutoScroll then
  1055.   begin
  1056.     FAutoScroll := Value;
  1057.     Scroller.Visible := False;
  1058.     ShowWindow(Scroller.Handle, SW_HIDE);
  1059.     Invalidate;
  1060.   end;
  1061. end;
  1062.  
  1063. procedure TTabSet.SetStartMargin(Value: Integer);
  1064. begin
  1065.   if Value <> FStartMargin then
  1066.   begin
  1067.     FStartMargin := Value;
  1068.     Invalidate;
  1069.   end;
  1070. end;
  1071.  
  1072. procedure TTabSet.SetEndMargin(Value: Integer);
  1073. begin
  1074.   if Value <> FEndMargin then
  1075.   begin
  1076.     FEndMargin := Value;
  1077.     Invalidate;
  1078.   end;
  1079. end;
  1080.  
  1081. function TTabSet.CanChange(NewIndex: Integer): Boolean;
  1082. begin
  1083.   Result := True;
  1084.   if Assigned(FOnChange) then
  1085.     FOnChange(Self, NewIndex, Result);
  1086. end;
  1087.  
  1088. procedure TTabSet.SetTabIndex(Value: Integer);
  1089. begin
  1090.   if Value <> FTabIndex then
  1091.   begin
  1092.     if (Value < -1) or (Value >= Tabs.Count) then
  1093.       raise Exception.CreateRes(SInvalidTabIndex);
  1094.     if CanChange(Value) then
  1095.     begin
  1096.       FTabIndex := Value;
  1097.       FixTabPos;
  1098.       Click;
  1099.       Invalidate;
  1100.     end;
  1101.   end;
  1102. end;
  1103.  
  1104. procedure TTabSet.SelectNext(Direction: Boolean);
  1105. var
  1106.   NewIndex: Integer;
  1107. begin
  1108.   if Tabs.Count > 1 then
  1109.   begin
  1110.     NewIndex := TabIndex;
  1111.     if Direction then
  1112.       Inc(NewIndex)
  1113.     else Dec(NewIndex);
  1114.     if NewIndex = Tabs.Count then
  1115.       NewIndex := 0
  1116.     else if NewIndex < 0 then
  1117.       NewIndex := Tabs.Count - 1;
  1118.     SetTabIndex(NewIndex);
  1119.   end;
  1120. end;
  1121.  
  1122. procedure TTabSet.SetFirstIndex(Value: Integer);
  1123. begin
  1124.   if (Value >= 0) and (Value < Tabs.Count) then
  1125.   begin
  1126.     FFirstIndex := Value;
  1127.     Invalidate;
  1128.   end;
  1129. end;
  1130.  
  1131. procedure TTabSet.SetTabList(Value: TStrings);
  1132. begin
  1133.   FTabs.Assign(Value);
  1134.   FTabIndex := -1;
  1135.   if FTabs.Count > 0 then TabIndex := 0
  1136.   else Invalidate;
  1137. end;
  1138.  
  1139. function TTabSet.GetTabCount: Integer;
  1140. begin
  1141.   Result := FTabs.Count;
  1142. end;
  1143.  
  1144. function TTabSet.GetTabName(Value: Integer): String;
  1145. begin
  1146.   if (Value >= 0) and (Value < Tabs.Count) then Result := Tabs[Value]
  1147.   else Result := '';
  1148. end;
  1149.  
  1150. procedure TTabSet.SetTabName(Value: Integer; const AName: String);
  1151. begin
  1152.   if (Value >= 0) and (Value < Tabs.Count) and (GetTabName(Value) <> AName) then
  1153.     Tabs[Value] := AName;
  1154. end;
  1155.  
  1156. procedure TTabSet.SetTabStyle(Value: TTabStyle);
  1157. begin
  1158.   if Value <> FStyle then
  1159.   begin
  1160.     FStyle := Value;
  1161.     CreateEdgeParts;
  1162.     Invalidate;
  1163.   end;
  1164. end;
  1165.  
  1166. procedure TTabSet.SetTabHeight(Value: Integer);
  1167. var
  1168.   SaveHeight: Integer;
  1169. begin
  1170.   if Value <> FOwnerDrawHeight then
  1171.   begin
  1172.     SaveHeight := FOwnerDrawHeight;
  1173.     try
  1174.       FOwnerDrawHeight := Value;
  1175.       CreateEdgeParts;
  1176.       Invalidate;
  1177.     except
  1178.       FOwnerDrawHeight := SaveHeight;
  1179.       raise;
  1180.     end;
  1181.   end;
  1182. end;
  1183.  
  1184. procedure TTabSet.DrawTab(TabCanvas: TCanvas; R: TRect; Index: Integer;
  1185.   Selected: Boolean);
  1186. begin
  1187.   if Assigned(FOnDrawTab) then
  1188.     FOnDrawTab(Self, TabCanvas, R, Index, Selected);
  1189. end;
  1190.  
  1191. procedure TTabSet.GetChildren(Proc: TGetChildProc);
  1192. begin
  1193. end;
  1194.  
  1195. procedure TTabSet.MeasureTab(Index: Integer; var TabWidth: Integer);
  1196. begin
  1197.   if Assigned(FOnMeasureTab) then
  1198.     FOnMeasureTab(Self, Index, TabWidth);
  1199. end;
  1200.  
  1201. procedure TTabSet.MouseDown(Button: TMouseButton; Shift: TShiftState;
  1202.   X, Y: Integer);
  1203. var
  1204.   TabPos: TTabPos;
  1205.   I: Integer;
  1206.   Extra: Integer;
  1207.   MinLeft: Integer;
  1208.   MaxRight: Integer;
  1209. begin
  1210.   inherited MouseDown(Button, Shift, X, Y);
  1211.   if (Button = mbLeft) and (Y <= FTabHeight) then
  1212.   begin
  1213.     if Y < FTabHeight div 2 then Extra := EdgeWidth div 3
  1214.     else Extra := EdgeWidth div 2;
  1215.  
  1216.     for I := 0 to TabPositions.Count - 1 do
  1217.     begin
  1218.       Pointer(TabPos) := TabPositions[I];
  1219.       MinLeft := TabPos.StartPos - Extra;
  1220.       MaxRight := TabPos.StartPos + TabPos.Size + Extra;
  1221.       if (X >= MinLeft) and (X <= MaxRight) then
  1222.       begin
  1223.         SetTabIndex(FirstIndex + I);
  1224.         Break;
  1225.       end;
  1226.     end;
  1227.   end;
  1228. end;
  1229.  
  1230. procedure TTabSet.WMSize(var Message: TWMSize);
  1231. var
  1232.   NumVisTabs, LastTabPos: Integer;
  1233.  
  1234.   function CalcNumTabs(Start, Stop: Integer; Canvas: TCanvas;
  1235.     First: Integer): Integer;
  1236.   var
  1237.     W: Integer;
  1238.   begin
  1239.     Result := First;
  1240.     while (Start < Stop) and (Result < Tabs.Count) do
  1241.       with Canvas do
  1242.       begin
  1243.         W := TextWidth(Tabs[Result]);
  1244.         if (FStyle = tsOwnerDraw) then MeasureTab(Result, W);
  1245.         Inc(Start, W + EdgeWidth);    { next usable position }
  1246.         if Start <= Stop then Inc(Result);
  1247.       end;
  1248.   end;
  1249.  
  1250. begin
  1251.   inherited;
  1252.   if Tabs.Count > 1 then
  1253.   begin
  1254.     LastTabPos := Width - EndMargin;
  1255.     NumVisTabs := CalcNumTabs(StartMargin + EdgeWidth, LastTabPos, Canvas, 0);
  1256.     if (FTabIndex = Tabs.Count) or (NumVisTabs > FVisibleTabs) or
  1257.       (NumVisTabs = Tabs.Count) then FirstIndex := Tabs.Count - NumVisTabs;
  1258.     FDoFix := True;
  1259.   end;
  1260.   Invalidate;
  1261. end;
  1262.  
  1263. procedure TTabSet.CMSysColorChange(var Message: TMessage);
  1264. begin
  1265.   inherited;
  1266.   CreateEdgeParts;
  1267.   CreateBrushPattern(BrushBitmap);
  1268.   MemBitmap.Canvas.Brush.Style := bsSolid;
  1269.   { Windows follows this message with a WM_PAINT }
  1270. end;
  1271.  
  1272. procedure TTabSet.CMFontChanged(var Message: TMessage);
  1273. begin
  1274.   inherited;
  1275.   Canvas.Font := Font;
  1276.   CreateEdgeParts;
  1277.   Invalidate;
  1278. end;
  1279.  
  1280. procedure TTabSet.WMGetDlgCode(var Message: TWMGetDlgCode);
  1281. begin
  1282.   Message.Result := DLGC_WANTALLKEYS;
  1283. end;
  1284.  
  1285. procedure TTabSet.CMDialogChar(var Message: TCMDialogChar);
  1286. var
  1287.   I: Integer;
  1288. begin
  1289.   for I := 0 to FTabs.Count - 1 do
  1290.   begin
  1291.     if IsAccel(Message.CharCode, FTabs[I]) then
  1292.     begin
  1293.       Message.Result := 1;
  1294.       if FTabIndex <> I then
  1295.         SetTabIndex(I);
  1296.       Exit;
  1297.     end;
  1298.   end;
  1299.   inherited;
  1300. end;
  1301.  
  1302. procedure TTabSet.DefineProperties(Filer: TFiler);
  1303. begin
  1304.   { Can be removed after version 1.0 }
  1305.   if Filer is TReader then inherited DefineProperties(Filer);
  1306.   Filer.DefineProperty('TabOrder', ReadIntData, nil, False);
  1307.   Filer.DefineProperty('TabStop', ReadBoolData, nil, False);
  1308. end;
  1309.  
  1310. procedure TTabSet.ReadIntData(Reader: TReader);
  1311. begin
  1312.   Reader.ReadInteger;
  1313. end;
  1314.  
  1315. procedure TTabSet.ReadBoolData(Reader: TReader);
  1316. begin
  1317.   Reader.ReadBoolean;
  1318. end;
  1319.  
  1320. end.
  1321.  
  1322.