www.pudn.com > mfcdraw.zip > atabctrl.pas


{************* 
 * 
 * Project....: Experiences and Examples 
 * File.......: ATabCtrl.pas 
 * Created by : Anton A. Mints 
 * Date.......: 1 December 1996 
 * Purpose....: Tab Control with glyph on the tabs. 
 * 
 * $Archive: /delphi experiences/tcontrol with pictures/atabctrl.pas $ 
 * 
 * $History: atabctrl.pas $ 
 *  
 * *****************  Version 1  ***************** 
 * User: Anton        Date: 12/21/96   Time: 8:37p 
 * Created in $/delphi experiences/tcontrol with pictures 
 * My own developed cool component 
 * 
 *************} 
 
unit 
  ATabCtrl; 
 
interface 
 
uses 
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, 
  ComCtrls, CommCtrl, StdCtrls, Tabs; 
 
type 
  TCEODrawTabEvent = procedure (Sender: TObject; 
    TabCanvas: TCanvas; const RectFg, RectBg: TRect; 
    Index: Integer; ActiveTab: Boolean) of object; 
 
  TCEOSheetColor = (scDefault, scSameAsTab); 
 
{ TCEOTabControl } 
  TCEOTabControl = class (TTabControl) 
  private 
    FStyle: TTabStyle; 
    FSheetColor: TColor; 
    FCanvas: TCanvas; 
    FImages: TImageList; 
    FImageChangeLink: TChangeLink; 
 
    FTabActiveColor: TColor; 
    FTabActiveFont: TFont; 
    FSheetStyle: TCEOSheetColor; 
 
    FOnDrawTab: TCEODrawTabEvent; 
 
    procedure ImageListChange (Sender: TObject); 
    procedure SetImages (Value: TImageList); 
 
    procedure SetImage (Index: Integer; imIndex: Integer); 
    function GetImage (Index: Integer): Integer; 
 
    procedure SetStyle (Value: TTabStyle); 
    procedure SetTabActiveColor (Value: TColor); 
    procedure SetTabActiveFont (Value: TFont); 
    procedure SetSheetStyle (Value: TCEOSheetColor); 
 
    procedure CNDrawTab(var Message: TWMDrawItem); message CN_DRAWITEM; 
    procedure CMParentColorChanged(var Message: TMessage); message CM_PARENTCOLORCHANGED; 
    procedure CMParentFontChanged(var Message: TMessage); message CM_PARENTFONTCHANGED; 
    procedure WMEraseBkgnd(var Message: TWMEraseBkgnd); message WM_ERASEBKGND; 
 
  protected 
    procedure CreateParams(var Params: TCreateParams); override; 
 
    procedure Invalidate; override; 
    procedure Change; override; 
 
  public 
    constructor Create (AOwner: TComponent); override; 
    destructor Destroy; override; 
 
    procedure DefaultDrawTab (RectFg, RectBg: TRect; 
      Index: Integer; ActiveTab: Boolean); 
 
    procedure DrawTab (TabCanvas: TCanvas; const RectFg, RectBg: TRect; 
      Index: Integer; ActiveTab: Boolean); virtual; 
 
    function GetSheetRect: TRect; virtual; 
 
    property Image[Index: Integer]: Integer read GetImage write SetImage; 
    property Canvas: TCanvas read FCanvas; 
 
  published 
    property Images: TImageList read FImages write SetImages; 
    property Style: TTabStyle read FStyle write SetStyle; 
 
    property ParentColor; 
    property Color; 
 
    property TabActiveColor: TColor read FTabActiveColor 
        write SetTabActiveColor; 
 
    property TabActiveFont: TFont read FTabActiveFont 
        write SetTabActiveFont; 
 
    property SheetStyle: TCEOSheetColor read FSheetStyle 
        write SetSheetStyle; 
 
    property OnDrawTab: TCEODrawTabEvent read FOnDrawTab write FOnDrawTab; 
  end; 
 
procedure Register; 
 
implementation 
 
procedure Register; 
begin 
  RegisterComponents('Additional', [TCEOTabControl]); 
end; 
 
{*******************************************************************} 
{*                     CLASS TCEOTabControl                        *} 
{*******************************************************************} 
 
constructor TCEOTabControl.Create (AOwner: TComponent); 
begin 
  inherited Create (AOwner); 
  FImageChangeLink := TChangeLink.Create; 
  FImageChangeLink.OnChange := ImageListChange; 
 
  FCanvas := TControlCanvas.Create; 
  TControlCanvas(FCanvas).Control := Self; 
 
  FTabActiveFont := TFont.Create; 
  FTabActiveFont.Assign (Font); 
  FTabActiveColor := Color; 
  FSheetColor := Color; 
end; 
 
destructor TCEOTabControl.Destroy; 
begin 
  FTabActiveFont.Free; 
  FCanvas.Free; 
  FImageChangeLink.Free; 
  inherited Destroy; 
end; 
 
 
{************* 
* Name.......: CreateParams 
* Description: Set Owner Draw style if we have to 
*} 
procedure TCEOTabControl.CreateParams(var Params: TCreateParams); 
begin 
  inherited CreateParams(Params); 
 
  if ( FStyle = tsOwnerDraw ) then 
    Params.Style := Params.Style or TCS_OWNERDRAWFIXED; 
end; 
 
 
{************* 
* Name.......: Invalidate 
*} 
procedure TCEOTabControl.Invalidate; 
begin 
  LockWindowUpdate (Canvas.Handle); 
  inherited Invalidate; 
  LockWindowUpdate (0); 
end; 
 
 
{************* 
* Name.......: Change 
* Description: Redraw sheet area if we have AsTabColot setting. 
*} 
procedure TCEOTabControl.Change; 
var 
  Rect : TRect; 
begin 
  inherited Change; 
  if ( FSheetStyle <> scDefault ) then 
  begin 
    Rect := GetSheetRect; 
    InvalidateRect (Handle, @Rect, True); 
  end; 
end; 
 
 
{************* 
* Name.......: ImageListChange 
* Description: Be called whenever a change in the ImageList occurs to 
*             notify TabControl about the changes. 
*} 
procedure TCEOTabControl.ImageListChange(Sender: TObject); 
begin 
  if HandleAllocated then 
    SendMessage(Handle, TCM_SETIMAGELIST, 0, Longint(TImageList(Sender).Handle)); 
end; 
 
 
{************* 
* Name.......: SetImages 
* Description: Assign ImageList to internal class variable and register 
*              TChangeLink object. 
*} 
procedure TCEOTabControl.SetImages (Value: TImageList); 
begin 
  if Assigned (FImages) then 
    Images.UnRegisterChanges(FImageChangeLink); 
 
  FImages := Value; 
 
  if Assigned (FImages) then 
    begin 
      Images.RegisterChanges (FImageChangeLink); 
      SendMessage(Handle, TCM_SETIMAGELIST, 0, Longint(Images.Handle)); 
    end 
  else 
    SendMessage(Handle, TCM_SETIMAGELIST, 0, Longint(0)); 
end; 
 
 
{************* 
* Name.......: SetImage 
* Description: Display image from ImageList on Index tab. 
*} 
procedure TCEOTabControl.SetImage (Index: Integer; imIndex: Integer); 
var 
  imItem: TTCItem; 
begin 
  imItem.iImage := imIndex; 
  imItem.mask := TCIF_IMAGE; 
  SendMessage (Handle, TCM_SETITEM, Index, Longint(@imItem)); 
end; 
 
 
{************* 
* Name.......: GetImage 
*} 
function TCEOTabControl.GetImage (Index: Integer): Integer; 
var 
  imItem: TTCItem; 
begin 
  if Assigned (FImages) then 
    begin 
      imItem.mask := TCIF_IMAGE; 
      SendMessage (Handle, TCM_GETITEM, Index, Longint(@imItem)); 
      Result := imItem.iImage; 
    end 
  else 
    Result := -1 
end; 
 
 
{************* 
* Name.......: SetStyle 
*} 
procedure TCEOTabControl.SetStyle (Value: TTabStyle); 
begin 
  // If style wasn't changed, do nothing 
  if ( Style <> Value ) then 
  begin 
    FStyle := Value; 
    RecreateWnd; 
  end; 
end; 
 
 
{************* 
* Name.......: SetTabActiveColor 
*} 
procedure TCEOTabControl.SetTabActiveColor (Value: TColor); 
begin 
  if FTabActiveColor <> Value then 
  begin 
    FTabActiveColor := Value; 
    Invalidate; 
  end; 
end; 
 
 
{************* 
* Name.......: SetTabActiveFont 
*} 
procedure TCEOTabControl.SetTabActiveFont (Value: TFont); 
begin 
  FTabActiveFont.Assign (Value); 
  Invalidate; 
end; 
 
 
{************* 
* Name.......: SetSheetColor 
*} 
procedure TCEOTabControl.SetSheetStyle (Value: TCEOSheetColor); 
begin 
  if ( FSheetStyle <> Value ) then 
  begin 
    FSheetStyle := Value; 
    Invalidate; 
  end; 
end; 
 
 
{************* 
* Name.......: GetSheetRect 
* Description: Return sheet rectangle 
*} 
function TCEOTabControl.GetSheetRect: TRect; 
var 
  TabRect: TRect; 
 
begin 
  // Get tab rectangle 
  SendMessage (Handle, TCM_GETITEMRECT, 0, Longint(@TabRect)); 
  // Calculate sheet rectangle 
  Result := Rect (2, TabRect.Bottom - TabRect.Top + 4, Width-3, Height-2); 
end; 
 
 
{************* 
* Name.......: DefaultDrawTab 
*} 
procedure TCEOTabControl.DefaultDrawTab (RectFg, RectBg: TRect; 
  Index: Integer; ActiveTab: Boolean); 
 
  {************* 
  * Name.......: DrawTabBkGround 
  *} 
  procedure DrawTabBkGround; 
  begin 
    Canvas.FillRect (RectBg); 
  end; 
 
  {************* 
  * Name.......: DrawTabImage 
  *} 
  procedure DrawTabImage; 
  var 
    ImageIndex: Integer; 
  begin 
    // Get image accotiateg with the current tab 
    ImageIndex := GetImage (Index); 
 
    // Draw image on tab 
    if ( ImageIndex >= 0 ) then 
    begin 
      FImages.Draw (Canvas, RectFg.Left + 6, 
        RectFg.Top + ((RectFg.Bottom - RectFg.Top - FImages.Height) div 2), ImageIndex); 
      RectFg.Left := RectFg.Left + 6 + FImages.Width; 
    end; 
  end; 
 
  {************* 
  * Name.......: DrawTabText 
  *} 
  procedure DrawTabText; 
  begin 
    DrawText (Canvas.Handle, PChar(Tabs[Index]), 
      Length(Tabs[Index]), RectFg, DT_SINGLELINE or DT_CENTER or DT_VCENTER); 
  end; 
 
  {************* 
  * Name.......: DrawTabSheet 
  *} 
  procedure DrawTabSheet; 
  begin 
    if ( FSheetStyle = scDefault ) then 
      FSheetColor := Color 
    else 
      FSheetColor := Canvas.Brush.Color; 
    Canvas.Brush.Color := FSheetColor; 
    Canvas.FillRect (GetSheetRect); 
  end; 
 
begin 
{ 
  if ActiveTab then 
  begin 
    if ( FSheetStyle = scDefault ) then 
      FSheetColor := Color 
    else 
      FSheetColor := Canvas.Brush.Color; 
  end; 
} 
  if ( Index < 0 ) then Exit; 
 
  DrawTabBkGround; 
  DrawTabImage; 
  DrawTabText; 
  if ActiveTab then DrawTabSheet; 
end; 
 
 
{************* 
* Name.......: DrawTab 
* Description: Call event handler if was set, 
*             or use default draw method to draw tab 
*} 
procedure TCEOTabControl.DrawTab (TabCanvas: TCanvas; const RectFg, RectBg: TRect; 
  Index: Integer; ActiveTab: Boolean); 
begin 
  if Assigned(FOnDrawTab) then 
    FOnDrawTab (Self, Canvas, RectFg, RectBg, Index, ActiveTab) 
  else 
    DefaultDrawTab (RectFg, RectBg, Index, ActiveTab); 
end; 
 
 
{************* 
* Name.......: CNDrawTab (Message CN_DRAWTAB) 
* Description: This message is sent when eacth tab needs to be repainted. 
*} 
procedure TCEOTabControl.CNDrawTab (var Message: TWMDrawItem); 
var 
  SaveIndex: Integer; 
  State: TOwnerDrawState; 
  RectFg, RectBg: TRect; 
  IsActiveTab: Boolean; 
 
  function AdjustRect (Rect: TRect; ALeft, ATop, ARight, ABottom: Integer): TRect; 
  begin 
    Result.Left := Rect.Left + ALeft; 
    Result.Top := Rect.Top + ATop; 
    Result.Right := Rect.Right + ARight; 
    Result.Bottom := Rect.Bottom + ABottom; 
  end; 
 
begin 
  with Message.DrawItemStruct^ do 
  begin 
    SaveIndex := SaveDC(hDC); 
    FCanvas.Handle := hDC; 
 
    { 
    State := []; 
    if (itemState and ODS_SELECTED) = ODS_SELECTED then 
      State := State + [odSelected]; 
    if ( itemState and ODS_DISABLED ) = ODS_DISABLED then 
      State := State + [odDisabled]; 
    if ( itemState and ODS_FOCUS ) = ODS_FOCUS then 
      State := State + [odFocused]; 
    } 
 
    // Define if we draw the Active tab 
    if ( itemID < 0 ) then 
      IsActiveTab := False 
    else 
      IsActiveTab := ( itemID = TabIndex ); 
 
    if IsActiveTab then 
      begin 
        FCanvas.Font := TabActiveFont; 
        FCanvas.Brush.Color := TabActiveColor; 
      end 
    else 
      begin 
        FCanvas.Font := Font; 
        FCanvas.Brush.Color := Color; 
      end; 
 
    FCanvas.Brush.Style := bsSolid; 
 
 
    // Adjust the rectangle size a little bit 
    if ( IsActiveTab ) then 
      begin 
        RectFg := AdjustRect (rcItem, 2, 1, -2, -3); 
        RectBg := AdjustRect (rcItem, 0, 0,  0,  0); 
      end 
    else 
      begin 
        RectFg := AdjustRect (rcItem, -2, 0, 0, 3); 
        RectBg := AdjustRect (rcItem, -2, 1, 0, 2); 
      end; 
 
    // Draw tab 
    DrawTab (FCanvas, RectFg, RectBg, itemID, IsActiveTab); 
 
    FCanvas.Handle := 0; 
    RestoreDC(hDC, SaveIndex); 
  end; 
  Message.Result := 1; 
end; 
 
 
{************* 
* Name.......: CMParentColorChanged (Message CM_PARENTCOLORCHANGED) 
*} 
procedure TCEOTabControl.CMParentColorChanged (var Message: TMessage); 
var 
  IsActiveColorEqual: Boolean; 
 
begin 
  IsActiveColorEqual := ( FTabActiveColor = Color ); 
 
  inherited; 
 
  // If the active tab color was equal tab control color, 
  //change active tab color too. 
  if IsActiveColorEqual and ( FTabActiveColor <> Color ) then 
  begin 
    FTabActiveColor := Color; 
    FSheetColor := Color; 
  end; 
 
  Invalidate; 
end; 
 
 
{************* 
* Name.......: CMParentFontChanged (Message CM_PARENTFONTCHANGED) 
*} 
procedure TCEOTabControl.CMParentFontChanged (var Message: TMessage); 
var 
  IsActiveFontEqual: Boolean; 
 
  function IsFontEqual (Font1, Font2: TFont): Boolean; 
  begin 
    Result := ( Font1.Name = Font2.Name ) and 
      ( Font1.Size = Font2.Size ) and 
      ( Font1.Style = Font2.Style ) and 
      ( Font1.Color  = Font2.Color ); 
  end; 
 
begin 
  IsActiveFontEqual := IsFontEqual (FTabActiveFont, Font); 
 
  inherited; 
 
  // If the active tab font was equal tab control font, 
  //change active tab font too. 
  if IsActiveFontEqual and not IsFontEqual (FTabActiveFont, Font) then 
    FTabActiveFont.Assign (Font); 
 
  Invalidate; 
end; 
 
 
{************* 
* Name.......: WMEraseBkgnd (Message WM_ERASEBKGND) 
*} 
procedure TCEOTabControl.WMEraseBkgnd (var Message: TWMEraseBkgnd); 
var 
  SaveIndex: Integer; 
 
begin 
  SaveIndex := SaveDC(Message.DC); 
  FCanvas.Handle := Message.DC; 
  FCanvas.Font := Font; 
  FCanvas.Brush.Color := FSheetColor; 
  FCanvas.Brush.Style := bsSolid; 
  FillRect (FCanvas.Handle, ClientRect, Parent.Brush.Handle); 
  FillRect (FCanvas.Handle, GetSheetRect, FCanvas.Brush.Handle); 
  FCanvas.Handle := 0; 
  RestoreDC(Message.DC, SaveIndex); 
  Message.Result := 1; 
end; 
 
end.