Componentes.Terceros.SpTBXLib/internal/2.4.4/1/Source/SpTBXTabs.pas
2010-01-19 16:32:53 +00:00

3494 lines
113 KiB
ObjectPascal

unit SpTBXTabs;
{==============================================================================
Version 2.4.4
The contents of this file are subject to the SpTBXLib License; you may
not use or distribute this file except in compliance with the
SpTBXLib License.
A copy of the SpTBXLib License may be found in SpTBXLib-LICENSE.txt or at:
http://www.silverpointdevelopment.com/sptbxlib/SpTBXLib-LICENSE.htm
Alternatively, the contents of this file may be used under the terms of the
Mozilla Public License Version 1.1 (the "MPL v1.1"), in which case the provisions
of the MPL v1.1 are applicable instead of those in the SpTBXLib License.
A copy of the MPL v1.1 may be found in MPL-LICENSE.txt or at:
http://www.mozilla.org/MPL/
If you wish to allow use of your version of this file only under the terms of
the MPL v1.1 and not to allow others to use your version of this file under the
SpTBXLib License, indicate your decision by deleting the provisions
above and replace them with the notice and other provisions required by the
MPL v1.1. If you do not delete the provisions above, a recipient may use your
version of this file under either the SpTBXLib License or the MPL v1.1.
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See the License for
the specific language governing rights and limitations under the License.
The initial developer of this code is Robert Lee.
Requirements:
For Delphi/C++Builder 2009 or newer:
- Jordan Russell's Toolbar 2000
http://www.jrsoftware.org
For Delphi/C++Builder 7-2007:
- Jordan Russell's Toolbar 2000
http://www.jrsoftware.org
- Troy Wolbrink's TNT Unicode Controls
http://www.tntware.com/delphicontrols/unicode/
Development Notes:
- All the theme changes and adjustments are marked with '[Theme-Change]'.
- When an item is hidden the ItemViewer.BoundsRect property is invalid.
- tbicDeleting item notification is fired after the ItemViewer of the
Item is destroyed by TTBView, but the Items array still has the Item.
TODO:
- Tabbed docking
History:
2 December 2009 - version 2.4.4
- Fixed TabControl flicker when changing the caption of a tab
item, thanks to Simon H. for reporting this.
13 September 2009 - version 2.4.3
- Fixed TabControl flicker when closing/selecting/scrolling
tab items.
- Fixed incorrect TabControl painting when transparent
child controls get invalidated, thanks for Alfred Vink
for reporting this.
- Fixed incorrect TabControl scrolling, thanks to Henner
Drewes for reporting this.
8 May 2009 - version 2.4.2
- Fixed incorrect TabControl behavior, when deleting
the tabs the ActiveTabIndex is incorrectly set if
there are regular Items on the control, thanks to
Jonah for reporting this.
15 March 2009 - version 2.4.1
- No changes.
17 January 2009 - version 2.4
- No changes.
26 September 2008 - version 2.3
- No changes.
29 July 2008 - version 2.2
- No changes.
26 June 2008 - version 2.1
- No changes.
3 May 2008 - version 2.0
- Fixed incorrect close button painting on tab items,
thanks to Miha for reporting this.
2 April 2008 - version 1.9.5
- Fixed incorrect TabControl behavior, when deleting the
only visible Tab all the auto-hidden tabs will not
be showed on resize, thanks to Jim for reporting this.
3 February 2008 - version 1.9.4
- Fixed AV on TabControl when scrolling tabs, thanks
to Beta Xiong and Yucel Yavuz for reporting this.
- Fixed incorrect TabControl.ScrollState behavior
thanks to Michele for reporting this.
19 January 2008 - version 1.9.3
- Fixed recursion on TSpTBXTabToolbar.RightAlignItems,
thanks to Jim for reporting this.
26 December 2007 - version 1.9.2
- Minor bug fixes.
1 December 2007 - version 1.9.1
- Added OnTabClosing event to TSpTBXTabItem.
- Fixed incorrect tab items painting when TabPosition was
ttpBottom, thanks to Marko Savolainen for reporting this.
20 November 2007 - version 1.9
- Added TabBackgroundBorders property to TSpTBXTabSet and
TSpTBXTabControl, when set to true the tabs area is
painted with borders.
- Added TabCloseButton and TabCloseButtonImageIndex properties
to SpTBXTabSet and TSpTBXTabControl to control the close
button visibility on the tab items.
- Added TabMaxSize property to SpTBXTabSet and TSpTBXTabControl
to control the maximum size of the tab items.
- Improved tab scrolling, thanks to Kevin Lu for
reporting this.
8 February 2007 - version 1.8.3
- Added accel char handling to TSpTBXTabSet and TSpTBXTabControl
17 December 2006 - version 1.8.2
- No changes.
24 November 2006 - version 1.8.1
- Fixed incorrect Tab painting when the Default theme was used
and the ThemeType was tttFlat.
27 August 2006 - version 1.8
- Fixed incorrect OnActiveTabChanging handling when
ActiveTabIndex is changed on this event, thanks to
Serg Chechenin for reporting this.
15 June 2006 - version 1.7
- Fixed incorrect Tab painting when the default theme was used,
the captions were painted in a pushed state, thanks to
Mikalai Arapau for reporting this.
- Fixed incorrect Tab aligning when Autofit was used and the
tab control was parented by a Frame, thanks to
Henk van Kampen for reporting this.
12 April 2006 - version 1.5
- Fixed incorrect Tab painting when TabAutofit was true.
27 February 2006 - version 1.4
- Fixed flicker when reordering TSpTBXTabSet and TSpTBXTabControl
tabs, thanks to Alexey Naumov for reporting this.
- Fixed incorrect context menu handling in TSpTBXTabSet and
TSpTBXTabControl, thanks to Boris Yankov for reporting this.
- Added OnActiveTabReorder event to TSpTBXTabSet and TSpTBXTabControl.
10 February 2006 - version 1.3
- Added TabDragReorder property to TSpTBXTabSet and TSpTBXTabControl,
when this property is true it allows tabs reordering with
drag and drop.
- Added TabAutofit and TabAutofitMaxSize properties to TSpTBXTabSet
and TSpTBXTabControl. When TabAutofit is true the tabs are resized
to fit the tabset.
28 December 2005 - version 1.2
- Fixed incorrect TSpTBXTabControl background painting on some themes.
- Fixed incorrect OnActiveTabChange call when the component is being
loaded, thanks to Leroy Casterline for reporting this.
- Fixed incorrect tab scrolling when an item is deleted, thanks to
Daniel Rikowski for reporting this.
18 October 2005 - version 1.1
- Fixed incorrect TSpTBXTabItem painting on some themes.
- Added Margins property to TSpTBXPageControl.
18 August 2005 - version 1.0
- Added TabVisible property to TSpTBXTabSet and TSpTBXPageControl.
- Added OnActiveTabChanging event to TSpTBXTabSet and TSpTBXPageControl.
10 June 2005 - version 0.9
- SpTBXLib may now alternatively, at your option, be used and/or
distributed under the terms of the SpTBXLib License.
Please see the updated LICENSE.TXT file for more information.
- Fixed AV in TSpTBXTabSet and TSpTBXPageControl when used in a Frame
with TabPosition setted to dpBottom, thanks to Cyril Velter for the fix.
20 May 2005 - version 0.8
- Fixed tab scrolling of TSpTBXTabSet and TSpTBXPageControl, the tabs
were not allowed to scroll when one single tab was visible, thanks
to Anders Olsson for the fix.
- Added MakeVisible method to the TSpTBXTabSet and TSpTBXPageControl,
it scrolls the tabset, if necessary, to ensure a Tab is in view.
16 February 2005 - version 0.7
- No changes.
23 December 2004 - version 0.6
- Fixed TSpTBXTabControl reordering bug.
- Changed the order of the TSpTBXTabThemeType enumerated type.
- Added ActivePage property to the TSpTBXTabControl.
- Added Caption, ImageIndex, TabVisible and PopupMenu properties
to TSpTBXTabControl.
30 August 2004 - version 0.5
- No changes.
21 July 2004 - version 0.4
- Fixed TSpTBXTabControl design time bug, it was allowing to drop
components when ActiveTabIndex = -1
- Fixed TSpTBXTabSet and TSpTBXTabControl design time bug, the
hidden items were not streamed to the DFM.
- Changed TSpTBXTabControl.OnTabClick event for OnActiveTabChange.
- Added GetPage method to TSpTBXTabControl to get the TSpTBXTabSheet
linked to a TSpTBXTabItem.
12 July 2004 - version 0.3.1
- Fixed nasty AV when setting TBXSwitcher.EnableXPStyles to false,
thanks to Alfred for reporting this.
Note: TBXThemeManager unloads the theme library and the theme
parts when some conditions are met, we must handle extra theme
parts outside TBXThemeManager space.
- Fixed incorrect TSpTBXTabSet.ActiveTabIndex property update at
design time.
- Fixed incorrect TSpTBXTabSet painting on some TBX themes, thanks
to Tim for reporting this.
9 July 2004 - version 0.3
- Fixed design time AVs when moving or deleting TabSheets.
- Published ThemeType and TabPosition properties for TSpTBXTabItem.
- New component added, TSpTBXTabSet, a fully customizable TabSet
with unicode and toolbar items support.
28 June 2004 - version 0.2
- No changes.
22 June 2004 - version 0.1
- Initial release.
==============================================================================}
interface
{$BOOLEVAL OFF} // Unit depends on short-circuit boolean evaluation
uses
Windows, Messages, Classes, SysUtils, Controls, Graphics, ImgList, Forms,
Dialogs, ExtCtrls, TB2Item, TB2Dock, TB2Toolbar,
SpTBXSkins, SpTBXItem, SpTBXControls;
const
C_SpTBXTabGroupIndex = 7777;
WM_INVALIDATETABBACKGROUND = WM_USER + 7777;
type
TSpTBXTabEdge = (
tedNone, // No edge needed
tedLeft, // Left edge of the tab
tedRight // Right edge of the tab
);
TSpTBXTabPosition = (
ttpTop, // Top aligned tabset
ttpBottom // Bottom aligned tabset
);
TSpTBXTabCloseButton = (
tcbNone, // No close button on tabs
tcbActive, // Close button only on active tab
tcbAll // Close button on all the tabs
);
TSpTBXTabChangeEvent = procedure(Sender: TObject; TabIndex: Integer) of object;
TSpTBXTabChangingEvent = procedure(Sender: TObject; TabIndex, NewTabIndex: Integer; var Allow: Boolean) of object;
TSpTBXTabClosingEvent = procedure(Sender: TObject; var Allow, CloseAndFree: Boolean) of object;
TSpTBXTabToolbar = class;
TSpTBXCustomTabSet = class;
TSpTBXCustomTabControl = class;
TSpTBXTabSheet = class;
TSpTBXTabItemDragObject = class(TSpTBXCustomDragObject)
public
constructor Create(ASourceControl: TControl; AItem: TTBCustomItem); override;
end;
{ TSpTBXTabItem }
TSpTBXTabItem = class(TSpTBXCustomItem)
private
FSkinType: TSpTBXSkinType;
FOnDrawTabCloseButton: TSpTBXDrawImageEvent;
FOnTabClose: TNotifyEvent;
FOnTabClosing: TSpTBXTabClosingEvent;
function GetTabColor: TColor;
function GetSkinType: TSpTBXSkinType;
procedure SetSkinType(const Value: TSpTBXSkinType);
protected
function DialogChar(CharCode: Word): Boolean; override;
procedure DoDrawTabCloseButton(ACanvas: TCanvas; State: TSpTBXSkinStatesType;
const PaintStage: TSpTBXPaintStage; var AImageList: TCustomImageList;
var AImageIndex: Integer; var ARect: TRect; var PaintDefault: Boolean); virtual;
procedure DoTabClose; virtual;
procedure DoTabClosing(var Allow, CloseAndFree: Boolean); virtual;
function GetItemViewerClass(AView: TTBView): TTBItemViewerClass; override;
function GetTabToolbar(out TabToolbar: TSpTBXTabToolbar): Boolean;
procedure ToggleControl; override;
property Control; // TabSheet
property TabColor: TColor read GetTabColor;
public
constructor Create(AOwner: TComponent); override;
procedure Click; override;
procedure TabClose;
function GetNextTab(GoForward: Boolean; SearchType: TSpTBXSearchItemViewerType): TSpTBXTabItem;
function IsFirstVisible: Boolean;
function IsFirstVisibleTab: Boolean;
function IsLastVisibleTab: Boolean;
published
property Action;
property Checked;
// Hide DisplayMode
// property DisplayMode default nbdmImageAndText;
property Enabled;
// Hide GroupIndex, all the TabItems must have the same GroupIndex
// property GroupIndex;
property HelpContext;
property ImageIndex;
property Images;
property InheritOptions;
property MaskOptions;
property Options;
property ShortCut;
property Visible;
property OnClick;
property OnSelect;
// TSpTBXCustomItem properties
property Alignment;
property CustomWidth;
property CustomHeight;
property Margins default 4;
property MinHeight;
property MinWidth;
property FontSettings;
property Wrapping default twEndEllipsis;
property OnDrawImage;
property OnDrawItem;
property OnDrawHint;
property OnDrawCaption;
// TSpTBXTabItem properties
property SkinType: TSpTBXSkinType read GetSkinType write SetSkinType default sknSkin;
property OnDrawTabCloseButton: TSpTBXDrawImageEvent read FOnDrawTabCloseButton write FOnDrawTabCloseButton;
property OnTabClose: TNotifyEvent read FOnTabClose write FOnTabClose;
property OnTabClosing: TSpTBXTabClosingEvent read FOnTabClosing write FOnTabClosing;
end;
TSpTBXTabItemViewer = class(TSpTBXItemViewer)
private
FTabCloseButtonState: TSpTBXSkinStatesType;
function CorrectTabRect(ARect: TRect): TRect;
function GetItem: TSpTBXTabItem;
procedure GetTabCloseButtonImgList(var AImageList: TCustomImageList; var AImageIndex: Integer);
function IsTabCloseButtonVisible: Boolean;
function GetTabPosition: TSpTBXTabPosition;
protected
procedure CalcSize(const Canvas: TCanvas; var AWidth, AHeight: Integer); override;
procedure DoDrawButton(ACanvas: TCanvas; ARect: TRect; ItemInfo: TSpTBXMenuItemInfo;
const PaintStage: TSpTBXPaintStage; var PaintDefault: Boolean); override;
procedure DoDrawCaption(ACanvas: TCanvas; ClientAreaRect: TRect; State: TSpTBXSkinStatesType;
var ACaption: WideString; var CaptionRect: TRect; var CaptionFormat: Cardinal;
IsTextRotated: Boolean; const PaintStage: TSpTBXPaintStage; var PaintDefault: Boolean); override;
procedure DoDrawImage(ACanvas: TCanvas; State: TSpTBXSkinStatesType;
const PaintStage: TSpTBXPaintStage; var AImageList: TCustomImageList;
var AImageIndex: Integer; var ARect: TRect;
var PaintDefault: Boolean); override;
procedure DoDrawTabCloseButton(ACanvas: TCanvas; State: TSpTBXSkinStatesType;
const PaintStage: TSpTBXPaintStage; var AImageList: TCustomImageList;
var AImageIndex: Integer; var ARect: TRect; var PaintDefault: Boolean); virtual;
procedure DrawBottomBorder(ACanvas: TCanvas; ARect: TRect);
procedure DrawTab(ACanvas: TCanvas; ARect: TRect;
AEnabled, AChecked, AHoverItem: Boolean; Position: TSpTBXTabPosition;
ASeparator: Boolean = False; AEdge: TSpTBXTabEdge = tedNone); virtual;
procedure DrawItemRightImage(ACanvas: TCanvas; ARect: TRect; ItemInfo: TSpTBXMenuItemInfo); override;
function GetRightImageSize: TSize; override;
function GetRightImageRect: TRect;
function GetTextColor(State: TSpTBXSkinStatesType): TColor; override;
procedure InternalMouseMove(Shift: TShiftState; X, Y: Integer); override;
procedure Leaving; override;
public
function IsOnTabToolbar: Boolean;
property Item: TSpTBXTabItem read GetItem; // Hides the inherited TB2K Item property
property TabCloseButtonState: TSpTBXSkinStatesType read FTabCloseButtonState;
property TabPosition: TSpTBXTabPosition read GetTabPosition;
end;
{ TSpTBXTabToolbar }
TSpTBXTabToolbarView = class(TSpTBXToolbarView)
public
procedure BeginUpdate; override;
procedure EndUpdate; override;
end;
TSpTBXTabToolbar = class(TSpTBXToolbar)
private
FActiveTabRect: TRect;
FSkinType: TSpTBXSkinType;
FTabAutofit: Boolean;
FTabAutofitMaxSize: Integer;
FTabCloseButtonImageIndex: Integer;
FTabCloseButton: TSpTBXTabCloseButton;
FTabDragReorder: Boolean;
FTabBackgroundBorders: Boolean;
FTabColor: TColor;
FTabMaxSize: Integer;
FTabPosition: TSpTBXTabPosition;
procedure Scroll(ToRight: Boolean);
function GetActiveTab: TSpTBXTabItem;
procedure SetActiveTabIndex(Value: Integer);
procedure SetSkinType(const Value: TSpTBXSkinType);
procedure SetTabCloseButton(const Value: TSpTBXTabCloseButton);
procedure SetTabCloseButtonImageIndex(const Value: Integer);
procedure SetTabAutofit(const Value: Boolean);
procedure SetTabAutofitMaxSize(const Value: Integer);
procedure SetTabBackgroundBorders(const Value: Boolean);
procedure SetTabColor(const Value: TColor);
procedure SetTabMaxSize(const Value: Integer);
procedure SetTabPosition(const Value: TSpTBXTabPosition);
procedure CMDesignHitTest(var Message: TCMDesignHitTest); message CM_DESIGNHITTEST;
protected
FHiddenTabs: TSpTBXItemCacheCollection;
FActiveTabIndex: Integer;
FOwnerTabControl: TSpTBXCustomTabSet;
procedure Autofit;
function GetItemsTextColor(State: TSpTBXSkinStatesType): TColor; override;
function GetViewClass: TTBToolbarViewClass; override;
procedure InternalDrawBackground(ACanvas: TCanvas; ARect: TRect; PaintOnNCArea: Boolean; PaintBorders: Boolean = True); override;
procedure DoItemNotification(Ancestor: TTBCustomItem; Relayed: Boolean; Action: TTBItemChangedAction; Index: Integer; Item: TTBCustomItem); override;
procedure RightAlignItems; override;
function CanDragCustomize(Button: TMouseButton; Shift: TShiftState; X, Y: Integer): Boolean; override;
procedure DoStartDrag(var DragObject: TDragObject); override;
procedure DragOver(Source: TObject; X: Integer; Y: Integer; State: TDragState; var Accept: Boolean); override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
function GetTabsCount(VisibleOnly: Boolean): Integer;
procedure InvalidateActiveTab;
procedure InvalidateNC;
procedure MakeVisible(ATab: TSpTBXTabItem);
procedure ScrollLeft;
procedure ScrollRight;
procedure ScrollState(out CanScrollToLeft, CanScrollToRight: Boolean);
procedure TabClose(ATab: TSpTBXTabItem);
published
property ActiveTab: TSpTBXTabItem read GetActiveTab;
property ActiveTabIndex: Integer read FActiveTabIndex write SetActiveTabIndex;
property TabCloseButton: TSpTBXTabCloseButton read FTabCloseButton write SetTabCloseButton default tcbNone;
property TabCloseButtonImageIndex: Integer read FTabCloseButtonImageIndex write SetTabCloseButtonImageIndex default -1;
property TabBackgroundBorders: Boolean read FTabBackgroundBorders write SetTabBackgroundBorders;
property TabAutofit: Boolean read FTabAutofit write SetTabAutofit default False;
property TabAutofitMaxSize: Integer read FTabAutofitMaxSize write SetTabAutofitMaxSize default 200;
property TabColor: TColor read FTabColor write SetTabColor default clBtnFace;
property TabMaxSize: Integer read FTabMaxSize write SetTabMaxSize default -1;
property TabPosition: TSpTBXTabPosition read FTabPosition write SetTabPosition default ttpTop;
property TabDragReorder: Boolean read FTabDragReorder write FTabDragReorder default False;
property SkinType: TSpTBXSkinType read FSkinType write SetSkinType default sknSkin;
end;
{ TSpTBXTabSheet }
TSpTBXTabSheet = class(TCustomControl)
private
FTabControl: TSpTBXCustomTabControl;
FItem: TSpTBXTabItem;
FItemName: String;
FPrevFocused: TWincontrol;
procedure ReadItemName(Reader: TReader);
procedure WriteItemName(Writer: TWriter);
function GetCaption: WideString;
function GetTabVisible: Boolean;
procedure SetCaption(const Value: WideString);
procedure SetTabVisible(const Value: Boolean);
function GetImageIndex: Integer;
procedure SetImageIndex(const Value: Integer);
procedure CMVisiblechanged(var Message: TMessage); message CM_VISIBLECHANGED;
procedure WMEraseBkgnd(var Message: TWMEraseBkgnd); message WM_ERASEBKGND;
procedure WMWindowPosChanged(var Message: TWMWindowPosChanged); message WM_WINDOWPOSCHANGED;
protected
procedure CreateParams(var Params: TCreateParams); override;
procedure AdjustClientRect(var Rect: TRect); override;
procedure DefineProperties(Filer: TFiler); override;
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
procedure ReadState(Reader: TReader); override;
procedure VisibleChanging; override;
property Align default alClient;
property PrevFocused: TWincontrol read FPrevFocused;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
property Item: TSpTBXTabItem read FItem write FItem;
property TabControl: TSpTBXCustomTabControl read FTabControl write FTabControl;
published
{$IF CompilerVersion > 15} // For Delphi 2005 and up
property Padding;
property OnAlignInsertBefore;
property OnAlignPosition;
{$IFEND}
property PopupMenu;
property Caption: WideString read GetCaption write SetCaption;
property ImageIndex: Integer read GetImageIndex write SetImageIndex;
property TabVisible: Boolean read GetTabVisible write SetTabVisible default True;
end;
{ TSpTBXTabControl }
TSpTBXCustomTabSet = class(TSpTBXCompoundItemsControl)
private
FItemMoveCount: Integer;
FItemMoved: TSpTBXTabItem;
FTabVisible: Boolean;
FLoadingActiveIndex: Integer;
FUpdatingIndex: Boolean;
FResizing: Boolean;
FOnDrawBackground: TSpTBXDrawEvent;
FOnActiveTabChange: TSpTBXTabChangeEvent;
FOnActiveTabChanging: TSpTBXTabChangingEvent;
FOnActiveTabReorder: TSpTBXTabChangeEvent;
FOnActiveTabReordering: TSpTBXTabChangingEvent;
procedure ReadHiddenItems(Reader: TReader);
procedure WriteHiddenItems(Writer: TWriter);
function GetActiveTabIndex: Integer;
procedure SetActiveTabIndex(Value: Integer);
function GetSkinType: TSpTBXSkinType;
procedure SetSkinType(const Value: TSpTBXSkinType);
function GetTabAutofit: Boolean;
procedure SetTabAutofit(const Value: Boolean);
function GetTabAutofitMaxSize: Integer;
procedure SetTabAutofitMaxSize(const Value: Integer);
function GetTabBackgroundBorders: Boolean;
procedure SetTabBackgroundBorders(const Value: Boolean);
function GetTabBackgroundColor: TColor;
procedure SetTabBackgroundColor(const Value: TColor);
function GetTabCloseButton: TSpTBXTabCloseButton;
procedure SetTabCloseButton(const Value: TSpTBXTabCloseButton);
function GetTabCloseButtonImageIndex: Integer;
procedure SetTabCloseButtonImageIndex(const Value: Integer);
function GetTabDragReorder: Boolean;
procedure SetTabDragReorder(const Value: Boolean);
function GetTabMaxSize: Integer;
procedure SetTabMaxSize(const Value: Integer);
function GetTabPosition: TSpTBXTabPosition;
procedure SetTabPosition(const Value: TSpTBXTabPosition);
procedure SetTabVisible(const Value: Boolean);
function GetTabToolbar: TSpTBXTabToolbar;
procedure CMColorchanged(var Message: TMessage); message CM_COLORCHANGED;
procedure CMSpTBXControlsInvalidate(var Message: TMessage); message CM_SPTBXCONTROLSINVALIDATE;
procedure WMEraseBkgnd(var Message: TMessage); message WM_ERASEBKGND;
procedure WMInvalidateTabBackground(var Message: TMessage); message WM_INVALIDATETABBACKGROUND;
procedure WMWindowPosChanged(var Message: TWMWindowPosChanged); message WM_WINDOWPOSCHANGED;
function GetActiveTab: TSpTBXTabItem;
protected
FBackground: TBitmap;
// Painting
procedure DoDrawBackground(ACanvas: TCanvas; ARect: TRect;
const PaintStage: TSpTBXPaintStage; var PaintDefault: Boolean); virtual;
function GetFullRepaint: Boolean; virtual;
// Tabs
function CanActiveTabChange(const TabIndex, NewTabIndex: Integer): Boolean; virtual;
procedure DoActiveTabChange(const TabIndex: Integer); virtual;
function CanActiveTabReorder(const TabIndex, NewTabIndex: Integer): Boolean; virtual;
procedure DoActiveTabReorder(const TabIndex: Integer); virtual;
procedure ItemNotification(Ancestor: TTBCustomItem; Relayed: Boolean;
Action: TTBItemChangedAction; Index: Integer; Item: TTBCustomItem); virtual; // Items change notification
procedure TabInserted(Item: TSpTBXTabItem); virtual;
procedure TabDeleting(Item: TSpTBXTabItem; FreeTabSheet: Boolean = True); virtual;
// Component
procedure DefineProperties(Filer: TFiler); override;
function GetToolbarClass: TSpTBXToolbarClass; override;
procedure Loaded; override;
property Color default clBtnFace;
property ParentColor default False;
property ActiveTabIndex: Integer read GetActiveTabIndex write SetActiveTabIndex;
property TabAutofit: Boolean read GetTabAutofit write SetTabAutofit default False;
property TabAutofitMaxSize: Integer read GetTabAutofitMaxSize write SetTabAutofitMaxSize default 200;
property TabBackgroundColor: TColor read GetTabBackgroundColor write SetTabBackgroundColor default clNone;
property TabBackgroundBorders: Boolean read GetTabBackgroundBorders write SetTabBackgroundBorders default False;
property TabCloseButton: TSpTBXTabCloseButton read GetTabCloseButton write SetTabCloseButton default tcbNone;
property TabCloseButtonImageIndex: Integer read GetTabCloseButtonImageIndex write SetTabCloseButtonImageIndex default -1;
property TabDragReorder: Boolean read GetTabDragReorder write SetTabDragReorder default False;
property TabMaxSize: Integer read GetTabMaxSize write SetTabMaxSize default -1;
property TabPosition: TSpTBXTabPosition read GetTabPosition write SetTabPosition default ttpTop;
property TabVisible: Boolean read FTabVisible write SetTabVisible default True;
property SkinType: TSpTBXSkinType read GetSkinType write SetSkinType default sknSkin;
property OnActiveTabChange: TSpTBXTabChangeEvent read FOnActiveTabChange write FOnActiveTabChange;
property OnActiveTabChanging: TSpTBXTabChangingEvent read FOnActiveTabChanging write FOnActiveTabChanging;
property OnActiveTabReorder: TSpTBXTabChangeEvent read FOnActiveTabReorder write FOnActiveTabReorder;
property OnActiveTabReordering: TSpTBXTabChangingEvent read FOnActiveTabReordering write FOnActiveTabReordering;
property OnDrawBackground: TSpTBXDrawEvent read FOnDrawBackground write FOnDrawBackground;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
function Add(ACaption: WideString): TSpTBXTabItem;
function Insert(NewIndex: Integer; ACaption: WideString): TSpTBXTabItem;
function DrawBackground(DC: HDC; ARect: TRect): Boolean;
function GetTabSetHeight: Integer;
procedure InvalidateBackground(InvalidateChildren: Boolean = True); override;
procedure MakeVisible(ATab: TSpTBXTabItem);
procedure ScrollLeft;
procedure ScrollRight;
procedure ScrollState(out Left, Right: Boolean);
procedure TabClick(ATab: TSpTBXTabItem); virtual;
property ActiveTab: TSpTBXTabItem read GetActiveTab;
property Canvas;
property Toolbar: TSpTBXTabToolbar read GetTabToolbar;
end;
TSpTBXTabSet = class(TSpTBXCustomTabSet)
published
property Align;
property Anchors;
property BiDiMode;
property Color;
property Constraints;
property DockSite;
property DragCursor;
property DragKind;
property DragMode;
property Enabled;
property Font;
property ParentColor;
property ParentBiDiMode;
property ParentFont;
property ParentShowHint;
property PopupMenu;
property ShowHint;
property Visible;
{$IF CompilerVersion > 15} // For Delphi 2005 and up
property OnAlignInsertBefore;
property OnAlignPosition;
{$IFEND}
property OnCanResize;
property OnContextPopup;
property OnDockDrop;
property OnDockOver;
property OnDragDrop;
property OnDragOver;
property OnEndDock;
property OnEndDrag;
property OnEnter;
property OnExit;
property OnGetSiteInfo;
property OnMouseDown;
property OnMouseMove;
property OnMouseUp;
property OnResize;
property OnStartDock;
property OnStartDrag;
property OnUnDock;
// TSpTBXCustomTabSet properties
property ActiveTabIndex;
property Images;
property TabAutofit;
property TabAutofitMaxSize;
property TabBackgroundColor;
property TabBackgroundBorders;
property TabCloseButton;
property TabCloseButtonImageIndex;
property TabDragReorder;
property TabMaxSize;
property TabPosition;
property TabVisible;
property SkinType;
property OnActiveTabChange;
property OnActiveTabChanging;
property OnActiveTabReorder;
property OnActiveTabReordering;
property OnDrawBackground;
end;
{ TSpTBXTabControl }
TSpTBXCustomTabControl = class(TSpTBXCustomTabSet)
private
FEmptyTabSheet: TSpTBXTabSheet;
procedure RealignTabSheets;
function GetActivePage: TSpTBXTabSheet;
function GetPages(Index: Integer): TSpTBXTabSheet;
function GetPagesCount: Integer;
procedure SetActivePage(const Value: TSpTBXTabSheet);
procedure CMSpTBXControlsInvalidate(var Message: TMessage); message CM_SPTBXCONTROLSINVALIDATE;
procedure WMSpSkinChange(var Message: TMessage); message WM_SPSKINCHANGE;
protected
FPages: TList;
procedure DoActiveTabChange(const ItemIndex: Integer); override;
function GetFullRepaint: Boolean; override;
procedure TabInserted(Item: TSpTBXTabItem); override;
procedure TabDeleting(Item: TSpTBXTabItem; FreeTabSheet: Boolean = True); override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
function GetPage(Item: TSpTBXTabItem): TSpTBXTabSheet;
property ActivePage: TSpTBXTabSheet read GetActivePage write SetActivePage;
property Pages[Index: Integer]: TSpTBXTabSheet read GetPages;
property PagesCount: Integer read GetPagesCount;
end;
TSpTBXTabControl = class(TSpTBXCustomTabControl)
published
property Align;
property Anchors;
property BiDiMode;
property Color;
property Constraints;
property DockSite;
property DragCursor;
property DragKind;
property DragMode;
property Enabled;
property Font;
property ParentColor;
property ParentBiDiMode;
property ParentFont;
property ParentShowHint;
property PopupMenu;
property ShowHint;
property Visible;
property OnCanResize;
property OnContextPopup;
property OnDockDrop;
property OnDockOver;
property OnDragDrop;
property OnDragOver;
property OnEndDock;
property OnEndDrag;
property OnEnter;
property OnExit;
property OnGetSiteInfo;
property OnMouseDown;
property OnMouseMove;
property OnMouseUp;
property OnResize;
property OnStartDock;
property OnStartDrag;
property OnUnDock;
// TSpTBXCustomTabControl properties
property ActiveTabIndex;
property Images;
property TabAutofit;
property TabAutofitMaxSize;
property TabBackgroundColor;
property TabBackgroundBorders;
property TabCloseButton;
property TabCloseButtonImageIndex;
property TabDragReorder;
property TabMaxSize;
property TabPosition;
property TabVisible;
property SkinType;
property OnActiveTabChange;
property OnActiveTabChanging;
property OnActiveTabReorder;
property OnActiveTabReordering;
property OnDrawBackground;
end;
function SpGetNextTabItemViewer(View: TTBView; IV: TTBItemViewer; GoForward: Boolean; SearchType: TSpTBXSearchItemViewerType): TTBItemViewer;
procedure SpDrawXPTab(ACanvas: TCanvas; ARect: TRect; Enabled, Checked, HotTrack, Focused: Boolean; Position: TSpTBXTabPosition; SkinType: TSpTBXSkinType; Edge: TSpTBXTabEdge = tedNone);
procedure SpDrawXPTabControlBackground(ACanvas: TCanvas; ARect: TRect; AColor: TColor; BottomTabs: Boolean; SkinType: TSpTBXSkinType);
implementation
uses
Themes, UxTheme, Types;
type
TTBItemViewerAccess = class(TTBItemViewer);
TSpTBXCustomItemAccess = class(TSpTBXCustomItem);
TSpTBXDockAccess = class(TSpTBXDock);
TSpTBXToolbarAccess = class(TSpTBXToolbar);
//WMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWM
{ Helpers }
function SpGetNextTabItemViewer(View: TTBView; IV: TTBItemViewer; GoForward: Boolean;
SearchType: TSpTBXSearchItemViewerType): TTBItemViewer;
// Returns the left or right Tab item depending on GoForward
// SearchType can be:
// sivtNormal: Normal search
// sivtInmediate: Search for the inmediate ItemViewer
// sivtInmediateSkipNonVisible: Search for the next inmediate ItemViewer, skipping non visible ones
begin
Result := nil;
while Result = nil do begin
IV := SpGetNextItemSameEdge(View, IV, GoForward, SearchType);
if not Assigned(IV) then
Break // Not found, exit
else
if IV.Item is TSpTBXTabItem then begin
Result := IV; // Found Tab
Break;
end
else begin
case SearchType of
sivtInmediate:
Break; // Inmediate not found, exit
sivtInmediateSkipNonVisible:
if IV.Item.Visible then Break; // Inmediate not found and visible, exit
end;
end;
end;
end;
procedure SpDrawXPTab(ACanvas: TCanvas; ARect: TRect;
Enabled, Checked, HotTrack, Focused: Boolean; Position: TSpTBXTabPosition;
SkinType: TSpTBXSkinType; Edge: TSpTBXTabEdge = tedNone);
var
Part, Flags: Cardinal;
B: TBitmap;
R, FlippedR: TRect;
State: TSpTBXSkinStatesType;
begin
SkinType := SpTBXSkinType(SkinType);
if (SkinType = sknNone) and not Checked then
Exit;
B := TBitmap.Create;
try
B.Width := ARect.Right - ARect.Left;
B.Height := ARect.Bottom - ARect.Top;
R := Rect(0, 0, B.Width, B.Height);
B.Canvas.Brush.Color := clFuchsia;
B.Canvas.FillRect(R);
B.TransparentColor := clFuchsia;
B.Transparent := True;
case SkinType of
sknNone:
if Checked then begin
Position := ttpTop; // Don't need to flip
B.Canvas.Brush.Color := ACanvas.Brush.Color;
B.Canvas.FillRect(R);
ExtCtrls.Frame3D(B.Canvas, R, clWindow, clWindowFrame, 1);
ExtCtrls.Frame3D(B.Canvas, R, B.Canvas.Brush.Color, clBtnShadow, 1);
R := Rect(0, 0, B.Width, B.Height); // Frame3D changed R
end;
sknWindows:
begin
case Edge of
tedLeft: Part := TABP_TABITEMLEFTEDGE;
tedRight: Part := TABP_TABITEMRIGHTEDGE;
else
Part := TABP_TABITEM;
end;
Flags := TIS_NORMAL;
if not Enabled then Flags := TIS_DISABLED
else
if Checked then Flags := TIS_SELECTED
else
if HotTrack then Flags := TIS_HOT;
DrawThemeBackground(ThemeServices.Theme[teTab], B.Canvas.Handle, Part, Flags, R, nil);
end;
sknSkin:
begin
State := CurrentSkin.GetState(Enabled, False, HotTrack, Checked);
CurrentSkin.PaintBackground(B.Canvas, R, skncTab, State, True, True);
end;
end;
// Flip top to bottom
if Position = ttpBottom then begin
// Unclear why extra "-1" is needed here.
FlippedR := R;
FlippedR.Top := R.Bottom - 1;
FlippedR.Bottom := R.Top - 1;
B.Canvas.CopyRect(R, B.Canvas, FlippedR);
end;
// Draw focus
if Checked and Focused then begin
InflateRect(R, -3, -3);
SpDrawFocusRect(B.Canvas, R);
end;
ACanvas.Draw(ARect.Left, ARect.Top, B);
finally
B.Free;
end;
end;
procedure SpDrawXPTabControlBackground(ACanvas: TCanvas; ARect: TRect; AColor: TColor;
BottomTabs: Boolean; SkinType: TSpTBXSkinType);
var
B: TBitmap;
R: TRect;
begin
SkinType := SpTBXSkinType(SkinType);
B := TBitmap.Create;
try
B.Width := ARect.Right - ARect.Left;
B.Height := ARect.Bottom - ARect.Top;
R := Rect(0, 0, B.Width, B.Height);
// Draw the top/bottom border
case SkinType of
sknNone:
begin
BottomTabs := False; // Don't flip
B.Canvas.Brush.Color := AColor;
B.Canvas.FillRect(R);
ExtCtrls.Frame3D(B.Canvas, R, clWindow, clWindowFrame, 1);
ExtCtrls.Frame3D(B.Canvas, R, AColor, clBtnShadow, 1);
R := Rect(0, 0, B.Width, B.Height); // Frame3D changed R
end;
sknWindows:
DrawThemeBackground(ThemeServices.Theme[teTab], B.Canvas.Handle, TABP_PANE, 0, R, nil);
sknSkin:
begin
B.Canvas.Brush.Color := clWhite;
B.Canvas.FillRect(R);
CurrentSkin.PaintBackground(B.Canvas, R, skncTabBackground, sknsNormal, True, True);
end;
end;
// Flip top to bottom
if BottomTabs then begin
// Unclear why extra "-1" is needed here.
R.Top := B.Height - 1;
R.Bottom := -1
end;
ACanvas.CopyRect(ARect, B.Canvas, R);
finally
B.Free;
end;
end;
//WMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWM
{ TSpTBXTabItemDragObject }
constructor TSpTBXTabItemDragObject.Create(ASourceControl: TControl;
AItem: TTBCustomItem);
begin
inherited Create(ASourceControl, AItem);
DragCursorAccept := crDefault;
DragCursorCancel := crDefault;
end;
//WMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWM
{ TSpTBXTabItem }
constructor TSpTBXTabItem.Create(AOwner: TComponent);
begin
inherited;
DisplayMode := nbdmImageAndText;
GroupIndex := C_SpTBXTabGroupIndex;
Wrapping := twEndEllipsis;
Margins := 4;
FSkinType := sknSkin;
end;
procedure TSpTBXTabItem.Click;
var
T: TSpTBXTabToolbar;
I: Integer;
begin
// Set the Checked property to True, Autocheck is False
// Checked will call Item.Invalidate, the TabSet.ItemNotification will
// handle the invalidation and set the ActiveTabIndex.
if not Checked and Enabled and Visible then begin
if GetTabToolbar(T) then begin
I := T.Items.IndexOf(Self);
if T.FOwnerTabControl.CanActiveTabChange(T.ActiveTabIndex, I) then
Checked := True;
end;
inherited;
end;
end;
procedure TSpTBXTabItem.TabClose;
var
NextTab: TSpTBXTabItem;
T: TSpTBXTabToolbar;
CloseAndFree, CanTabClose: Boolean;
begin
if Visible then begin
GetTabToolbar(T);
if not Assigned(T) then Exit;
CanTabClose := True;
CloseAndFree := False;
DoTabClosing(CanTabClose, CloseAndFree);
if CanTabClose then begin
// Check the next visible tab
NextTab := nil;
if Checked then begin
NextTab := GetNextTab(True, sivtInmediateSkipNonVisible);
if not Assigned(NextTab) then
NextTab := GetNextTab(False, sivtInmediateSkipNonVisible);
end;
T.BeginUpdate;
try
Visible := False;
DoTabClose;
if CloseAndFree then
Free; // Removes the item from the parent, sends tbicDeleting notification and frees the item
if Assigned(NextTab) then
NextTab.Click; // Sends tbicInvalidate notification, which is handled by TSpTBXCustomTabSet.ItemNotification
finally
T.EndUpdate;
end;
end;
end;
end;
function TSpTBXTabItem.DialogChar(CharCode: Word): Boolean;
begin
Result := inherited DialogChar(CharCode);
if Enabled and Visible and IsAccel(CharCode, Caption) then begin
Click;
Result := True;
end;
end;
procedure TSpTBXTabItem.DoDrawTabCloseButton(ACanvas: TCanvas;
State: TSpTBXSkinStatesType; const PaintStage: TSpTBXPaintStage;
var AImageList: TCustomImageList; var AImageIndex: Integer; var ARect: TRect;
var PaintDefault: Boolean);
begin
if Assigned(FOnDrawTabCloseButton) then FOnDrawTabCloseButton(Self, ACanvas, State, PaintStage,
AImageList, AImageIndex, ARect, PaintDefault);
end;
procedure TSpTBXTabItem.DoTabClose;
begin
if Assigned(FOnTabClose) then FOnTabClose(Self);
end;
procedure TSpTBXTabItem.DoTabClosing(var Allow, CloseAndFree: Boolean);
begin
if Assigned(FOnTabClosing) then FOnTabClosing(Self, Allow, CloseAndFree);
end;
function TSpTBXTabItem.GetItemViewerClass(AView: TTBView): TTBItemViewerClass;
begin
Result := TSpTBXTabItemViewer;
end;
function TSpTBXTabItem.GetNextTab(GoForward: Boolean; SearchType: TSpTBXSearchItemViewerType): TSpTBXTabItem;
// Returns the left or right Tab item depending on GoForward, skipping all the non-visible ones.
// If Inmediate is true it will only search for the next inmediate tab
var
T: TSpTBXTabToolbar;
IV: TTBItemViewer;
begin
Result := nil;
if GetTabToolbar(T) then begin
IV := SpFindItemViewer(T.View, Self);
if Assigned(IV) then begin
IV := SpGetNextTabItemViewer(T.View, IV, GoForward, SearchType);
if Assigned(IV) then
Result := IV.Item as TSpTBXTabItem
end;
end;
end;
function TSpTBXTabItem.GetTabColor: TColor;
var
T: TSpTBXTabToolbar;
begin
Result := clBtnFace;
if GetTabToolbar(T) then
Result := T.TabColor;
end;
function TSpTBXTabItem.GetSkinType: TSpTBXSkinType;
var
T: TSpTBXTabToolbar;
begin
if GetTabToolbar(T) then
FSkinType := T.SkinType;
Result := FSkinType;
end;
procedure TSpTBXTabItem.SetSkinType(const Value: TSpTBXSkinType);
var
T: TSpTBXTabToolbar;
begin
// Don't change the SkinType if the item is inside a TabToolbar
if not GetTabToolbar(T) then begin
FSkinType := Value;
Change(False);
end;
end;
function TSpTBXTabItem.IsFirstVisible: Boolean;
var
T: TSpTBXTabToolbar;
IV: TTBItemViewer;
begin
Result := False;
if GetTabToolbar(T) then begin
IV := SpFindItemViewer(T.View, Self);
if Assigned(IV) then
Result := T.View.NextSelectable(nil, True) = IV;
end;
end;
function TSpTBXTabItem.IsFirstVisibleTab: Boolean;
var
T: TSpTBXTabToolbar;
begin
if GetTabToolbar(T) then
Result := not Assigned(GetNextTab(False, sivtNormal))
else
Result := False;
end;
function TSpTBXTabItem.IsLastVisibleTab: Boolean;
var
T: TSpTBXTabToolbar;
begin
if GetTabToolbar(T) then
Result := not Assigned(GetNextTab(True, sivtNormal))
else
Result := False;
end;
function TSpTBXTabItem.GetTabToolbar(out TabToolbar: TSpTBXTabToolbar): Boolean;
var
C: TComponent;
begin
C := GetParentComponent;
if Assigned(C) and (C is TSpTBXTabToolbar) then
TabToolbar := C as TSpTBXTabToolbar
else
TabToolbar := nil;
Result := Assigned(TabToolbar);
end;
procedure TSpTBXTabItem.ToggleControl;
begin
// Do nothing, the Control property is the Tabsheet, and its visibility
// is setted by TabSet.ActiveTabIndex
end;
//WMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWM
{ TSpTBXTabItemViewer }
procedure TSpTBXTabItemViewer.CalcSize(const Canvas: TCanvas; var AWidth,
AHeight: Integer);
var
TabMaxSize: Integer;
begin
inherited CalcSize(Canvas, AWidth, AHeight);
if IsOnTabToolbar and not Item.Anchored then begin
TabMaxSize := TSpTBXTabToolbar(View.Window).TabMaxSize;
if TabMaxSize > 0 then
if IsRotated then begin
if AHeight > TabMaxSize then
AHeight := TabMaxSize;
end
else begin
if AWidth > TabMaxSize then
AWidth := TabMaxSize;
end;
end;
end;
function TSpTBXTabItemViewer.CorrectTabRect(ARect: TRect): TRect;
// Offsets the rect to give a pushed effect on the tabs
begin
Result := ARect;
if not Item.Checked then
case TabPosition of
ttpTop: OffsetRect(Result, 0, 2);
ttpBottom: OffsetRect(Result, 0, -2);
end;
end;
procedure TSpTBXTabItemViewer.DoDrawButton(ACanvas: TCanvas; ARect: TRect;
ItemInfo: TSpTBXMenuItemInfo; const PaintStage: TSpTBXPaintStage;
var PaintDefault: Boolean);
var
LeftT, RightT: TTBItemViewer;
IsHoverItem: Boolean;
R: TRect;
Position: TSpTBXTabPosition;
TT: TSpTBXSkinType;
begin
inherited;
if (PaintStage = pstPrePaint) and PaintDefault then begin
PaintDefault := False;
IsHoverItem := (ItemInfo.State = sknsHotTrack) or (ItemInfo.State = sknsCheckedAndHotTrack);
Position := TabPosition;
// Match the bottom of the Tab with the bottom of the TabSet
case Position of
ttpTop: ARect.Bottom := ARect.Bottom + 1;
ttpBottom: ARect.Top := ARect.Top - 1;
end;
R := ARect;
TT := SpTBXSkinType(Item.SkinType);
case TT of
sknNone, sknSkin:
if Item.Checked or (IsHoverItem and (TT <> sknNone)) or
not CurrentSkin.Options(skncTab, sknsNormal).Body.IsEmpty or
not CurrentSkin.Options(skncTab, sknsNormal).Borders.IsEmpty then
begin
case Position of
ttpTop: Inc(R.Bottom, 5);
ttpBottom: Dec(R.Top, 5);
end;
DrawTab(ACanvas, R, Item.Enabled, Item.Checked, IsHoverItem, Position);
end
else begin
// Draw the separators
RightT := SpGetNextTabItemViewer(View, Self, True, sivtInmediateSkipNonVisible);
if Assigned(RightT) and not RightT.Item.Checked then
DrawTab(ACanvas, R, Item.Enabled, Item.Checked, IsHoverItem, Position, True);
end;
sknWindows:
begin
if IsOnTabToolbar then begin
// Find the inmediate left and right tabs
LeftT := SpGetNextTabItemViewer(View, Self, False, sivtInmediateSkipNonVisible);
RightT := SpGetNextTabItemViewer(View, Self, True, sivtInmediateSkipNonVisible);
end
else begin
LeftT := nil;
RightT := nil;
end;
if Item.Checked then begin
// The left border of the Tab will be painted by the Left tab if
// its the first tab
if Assigned(LeftT) or (Item.IsFirstVisible) then
R.Left := R.Left - 2;
// The right border of the Tab will be painted by the Right tab
if Assigned(RightT) then
R.Right := R.Right + 2;
end
else begin
// Non checked tabs should be smaller
case Position of
ttpTop: Inc(R.Top, 2);
ttpBottom: Dec(R.Bottom, 2);
end;
end;
// Draw the Tab
DrawTab(ACanvas, R, Item.Enabled, Item.Checked, IsHoverItem, Position);
// If the Tab is not checked then it should paint the active tab borders
if not Item.Checked then begin
R := ARect;
// Draw the left border
if Assigned(LeftT) and LeftT.Item.Checked then begin
R.Right := R.Left + 2;
R.Left := R.Right - 10;
DrawTab(ACanvas, R, LeftT.Item.Enabled, True, IsHoverItem, Position);
end
else
// Draw the right border
if Assigned(RightT) and RightT.Item.Checked then begin
R.Left := R.Right - 2;
R.Right := R.Left + 10;
DrawTab(ACanvas, R, RightT.Item.Enabled, True, IsHoverItem, Position);
end;
end;
end;
end;
end;
end;
procedure TSpTBXTabItemViewer.DoDrawCaption(ACanvas: TCanvas; ClientAreaRect: TRect;
State: TSpTBXSkinStatesType; var ACaption: WideString; var CaptionRect: TRect;
var CaptionFormat: Cardinal; IsTextRotated: Boolean;
const PaintStage: TSpTBXPaintStage; var PaintDefault: Boolean);
begin
CaptionRect := CorrectTabRect(CaptionRect); // Offset the rect to give a pushed effect on the tabs
// [Theme-Change]
// The Default theme paints the caption of the pushed button in a down
// state, this only happens when the item is in a toolbarstyle
if (State = sknsPushed) and (SkinManager.CurrentSkinName = 'Default') then
OffsetRect(CaptionRect, -1, -1);
inherited DoDrawCaption(ACanvas, ClientAreaRect, State, ACaption, CaptionRect,
CaptionFormat, IsTextRotated, PaintStage, PaintDefault);
end;
procedure TSpTBXTabItemViewer.DoDrawImage(ACanvas: TCanvas;
State: TSpTBXSkinStatesType; const PaintStage: TSpTBXPaintStage;
var AImageList: TCustomImageList; var AImageIndex: Integer; var ARect: TRect;
var PaintDefault: Boolean);
begin
ARect := CorrectTabRect(ARect); // Offset the rect to give a pushed effect on the tabs
inherited DoDrawImage(ACanvas, State, PaintStage, AImageList, AImageIndex, ARect, PaintDefault);
end;
procedure TSpTBXTabItemViewer.DoDrawTabCloseButton(ACanvas: TCanvas;
State: TSpTBXSkinStatesType; const PaintStage: TSpTBXPaintStage;
var AImageList: TCustomImageList; var AImageIndex: Integer; var ARect: TRect;
var PaintDefault: Boolean);
begin
TSpTBXTabItem(Item).DoDrawTabCloseButton(ACanvas, State, PaintStage, AImageList, AImageIndex, ARect, PaintDefault);
end;
procedure TSpTBXTabItemViewer.DrawBottomBorder(ACanvas: TCanvas; ARect: TRect);
var
CR, R: TRect;
Edge: TSpTBXTabEdge;
LeftT, RightT: Boolean;
Position: TSpTBXTabPosition;
B: TBitmap;
TT: TSpTBXSkinType;
begin
if not IsOnTabToolbar then Exit;
TT := SpTBXSkinType(Item.SkinType);
Position := TabPosition;
Edge := tedNone;
CR := ARect;
case Position of
ttpTop:
Inc(CR.Bottom, 2);
ttpBottom:
begin
// When sknNone the bottom border size is 2
if TT = sknNone then
Dec(CR.Top, 2)
else
Dec(CR.Top, 2);
end;
end;
if TT = sknWindows then begin
LeftT := Assigned(SpGetNextTabItemViewer(View, Self, False, sivtInmediateSkipNonVisible));
RightT := Assigned(SpGetNextTabItemViewer(View, Self, True, sivtInmediateSkipNonVisible));
if Item.IsFirstVisible then // Is first IV?
Edge := tedLeft;
if Edge = tedLeft then begin
CR.Left := CR.Left - 2;
if RightT then
CR.Right := CR.Right + 2;
end
else begin
if LeftT then
CR.Left := CR.Left - 2;
if RightT then
CR.Right := CR.Right + 2;
end;
end;
B := TBitmap.Create;
try
B.Width := CR.Right - CR.Left;
B.Height := CR.Bottom - CR.Top + 4; // Larger than CR
R := Rect(0, 0, B.Width, B.Height);
DrawTab(B.Canvas, R, True, True, False, Position, False, Edge);
case Position of
ttpTop:
R := Bounds(0, 0, CR.Right - CR.Left, CR.Bottom - CR.Top); // Copy from Y = 0
ttpBottom:
R := Bounds(0, 2, CR.Right - CR.Left, CR.Bottom - CR.Top + 2); // Copy from Y = 2
end;
ACanvas.CopyRect(CR, B.Canvas, R);
finally
B.Free;
end;
end;
procedure TSpTBXTabItemViewer.DrawItemRightImage(ACanvas: TCanvas; ARect: TRect;
ItemInfo: TSpTBXMenuItemInfo);
var
PaintDefault: Boolean;
ImgList: TCustomImageList;
ImgIndex: Integer;
PatternColor: TColor;
begin
if IsOnTabToolbar then begin
if not IsTabCloseButtonVisible then
Exit;
GetTabCloseButtonImgList(ImgList, ImgIndex);
ARect := CorrectTabRect(ARect); // Offset the rect to give a pushed effect on the tabs
ItemInfo.Pushed := False;
ItemInfo.Checked := False;
if ItemInfo.Enabled then
if FTabCloseButtonState = sknsHotTrack then
ItemInfo.State := sknsHotTrack
else begin
ItemInfo.Enabled := False;
ItemInfo.State := sknsDisabled;
end;
PaintDefault := True;
DoDrawTabCloseButton(ACanvas, ItemInfo.State, pstPrePaint, ImgList, ImgIndex, ARect, PaintDefault);
if PaintDefault and Assigned(ImgList) and (FTabCloseButtonState = sknsHotTrack) then
if (ImgList = MDIButtonsImgList) or ((ImgIndex >= 0) and (ImgIndex < ImgList.Count)) then
SpDrawXPMenuItem(ACanvas, ARect, ItemInfo);
PaintDefault := True;
if ImgList = MDIButtonsImgList then begin
PatternColor := GetTextColor(ItemInfo.State);
SpDrawGlyphPattern(ACanvas, ARect, ImgIndex, PatternColor);
end
else
DoDrawTabCloseButton(ACanvas, ItemInfo.State, pstPostPaint, ImgList, ImgIndex, ARect, PaintDefault);
if PaintDefault and Assigned(ImgList) and (ImgIndex >= 0) and (ImgIndex < ImgList.Count) then begin
SpDrawXPMenuItemImage(ACanvas, ARect, ItemInfo, ImgList, ImgIndex);
end;
end;
end;
procedure TSpTBXTabItemViewer.DrawTab(ACanvas: TCanvas; ARect: TRect; AEnabled,
AChecked, AHoverItem: Boolean; Position: TSpTBXTabPosition;
ASeparator: Boolean; AEdge: TSpTBXTabEdge);
begin
if ASeparator then begin
ARect.Left := ARect.Right - 2;
SpDrawXPMenuSeparator(ACanvas, ARect, False, True)
end
else begin
ACanvas.Brush.Color := Item.TabColor;
SpDrawXPTab(ACanvas, ARect, AEnabled, AChecked, AHoverItem, False, Position, Item.SkinType, AEdge);
end;
end;
function TSpTBXTabItemViewer.GetItem: TSpTBXTabItem;
begin
Result := TSpTBXTabItem(inherited Item);
end;
function TSpTBXTabItemViewer.GetRightImageRect: TRect;
var
RightGlyphSize: TSize;
R: TRect;
begin
RightGlyphSize := GetRightImageSize;
R := BoundsRect;
Result.Left := R.Right - 4 - RightGlyphSize.cx;
Result.Right := Result.Left + RightGlyphSize.cx;
Result.Top := (R.Top + R.Bottom - RightGlyphSize.cy) div 2;
Result.Bottom := Result.Top + RightGlyphSize.cy;
Result := CorrectTabRect(Result); // Offset the rect to give a pushed effect on the tabs
end;
function TSpTBXTabItemViewer.GetRightImageSize: TSize;
var
ImgList: TCustomImageList;
ImgIndex: Integer;
begin
Result.cx := 0;
Result.cy := 0;
GetTabCloseButtonImgList(ImgList, ImgIndex);
if Assigned(ImgList) then
if ImgList = MDIButtonsImgList then begin
Result.cx := 15;
Result.cy := 15;
end
else if (ImgIndex >= 0) and (ImgIndex < ImgList.Count) then begin
Result.cx := ImgList.Width;
Result.cy := ImgList.Height;
end;
end;
procedure TSpTBXTabItemViewer.GetTabCloseButtonImgList(var AImageList: TCustomImageList;
var AImageIndex: Integer);
var
T: TSpTBXTabToolbar;
begin
AImageList := nil;
AimageIndex := -1;
if IsOnTabToolbar then begin
T := TSpTBXTabToolbar(View.Window);
if T.TabCloseButton <> tcbNone then begin
AImageList := GetImageList;
AImageIndex := T.TabCloseButtonImageIndex;
if not Assigned(AImageList) or (AImageIndex < 0) or (AImageIndex >= AImageList.Count) then begin
AImageList := MDIButtonsImgList;
AImageIndex := 0;
end;
end;
end;
end;
function TSpTBXTabItemViewer.GetTabPosition: TSpTBXTabPosition;
begin
if IsOnTabToolbar then
Result := TSpTBXTabToolbar(View.Window).TabPosition
else
Result := ttpTop;
end;
function TSpTBXTabItemViewer.GetTextColor(State: TSpTBXSkinStatesType): TColor;
begin
Result := Item.FontSettings.Color;
if Result = clNone then begin
if View.Window is TSpTBXTabToolbar then
Result := TSpTBXToolbarAccess(View.Window).GetItemsTextColor(State);
if Result = clNone then
Result := CurrentSkin.GetTextColor(skncTab, State)
end;
end;
procedure TSpTBXTabItemViewer.InternalMouseMove(Shift: TShiftState; X,
Y: Integer);
var
P: TPoint;
R: TRect;
NewState: TSpTBXSkinStatesType;
begin
inherited;
if FTabCloseButtonState in [sknsNormal, sknsHotTrack] then begin
if not Item.Enabled then begin
FTabCloseButtonState := sknsDisabled;
Exit;
end;
P := Point(X, Y);
R := GetRightImageRect;
if PtInRect(R, P) then
NewState := sknsHotTrack
else
NewState := sknsNormal;
if NewState <> FTabCloseButtonState then begin
FTabCloseButtonState := NewState;
InvalidateRect(View.Window.Handle, @R, True)
end;
end;
end;
function TSpTBXTabItemViewer.IsOnTabToolbar: Boolean;
begin
Result := Assigned(View.Window) and (View.Window is TSpTBXTabToolbar);
end;
function TSpTBXTabItemViewer.IsTabCloseButtonVisible: Boolean;
var
T: TSpTBXTabToolbar;
begin
Result := False;
if IsOnTabToolbar then begin
T := TSpTBXTabToolbar(View.Window);
case T.TabCloseButton of
tcbNone:
Exit;
tcbActive:
if not Item.Checked then Exit;
end;
Result := True;
end;
end;
procedure TSpTBXTabItemViewer.Leaving;
var
R: TRect;
begin
inherited;
if FTabCloseButtonState = sknsHotTrack then begin
FTabCloseButtonState := sknsNormal;
R := GetRightImageRect;
InvalidateRect(View.Window.Handle, @R, True)
end;
end;
//WMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWM
{ TSpTBXTabToolbarView }
procedure TSpTBXTabToolbarView.BeginUpdate;
var
T: TSpTBXTabToolbar;
begin
if (FUpdating = 0) and (Owner is TSpTBXTabToolbar) then begin
T := TSpTBXTabToolbar(Owner);
if Assigned(T.FOwnerTabControl) then
SendMessage(T.FOwnerTabControl.Handle, WM_SETREDRAW, 0, 0);
end;
inherited;
end;
procedure TSpTBXTabToolbarView.EndUpdate;
var
T: TSpTBXTabToolbar;
begin
inherited;
if (FUpdating = 0) and (Owner is TSpTBXTabToolbar) then begin
T := TSpTBXTabToolbar(Owner);
if Assigned(T.FOwnerTabControl) then begin
SendMessage(T.FOwnerTabControl.Handle, WM_SETREDRAW, 1, 0);
SpInvalidateSpTBXControl(T.FOwnerTabControl, True, False);
end;
end;
end;
//WMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWM
{ TSpTBXTabToolbar }
constructor TSpTBXTabToolbar.Create(AOwner: TComponent);
begin
inherited;
FHiddenTabs := TSpTBXItemCacheCollection.Create(TSpTBXItemCache);
if Owner is TSpTBXCustomTabSet then
FOwnerTabControl := Owner as TSpTBXCustomTabSet
else
FOwnerTabControl := nil;
FActiveTabIndex := -1;
FTabBackgroundBorders := False;
FSkinType := sknSkin;
FTabAutofitMaxSize := 200;
FTabCloseButtonImageIndex := -1;
FTabColor := clBtnFace;
FTabMaxSize := -1;
FTabPosition := ttpTop;
end;
destructor TSpTBXTabToolbar.Destroy;
begin
FHiddenTabs.Free;
inherited;
end;
procedure TSpTBXTabToolbar.DoItemNotification(Ancestor: TTBCustomItem;
Relayed: Boolean; Action: TTBItemChangedAction; Index: Integer;
Item: TTBCustomItem);
var
Tab: TSpTBXTabItem;
IV: TTBItemViewer;
begin
inherited;
if Action = tbicInvalidateAndResize then begin
// Invalidate the NC area, draw the bottom border of the active tab
// Instead of invalidating every time, save the Rect that needs to be drawn
// (bottom border of the active tab) and see if it needs to be repainted
if not IsItemMoving then begin
Tab := ActiveTab;
if Assigned(Tab) and Tab.Visible then begin
IV := SpFindItemViewer(View, Tab);
if Assigned(IV) then
if not EqualRect(FActiveTabRect, IV.BoundsRect) then
InvalidateNC;
end;
end;
end;
end;
procedure TSpTBXTabToolbar.InternalDrawBackground(ACanvas: TCanvas; ARect: TRect;
PaintOnNCArea: Boolean; PaintBorders: Boolean = True);
var
B: TBitmap;
R, BitmapR, DestR: TRect;
Tab: TSpTBXTabItem;
IV: TTBItemViewer;
T: TSpTBXSkinType;
PrevDelta, NextDelta: Integer;
begin
T := SpTBXSkinType(SkinType);
if PaintOnNCArea and Assigned(FOwnerTabControl) then begin
B := TBitmap.Create;
B.Canvas.Lock;
try
R := ARect;
B.Width := R.Right - R.Left;
B.Height := R.Bottom - R.Top;
SpDrawXPToolbar(Self, B.Canvas, R, PaintOnNCArea, FTabBackgroundBorders and (T <> sknNone), skncTabToolbar);
// Draw the bottom border of the active tab
Tab := ActiveTab;
if Assigned(Tab) and Tab.Visible then begin
IV := SpFindItemViewer(View, Tab);
if Assigned(IV) then begin
FActiveTabRect := IV.BoundsRect;
DestR := IV.BoundsRect;
OffsetRect(DestR, 2, 2); // Add the toolbar margins
TSpTBXTabItemViewer(IV).DrawBottomBorder(B.Canvas, DestR);
end;
if T = sknWindows then begin
if Tab.IsFirstVisible or Assigned(Tab.GetNextTab(False, sivtInmediateSkipNonVisible)) then
PrevDelta := 1
else
PrevDelta := -1;
if Assigned(Tab.GetNextTab(True, sivtInmediateSkipNonVisible)) then
NextDelta := 1
else
NextDelta := -1;
if FTabPosition = ttpTop then
ExcludeClipRect(B.Canvas.Handle, DestR.Left - PrevDelta, R.Bottom - 2, DestR.Right + NextDelta, R.Bottom + 4)
else
ExcludeClipRect(B.Canvas.Handle, DestR.Left - PrevDelta, R.Top + 2, DestR.Right + NextDelta, R.Top - 4);
end
else
if FTabPosition = ttpTop then
ExcludeClipRect(B.Canvas.Handle, DestR.Left + 1, R.Bottom - 2, DestR.Right - 1, R.Bottom + 4)
else
ExcludeClipRect(B.Canvas.Handle, DestR.Left + 1, R.Top + 2, DestR.Right -1 , R.Top - 4);
end;
// Draw the bottom border of the tabs pane
BitmapR := Rect(0, 0, FOwnerTabControl.FBackground.Width, FOwnerTabControl.FBackground.Height);
case FTabPosition of
ttpTop:
begin
DestR := Rect(R.Left, R.Bottom - 2, R.Right, R.Bottom);
BitmapR.Bottom := BitmapR.Top + 2;
end;
ttpBottom:
begin
DestR := Rect(R.Left, R.Top, R.Right, R.Top + 2);
BitmapR.Top := BitmapR.Bottom - 2;
end;
end;
B.Canvas.CopyRect(DestR, FOwnerTabControl.FBackground.Canvas, BitmapR);
ACanvas.Draw(0, 0, B);
finally
B.Canvas.UnLock;
B.Free;
end;
end
else
SpDrawXPToolbar(Self, ACanvas, ARect, PaintOnNCArea, FTabBackgroundBorders and (T <> sknNone), skncTabToolbar);
end;
procedure TSpTBXTabToolbar.InvalidateActiveTab;
var
Tab: TSpTBXTabItem;
IV: TTBItemViewer;
begin
Tab := ActiveTab;
if Assigned(Tab) then begin
IV := SpFindItemViewer(View, Tab);
if Assigned(IV) then
View.Invalidate(IV);
end;
end;
procedure TSpTBXTabToolbar.InvalidateNC;
begin
if not IsUpdating and not (tstResizing in FState) and HandleAllocated then
RedrawWindow(Handle, nil, 0, RDW_ERASE or RDW_INVALIDATE or RDW_FRAME);
end;
function TSpTBXTabToolbar.GetActiveTab: TSpTBXTabItem;
var
Item: TTBCustomItem;
begin
Result := nil;
if Assigned(Items) and (FActiveTabIndex > -1) and (Items.Count > 0) and
(FActiveTabIndex < Items.Count) then
begin
Item := Items[FActiveTabIndex];
if not (csDestroying in Item.ComponentState) and (Item is TSpTBXTabItem) and Assigned(Item.Parent) then
Result := Items[FActiveTabIndex] as TSpTBXTabItem;
end;
end;
function TSpTBXTabToolbar.GetItemsTextColor(State: TSpTBXSkinStatesType): TColor;
begin
Result := CurrentSkin.GetTextColor(skncTabToolbar, State, SkinType);
// Don't call inherited GetItemsTextColor, let the TabItem decide the color.
end;
function TSpTBXTabToolbar.GetTabsCount(VisibleOnly: Boolean): Integer;
var
I: Integer;
IV: TTBItemViewer;
begin
Result := 0;
for I := 0 to View.ViewerCount - 1 do begin
IV := View.Viewers[I];
if IV.Item is TSpTBXTabItem then
if VisibleOnly then begin
if IV.Item.Visible then Inc(Result);
end
else
Inc(Result);
end;
end;
function TSpTBXTabToolbar.GetViewClass: TTBToolbarViewClass;
begin
Result := TSpTBXTabToolbarView;
end;
procedure TSpTBXTabToolbar.Autofit;
var
I, TabsCount, TabsWidth, TabsArea, NonTabsArea, RightAlignWidth: Integer;
IV: TTBItemViewer;
R: TRect;
begin
if not FTabAutofit or IsUpdating or (Items.Count = 0) then Exit;
View.ValidatePositions;
View.BeginUpdate;
try
// Make all the clipped items visible
for I := 0 to FHiddenTabs.Count - 1 do
FHiddenTabs.Items[I].Item.Visible := True;
FHiddenTabs.Clear;
TabsCount := 0;
TabsWidth := 0;
NonTabsArea := 0;
RightAlignWidth := 0;
// Get TabsCount and NonTabsArea
for I := 0 to View.ViewerCount - 1 do begin
IV := View.Viewers[I];
if IV.Item.Visible then begin
if IV.Item is TSpTBXTabItem then
Inc(TabsCount)
else
if IV.Item is TSpTBXRightAlignSpacerItem then
Inc(RightAlignWidth, 20)
else begin
R := SpGetBoundsRect(IV, Items);
Inc(NonTabsArea, R.Right - R.Left);
end;
end;
end;
// Get TabsArea
if TabsCount > 0 then begin
TabsArea := CurrentDock.ClientWidth - 4 - NonTabsArea - RightAlignWidth;
TabsWidth := TabsArea div TabsCount;
if TabsWidth > FTabAutofitMaxSize then
TabsWidth := FTabAutofitMaxSize;
end;
// Get RightAlignWidth
Inc(RightAlignWidth, CurrentDock.Width - ((TabsWidth * TabsCount) + NonTabsArea + RightAlignWidth));
// Set TabsWidth and RightAlignWidth to the Items
for I := 0 to View.ViewerCount - 1 do begin
IV := View.Viewers[I];
if IV.Item.Visible then begin
if IV.Item is TSpTBXTabItem then
TSpTBXTabItem(IV.Item).CustomWidth := TabsWidth
else
if IV.Item is TSpTBXRightAlignSpacerItem then
TSpTBXRightAlignSpacerItem(IV.Item).CustomWidth := RightAlignWidth - GetRightAlignMargin;
end;
end;
finally
View.EndUpdate;
end;
end;
procedure TSpTBXTabToolbar.RightAlignItems;
// Hide the items on resizing
var
I, J, W, H, VisibleTabsCount, iStart, iEnd: Integer;
VisibleWidth, RightAlignedWidth, SpacerW, RightAlignedBorder: Integer;
IV: TTBItemViewer;
Spacer: TSpTBXItemViewer;
RightAlignedList: TList;
IsRotated, IsFirstPartiallyVisible: Boolean;
begin
if (csDestroying in ComponentState) or (tstRightAligning in FState) or
not Assigned(CurrentDock) or (Items.Count <= 0) or
not Stretch or (ShrinkMode <> tbsmNone) or
(CurrentDock.Width <= 0) or (CurrentDock.Height <= 0) or IsUpdating then
Exit;
if FTabAutofit then begin
Autofit;
Exit;
end;
FState := FState + [tstRightAligning];
View.ValidatePositions;
View.BeginUpdate;
RightAlignedList := TList.Create;
try
IsRotated := CurrentDock.Position in [dpLeft, dpRight];
// Find the spacer and the right aligned items
Spacer := SpGetRightAlignedItems(View, RightAlignedList, IsRotated, VisibleWidth, RightAlignedWidth);
if Assigned(Spacer) then begin
SpacerW := Spacer.BoundsRect.Right - Spacer.BoundsRect.Left;
RightAlignedBorder := CurrentDock.Width - 2 - RightAlignedWidth + SpacerW;
VisibleWidth := VisibleWidth - SpacerW;
SpacerW := CurrentDock.Width - VisibleWidth - 4;
end
else begin
SpacerW := 0;
RightAlignedBorder := CurrentDock.Width - 2;
end;
// Show items
VisibleTabsCount := GetTabsCount(True);
IsFirstPartiallyVisible := False;
if VisibleTabsCount = 1 then begin
if VisibleWidth > CurrentDock.Width - 2 then
IsFirstPartiallyVisible := True;
end;
if not IsFirstPartiallyVisible then begin
IV := View.NextSelectable(nil, True);
if Assigned(IV) then begin
iStart := IV.Index;
iEnd := IV.Index;
end
else begin
iStart := 0;
iEnd := 0;
end;
// Show items from left side of the first visible tab
for I := iStart downto 0 do begin
IV := View.Viewers[I];
if not IV.Item.Visible and (RightAlignedList.IndexOf(IV) = -1) then begin
// If the item was hidden and can be showed remove it from the HiddenList
J := FHiddenTabs.IndexOf(IV.Item);
if J > -1 then begin
W := 0;
H := 0;
TTBItemViewerAccess(IV).CalcSize(Canvas, W, H);
VisibleWidth := VisibleWidth + W;
if (VisibleTabsCount = 0) or (VisibleWidth < CurrentDock.Width - 2) then begin
SpacerW := SpacerW - W;
FHiddenTabs.Delete(J);
IV.Item.Visible := True;
Inc(VisibleTabsCount);
end
else
Break;
end;
end;
end;
// Show items from right side of the first visible tab
for I := iEnd to View.ViewerCount - 1 do begin
IV := View.Viewers[I];
if not IV.Item.Visible and (RightAlignedList.IndexOf(IV) = -1) then begin
// If the item was hidden and can be showed remove it from the HiddenList
J := FHiddenTabs.IndexOf(IV.Item);
if J > -1 then begin
W := 0;
H := 0;
TTBItemViewerAccess(IV).CalcSize(Canvas, W, H);
VisibleWidth := VisibleWidth + W;
if (VisibleTabsCount = 0) or (VisibleWidth < CurrentDock.Width - 2) then begin
SpacerW := SpacerW - W;
FHiddenTabs.Delete(J);
IV.Item.Visible := True;
Inc(VisibleTabsCount);
end
else
Break;
end;
end;
end;
// Hide items
if VisibleTabsCount > 1 then
for I := View.ViewerCount - 1 downto 0 do begin
IV := View.Viewers[I];
if (VisibleTabsCount > 1) and (IV is TSpTBXTabItemViewer) and IV.Item.Visible and (IV.BoundsRect.Right > RightAlignedBorder) then
if RightAlignedList.IndexOf(IV) = -1 then begin
// If the item can't be showed add it to the HiddenList
SpacerW := SpacerW + (IV.BoundsRect.Right - IV.BoundsRect.Left);
FHiddenTabs.Add(IV.Item);
IV.Item.Visible := False;
Dec(VisibleTabsCount);
end;
end;
end;
// Resize the spacer
if Assigned(Spacer) then
TSpTBXCustomItemAccess(Spacer.Item).CustomWidth := SpacerW;
View.UpdatePositions;
finally
RightAlignedList.Free;
View.EndUpdate;
FState := FState - [tstRightAligning];
end;
end;
procedure TSpTBXTabToolbar.TabClose(ATab: TSpTBXTabItem);
begin
ATab.TabClose;
end;
procedure TSpTBXTabToolbar.MakeVisible(ATab: TSpTBXTabItem);
var
TabIV, FirstIV, LastIV: TTBItemViewer;
I: Integer;
Spacer: TSpTBXItemViewer;
begin
if (Items.Count > 1) and Assigned(ATab) and (ATab.Visible = False) then begin
TabIV := View.Find(ATab);
FirstIV := View.NextSelectable(nil, True);
// LastIV minus the right aligned items
Spacer := SpGetFirstRightAlignSpacer(View);
if Assigned(Spacer) then
LastIV := View.NextSelectable(Spacer, False)
else
LastIV := View.NextSelectable(nil, False);
if Assigned(FirstIV) and Assigned(LastIV) then begin
for I := 0 to View.ViewerCount - 1 do begin
if TabIV.Index >= FirstIV.Index then
ScrollRight
else
ScrollLeft;
if TabIV.Item.Visible then
Break;
end;
end;
end;
end;
procedure TSpTBXTabToolbar.Scroll(ToRight: Boolean);
var
FirstIV, LastIV: TTBItemViewer;
I, VisibleWidth: Integer;
Spacer: TSpTBXItemViewer;
function ProcessScroll(IV: TTBItemViewer): Boolean;
var
IVIndex, ClippedIndex, VisibleTabsCount, W, H: Integer;
begin
Result := False;
ClippedIndex := FHiddenTabs.IndexOf(IV.Item);
if Assigned(IV) and (ClippedIndex > -1) then begin
Result := True; // a clipped tab was found
BeginUpdate;
try
VisibleTabsCount := GetTabsCount(True);
// Try to hide all the necessary tabs
W := 0;
H := 0;
TTBItemViewerAccess(IV).CalcSize(Canvas, W, H);
if ToRight then begin
while Assigned(FirstIV) and (VisibleWidth + W >= CurrentDock.ClientWidth - 2) do begin
VisibleWidth := VisibleWidth - (FirstIV.BoundsRect.Right - FirstIV.BoundsRect.Left);
FHiddenTabs.Add(FirstIV.Item);
FirstIV.Item.Visible := False;
FirstIV := SpGetNextTabItemViewer(View, FirstIV, True, sivtNormal);
Dec(VisibleTabsCount);
end;
end
else begin
while Assigned(LastIV) and (VisibleWidth + W >= CurrentDock.ClientWidth - 2) do begin
VisibleWidth := VisibleWidth - (LastIV.BoundsRect.Right - LastIV.BoundsRect.Left);
FHiddenTabs.Add(LastIV.Item);
LastIV.Item.Visible := False;
LastIV := SpGetNextTabItemViewer(View, LastIV, False, sivtNormal);
Dec(VisibleTabsCount);
end;
end;
// Try to show all the necessary clipped tabs
IVIndex := IV.Index;
while Assigned(IV) and (ClippedIndex > -1) and ((VisibleTabsCount = 0) or (VisibleWidth + W <= CurrentDock.ClientWidth - 2)) do begin
VisibleWidth := VisibleWidth + W;
IV.Item.Visible := True;
FHiddenTabs.Delete(ClippedIndex);
Inc(VisibleTabsCount);
if ToRight then
Inc(IVIndex)
else
Dec(IVIndex);
if (IVIndex > -1) and (IVIndex < View.ViewerCount) then begin
IV := View.Viewers[IVIndex];
ClippedIndex := FHiddenTabs.IndexOf(IV.Item);
W := 0;
H := 0;
TTBItemViewerAccess(IV).CalcSize(Canvas, W, H);
end
else
Break;
end;
finally
EndUpdate;
end;
end;
end;
begin
if (Items.Count > 1) and not FTabAutofit then begin
// Find the first Tab
FirstIV := SpGetNextTabItemViewer(View, nil, True, sivtNormal);
// Get the VisibleWidth
LastIV := View.NextSelectable(nil, False);
if not Assigned(LastIV) then Exit;
VisibleWidth := LastIV.BoundsRect.Right;
// LastIV minus the right aligned items
Spacer := SpGetFirstRightAlignSpacer(View);
if Assigned(Spacer) then begin
VisibleWidth := VisibleWidth - (Spacer.BoundsRect.Right - Spacer.BoundsRect.Left);
LastIV := View.NextSelectable(Spacer, False);
end;
if Assigned(LastIV) and not (LastIV.Item is TSpTBXTabItem) then
LastIV := SpGetNextTabItemViewer(View, LastIV, False, sivtNormal);
if Assigned(FirstIV) and Assigned(LastIV) then begin
if ToRight then begin
// Find the first clipped tab from the right side of the tabset
for I := LastIV.Index + 1 to View.ViewerCount - 1 do
if ProcessScroll(View.Viewers[I]) then
Break;
end
else begin
// Find the first clipped tab from the left side of the tabset
for I := FirstIV.Index - 1 downto 0 do
if ProcessScroll(View.Viewers[I]) then
Break;
end;
end;
end;
end;
procedure TSpTBXTabToolbar.ScrollLeft;
begin
Scroll(False);
end;
procedure TSpTBXTabToolbar.ScrollRight;
begin
Scroll(True);
end;
procedure TSpTBXTabToolbar.ScrollState(out CanScrollToLeft, CanScrollToRight: Boolean);
var
FirstIV, LastIV: TTBItemViewer;
I, ClippedIndex: Integer;
Spacer: TSpTBXItemViewer;
begin
CanScrollToLeft := False;
CanScrollToRight := False;
if (FHiddenTabs.Count > 0) and not FTabAutofit then begin
// Find the first Tab
FirstIV := SpGetNextTabItemViewer(View, nil, True, sivtNormal);
if Assigned(FirstIV) then begin
// Find the first clipped tab from the left side of the tabset
for I := FirstIV.Index - 1 downto 0 do begin
ClippedIndex := FHiddenTabs.IndexOf(View.Viewers[I].Item);
if ClippedIndex > -1 then begin
CanScrollToLeft := True;
Break;
end;
end;
end;
// LastIV minus the right aligned items
Spacer := SpGetFirstRightAlignSpacer(View);
if Assigned(Spacer) then
LastIV := View.NextSelectable(Spacer, False)
else
LastIV := View.NextSelectable(nil, False);
// Find the first clipped tab from the right side of the tabset
if Assigned(LastIV) then begin
for I := LastIV.Index + 1 to View.ViewerCount - 1 do begin
ClippedIndex := FHiddenTabs.IndexOf(View.Viewers[I].Item);
if ClippedIndex > -1 then begin
CanScrollToRight := True;
Break;
end;
end;
end;
end;
end;
procedure TSpTBXTabToolbar.SetActiveTabIndex(Value: Integer);
var
ATab, APrevTab: TSpTBXTabItem;
I: Integer;
begin
if not Assigned(FOwnerTabControl) then Exit;
if (Value > -1) and (Value < Items.Count) and not (csDestroying in Items[Value].ComponentState) then begin
if not (Items[Value] is TSpTBXTabItem) then
Value := FActiveTabIndex;
end
else
Value := -1;
if (Value <> FActiveTabIndex) and FOwnerTabControl.CanActiveTabChange(FActiveTabIndex, Value) then
begin
I := FActiveTabIndex;
FActiveTabIndex := Value;
// Hide the previous TabSheet
if (I > -1) and (I < Items.Count) and not (csDestroying in Items[I].ComponentState) and
(Items[I] is TSpTBXTabItem) then
begin
APrevTab := Items[I] as TSpTBXTabItem;
APrevTab.Checked := False;
if Assigned(APrevTab.Control) then
APrevTab.Control.Visible := False;
end;
// Check the item and invalidate NC
if FActiveTabIndex > -1 then begin
// Show the TabSheet
ATab := Items[FActiveTabIndex] as TSpTBXTabItem;
ATab.Checked := True;
if Assigned(ATab.Control) then begin
ATab.Control.Visible := True;
ATab.Control.BringToFront;
end;
MakeVisible(ATab);
end;
FOwnerTabControl.DoActiveTabChange(FActiveTabIndex);
// To avoid flicker don't call InvalidateNC, use the following instead
if not IsUpdating then begin
View.InvalidatePositions;
SetWindowPos(Handle, 0, 0, 0, 0, 0, SWP_NOZORDER or SWP_NOSIZE or
SWP_NOMOVE or SWP_DRAWFRAME or SWP_SHOWWINDOW);
end;
end;
end;
procedure TSpTBXTabToolbar.SetTabCloseButton(const Value: TSpTBXTabCloseButton);
begin
if FTabCloseButton <> Value then begin
FTabCloseButton := Value;
View.InvalidatePositions;
RightAlignItems;
InvalidateNC;
end;
end;
procedure TSpTBXTabToolbar.SetTabCloseButtonImageIndex(const Value: Integer);
begin
if FTabCloseButtonImageIndex <> Value then begin
FTabCloseButtonImageIndex := Value;
Invalidate;
end;
end;
procedure TSpTBXTabToolbar.SetTabAutofit(const Value: Boolean);
begin
if FTabAutofit <> Value then begin
FTabAutofit := Value;
if FTabAutofit then begin
Autofit;
InvalidateNC;
end;
end;
end;
procedure TSpTBXTabToolbar.SetTabAutofitMaxSize(const Value: Integer);
begin
if FTabAutofitMaxSize <> Value then begin
FTabAutofitMaxSize := Value;
if FTabAutofit then Autofit;
end;
end;
procedure TSpTBXTabToolbar.SetTabBackgroundBorders(const Value: Boolean);
begin
if FTabBackgroundBorders <> Value then begin
FTabBackgroundBorders := Value;
InvalidateNC;
end;
end;
procedure TSpTBXTabToolbar.SetTabColor(const Value: TColor);
begin
if (FTabColor <> Value) then begin
FTabColor := Value;
if FSkinType <> sknSkin then begin
Invalidate;
InvalidateNC;
end;
end;
end;
procedure TSpTBXTabToolbar.SetTabMaxSize(const Value: Integer);
begin
if FTabMaxSize <> Value then begin
FTabMaxSize := Value;
View.InvalidatePositions;
RightAlignItems;
InvalidateNC;
end;
end;
procedure TSpTBXTabToolbar.SetTabPosition(const Value: TSpTBXTabPosition);
begin
if FTabPosition <> Value then
FTabPosition := Value;
end;
procedure TSpTBXTabToolbar.SetSkinType(const Value: TSpTBXSkinType);
begin
if FSkinType <> Value then begin
FSkinType := Value;
Invalidate;
InvalidateNC;
end;
end;
procedure TSpTBXTabToolbar.CMDesignHitTest(var Message: TCMDesignHitTest);
var
P: TPoint;
IV: TTBItemViewer;
Shift: TShiftState;
begin
// Allow left-clicks on TabItems at design time
Shift := KeysToShiftState(Message.Keys);
if (csDesigning in ComponentState) and (ssLeft in Shift) and Assigned(View) then begin
P := SmallPointToPoint(Message.Pos);
IV := View.ViewerFromPoint(P);
if Assigned(IV) and Assigned(IV.Item) and (IV.Item is TSpTBXTabItem) then
IV.Item.Click;
end;
inherited;
end;
function TSpTBXTabToolbar.CanDragCustomize(Button: TMouseButton;
Shift: TShiftState; X, Y: Integer): Boolean;
var
IV: TTBItemViewer;
TabIV: TSpTBXTabItemViewer;
begin
Result := False;
FBeginDragIV := nil;
if not (csDesigning in ComponentState) and (Button = mbLeft) then begin
IV := SpGetItemViewerFromPoint(Items, View, Point(X, Y));
if Assigned(IV) and (IV is TSpTBXTabItemViewer) and Assigned(IV.Item) and IV.Item.Enabled and IV.Item.Visible then begin
// Close the tab if the close button is pressed
TabIV := TSpTBXTabItemViewer(IV);
if (TabIV.TabCloseButtonState = sknsHotTrack) and TabIV.IsTabCloseButtonVisible then begin
Result := True; // Bypass the inherited mouse down
TabIV.Item.TabClose;
end
else begin
// Click the item on mouse down
if not IV.Item.Checked then begin
Result := True; // Bypass the inherited mouse down
IV.Item.Click;
if Assigned(OnMouseDown) then OnMouseDown(Self, Button, Shift, X, Y);
end;
// Drag reorder
if FTabDragReorder and not IsCustomizing and IV.Item.Checked then begin
Result := True; // Bypass the inherited mouse down
FBeginDragIV := IV;
BeginDrag(False, 2);
end;
end;
end
else
Result := inherited CanDragCustomize(Button, Shift, X, Y);
end;
end;
procedure TSpTBXTabToolbar.DoStartDrag(var DragObject: TDragObject);
begin
if FTabDragReorder and Assigned(FBeginDragIV) and Assigned(FBeginDragIV.Item) and (FBeginDragIV is TSpTBXTabItemViewer) then begin
DragObject := TSpTBXTabItemDragObject.Create(Self, FBeginDragIV.Item);
inherited DoStartDrag(DragObject);
end
else
inherited DoStartDrag(DragObject);
end;
procedure TSpTBXTabToolbar.DragOver(Source: TObject; X, Y: Integer;
State: TDragState; var Accept: Boolean);
var
D: TSpTBXTabItemDragObject;
DestIV, RightAlignIV: TTBItemViewer;
OrigItem: TTBCustomItem;
OrigPos, DestPos, RightAlignPos: Integer;
begin
inherited DragOver(Source, X, Y, State, Accept);
if FTabDragReorder and Assigned(Source) and (Source is TSpTBXTabItemDragObject) then begin
D := Source as TSpTBXTabItemDragObject;
OrigItem := D.SouceItem;
OrigPos := OrigItem.Parent.IndexOf(OrigItem);
// Move the dragging item in the toolbar
if OrigItem.Parent = Items then begin
Accept := True;
SpGetDropPosItemViewer(Items, View, Point(X, Y), OrigPos, DestIV, DestPos);
RightAlignIV := SpGetFirstRightAlignSpacer(View);
if Assigned(RightAlignIV) then
RightAlignPos := Items.IndexOf(RightAlignIV.Item)
else
RightAlignPos := -1;
if (OrigPos <> DestPos) and (DestPos > -1) and (DestPos < Items.Count) and (OrigItem <> DestIV.Item) and
not ((RightAlignPos > -1) and (DestPos >= RightAlignPos)) then
begin
if FOwnerTabControl.CanActiveTabReorder(OrigPos, DestPos) then begin
BeginItemMove;
View.BeginUpdate;
try
// The item is the active tab, we need to update the ActiveTabIndex
// Just set the internal value because the page didn't change
FActiveTabIndex := DestPos;
Items.Move(OrigPos, DestPos);
FOwnerTabControl.DoActiveTabReorder(DestPos);
finally
View.EndUpdate;
EndItemMove;
InvalidateNC;
end;
end;
end;
end;
end;
end;
//WMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWM
{ TSpTBXTabSheet }
procedure TSpTBXTabSheet.AdjustClientRect(var Rect: TRect);
var
Margin, XPMargin: Integer;
begin
inherited AdjustClientRect(Rect);
if Assigned(FTabControl) then begin
Margin := 2;
XPMargin := 2;
// [Theme-Change]
// WinXP theme needs to have 4 pixel margin
if SpTBXSkinType(FTabControl.SkinType) = sknWindows then
XPMargin := Margin + 2;
Inc(Rect.Left, Margin);
Dec(Rect.Right, XPMargin);
case FTabControl.TabPosition of
ttpTop: dec(Rect.Bottom, XPMargin);
ttpBottom: inc(Rect.Top, XPMargin);
end;
end;
end;
constructor TSpTBXTabSheet.Create(AOwner: TComponent);
begin
inherited;
ControlStyle := ControlStyle + [csAcceptsControls, csSetCaption];
Align := alClient;
Visible := False;
end;
procedure TSpTBXTabSheet.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
if not (csDesigning in ComponentState) then begin
with Params do
Style := Style or WS_CLIPCHILDREN;
with Params.WindowClass do
Style := Style and not (CS_HREDRAW or CS_VREDRAW);
end;
end;
procedure TSpTBXTabSheet.DefineProperties(Filer: TFiler);
begin
inherited;
Filer.DefineProperty('TabItem', ReadItemName, WriteItemName, True);
end;
destructor TSpTBXTabSheet.Destroy;
begin
// If the Item <> nil it means the tabsheet was removed from the form at
// designtime or freed at runtime.
// If that happens TabDeleting was not called, we should call it before
// the tabsheet is destroyed to free the Item and delete it from the
// FPages list.
if Assigned(FItem) then
if Assigned(FTabControl) and not (csDestroying in FTabControl.ComponentState) then
FTabControl.TabDeleting(FItem, False);
FTabControl := nil;
FItem := nil;
inherited;
end;
procedure TSpTBXTabSheet.Notification(AComponent: TComponent;
Operation: TOperation);
begin
inherited;
if Operation = opRemove then
if AComponent = FPrevFocused then FPrevFocused := nil;
end;
procedure TSpTBXTabSheet.VisibleChanging;
begin
if not (csDesigning in ComponentState) then
if Visible then begin
// TabSheet will be hidden, save the focused control
if Assigned(FPrevFocused) then FPrevFocused.RemoveFreeNotification(Self);
SpIsFocused(Self, FPrevFocused);
if Assigned(FPrevFocused) then FPrevFocused.FreeNotification(Self);
end;
inherited;
end;
procedure TSpTBXTabSheet.CMVisiblechanged(var Message: TMessage);
begin
if not (csDesigning in ComponentState) then
if Visible then begin
// TabSheet was showed.
// Focus the previous focused control, or focus the first child
if Assigned(FPrevFocused) then begin
if SpCanFocus(FPrevFocused) then
FPrevFocused.SetFocus;
FPrevFocused.RemoveFreeNotification(Self);
FPrevFocused := nil;
end
else
SpFocusFirstChild(Self);
end;
inherited;
end;
function TSpTBXTabSheet.GetCaption: WideString;
begin
if Assigned(FItem) then Result := FItem.Caption
else Result := '';
end;
function TSpTBXTabSheet.GetImageIndex: Integer;
begin
if Assigned(FItem) then Result := FItem.ImageIndex
else Result := -1;
end;
function TSpTBXTabSheet.GetTabVisible: Boolean;
begin
if Assigned(FItem) then Result := FItem.Visible
else Result := False;
end;
procedure TSpTBXTabSheet.SetCaption(const Value: WideString);
begin
if Assigned(FItem) then FItem.Caption := Value;
end;
procedure TSpTBXTabSheet.SetImageIndex(const Value: Integer);
begin
if Assigned(FItem) then FItem.ImageIndex := Value;
end;
procedure TSpTBXTabSheet.SetTabVisible(const Value: Boolean);
begin
if Assigned(FItem) then FItem.Visible := Value;
end;
procedure TSpTBXTabSheet.ReadItemName(Reader: TReader);
begin
case Reader.NextValue of
vaLString, vaString:
FItemName := Reader.ReadString;
else
FItemName := Reader.ReadWideString;
end;
end;
procedure TSpTBXTabSheet.WriteItemName(Writer: TWriter);
begin
if Assigned(Item) then
FItemName := Item.Name;
Writer.WriteWideString(FItemName);
end;
procedure TSpTBXTabSheet.ReadState(Reader: TReader);
var
C: TComponent;
TC: TSpTBXCustomTabControl;
begin
// The TabSheet is being created from the DFM stream
// We must set the initial values of TabControl, Item and add itself to
// the Pages list of the parent TabControl.
inherited ReadState(Reader);
if Reader.Parent is TSpTBXCustomTabControl then begin
// Set TabControl
TC := TSpTBXCustomTabControl(Reader.Parent);
TabControl := TC;
// Set Item and add Self to TabControl.Pages
if not Assigned(FItem) and (FItemName <> '') then begin
C := Owner.FindComponent(FItemName);
if Assigned(C) and (C is TSpTBXTabItem) then begin
FItem := C as TSpTBXTabItem;
FItem.Control := Self;
if TC.FPages.IndexOf(Self) = -1 then
TC.FPages.Add(Self);
end;
end;
end;
end;
procedure TSpTBXTabSheet.WMEraseBkgnd(var Message: TWMEraseBkgnd);
var
R: TRect;
begin
Message.Result := 1;
if Assigned(FTabControl) and Visible then begin
if not DoubleBuffered or (TMessage(Message).wParam = TMessage(Message).lParam) then begin
R := ClientRect;
if FTabControl.TabVisible then begin
case FTabControl.TabPosition of
ttpTop: dec(R.Top, 4);
ttpBottom: inc(R.Bottom, 4);
end;
end;
FTabControl.DrawBackground(Message.DC, R);
end;
end;
end;
procedure TSpTBXTabSheet.WMWindowPosChanged(var Message: TWMWindowPosChanged);
begin
inherited;
SpInvalidateSpTBXControl(Self, True, True);
end;
//WMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWM
{ TSpTBXCustomTabSet }
constructor TSpTBXCustomTabSet.Create(AOwner: TComponent);
begin
inherited;
ControlStyle := ControlStyle + [csAcceptsControls, csOpaque];
FTabVisible := True;
FBackground := TBitmap.Create;
Width := 289;
Height := FDock.Height + 2;
ParentColor := False;
Color := clBtnFace;
FToolbar.Items.RegisterNotification(ItemNotification);
end;
destructor TSpTBXCustomTabSet.Destroy;
begin
FToolbar.Items.UnRegisterNotification(ItemNotification);
FreeAndNil(FBackground);
inherited;
end;
procedure TSpTBXCustomTabSet.DefineProperties(Filer: TFiler);
begin
inherited;
Filer.DefineProperty('HiddenItems', ReadHiddenItems, WriteHiddenItems, True);
end;
procedure TSpTBXCustomTabSet.ReadHiddenItems(Reader: TReader);
begin
if Reader.ReadValue = vaCollection then
Reader.ReadCollection(Toolbar.FHiddenTabs);
end;
procedure TSpTBXCustomTabSet.WriteHiddenItems(Writer: TWriter);
begin
Writer.WriteCollection(Toolbar.FHiddenTabs);
end;
procedure TSpTBXCustomTabSet.Loaded;
var
I: Integer;
CacheCollection: TSpTBXItemCacheCollection;
Cache: TSpTBXItemCache;
C: TComponent;
begin
ActiveTabIndex := FLoadingActiveIndex;
inherited;
// Read the HiddenTabs collection, and fill the Item property of the
// collection items reading the Name from the DFM
CacheCollection := Toolbar.FHiddenTabs;
if Assigned(CacheCollection) then
for I := CacheCollection.Count - 1 downto 0 do begin
Cache := CacheCollection[I];
if not Assigned(Cache.Item) then begin
if Cache.Name = '' then
CacheCollection.Delete(I)
else begin
C := Owner.FindComponent(Cache.Name);
if Assigned(C) and (C is TTBCustomItem) then begin
Cache.Item := C as TTBCustomItem;
// If the Item is visible then the entry is not valid, delete it
if Cache.Item.Visible then
CacheCollection.Delete(I);
end;
end;
end;
end;
if TabAutofit then
Toolbar.Autofit;
end;
function TSpTBXCustomTabSet.GetToolbarClass: TSpTBXToolbarClass;
begin
Result := TSpTBXTabToolbar;
end;
function TSpTBXCustomTabSet.GetFullRepaint: Boolean;
begin
Result := True;
end;
function TSpTBXCustomTabSet.Add(ACaption: WideString): TSpTBXTabItem;
var
I: Integer;
SpacerIV: TSpTBXItemViewer;
begin
Result := TSpTBXTabItem.Create(Self);
try
Result.Caption := ACaption;
SpacerIV := SpGetFirstRightAlignSpacer(View);
if Assigned(SpacerIV) then begin
I := Items.IndexOf(SpacerIV.Item);
if I > -1 then
Items.Insert(I, Result);
end
else
Items.Add(Result);
except
Result.Free;
Result := nil;
end;
end;
function TSpTBXCustomTabSet.Insert(NewIndex: Integer; ACaption: WideString): TSpTBXTabItem;
begin
Result := TSpTBXTabItem.Create(Self);
try
Result.Caption := ACaption;
Items.Insert(NewIndex, Result);
except
Result.Free;
Result := nil;
end;
end;
procedure TSpTBXCustomTabSet.TabClick(ATab: TSpTBXTabItem);
begin
ATab.Click; // calls TabToolbar.DoTabClick and Self.DoTabClick
end;
function TSpTBXCustomTabSet.CanActiveTabChange(const TabIndex, NewTabIndex: Integer): Boolean;
begin
Result := True;
if not (csLoading in ComponentState) then
if (NewTabIndex > -1) and not Items[NewTabIndex].Checked then
if Assigned(FOnActiveTabChanging) then FOnActiveTabChanging(Self, TabIndex, NewTabIndex, Result);
end;
procedure TSpTBXCustomTabSet.DoActiveTabChange(const TabIndex: Integer);
begin
if not (csLoading in ComponentState) then
if Assigned(FOnActiveTabChange) then FOnActiveTabChange(Self, TabIndex);
end;
function TSpTBXCustomTabSet.CanActiveTabReorder(const TabIndex, NewTabIndex: Integer): Boolean;
begin
Result := True;
if Assigned(FOnActiveTabReordering) then FOnActiveTabReordering(Self, TabIndex, NewTabIndex, Result);
end;
procedure TSpTBXCustomTabSet.DoActiveTabReorder(const TabIndex: Integer);
begin
if Assigned(FOnActiveTabReorder) then FOnActiveTabReorder(Self, TabIndex);
end;
procedure TSpTBXCustomTabSet.MakeVisible(ATab: TSpTBXTabItem);
begin
if Assigned(FToolbar) then Toolbar.MakeVisible(ATab);
end;
procedure TSpTBXCustomTabSet.ScrollLeft;
begin
if Assigned(FToolbar) then Toolbar.ScrollLeft;
end;
procedure TSpTBXCustomTabSet.ScrollRight;
begin
if Assigned(FToolbar) then Toolbar.ScrollRight;
end;
procedure TSpTBXCustomTabSet.ScrollState(out Left, Right: Boolean);
begin
if Assigned(FToolbar) then Toolbar.ScrollState(Left, Right);
end;
function TSpTBXCustomTabSet.GetActiveTab: TSpTBXTabItem;
begin
if Assigned(FToolbar) then
Result := Toolbar.ActiveTab
else
Result := nil;
end;
function TSpTBXCustomTabSet.GetActiveTabIndex: Integer;
begin
if Assigned(FToolbar) then
Result := Toolbar.ActiveTabIndex
else
Result := -1;
end;
procedure TSpTBXCustomTabSet.SetActiveTabIndex(Value: Integer);
begin
// When the component is reading from the DFM the Items are not created.
// We must save the value setted at design time and use it when the
// form is finally loaded.
if csReading in ComponentState then
FLoadingActiveIndex := Value
else
if Assigned(FToolbar) then
Toolbar.ActiveTabIndex := Value;
end;
function TSpTBXCustomTabSet.GetTabCloseButton: TSpTBXTabCloseButton;
begin
if Assigned(FToolbar) then
Result := Toolbar.TabCloseButton
else
Result := tcbNone;
end;
procedure TSpTBXCustomTabSet.SetTabCloseButton(const Value: TSpTBXTabCloseButton);
begin
if Assigned(FToolbar) then
Toolbar.TabCloseButton := Value;
end;
function TSpTBXCustomTabSet.GetTabCloseButtonImageIndex: Integer;
begin
if Assigned(FToolbar) then
Result := Toolbar.TabCloseButtonImageIndex
else
Result := -1;
end;
procedure TSpTBXCustomTabSet.SetTabCloseButtonImageIndex(const Value: Integer);
begin
if Assigned(FToolbar) then
Toolbar.TabCloseButtonImageIndex := Value;
end;
function TSpTBXCustomTabSet.GetTabAutofit: Boolean;
begin
if Assigned(FToolbar) then
Result := Toolbar.TabAutofit
else
Result := False;
end;
procedure TSpTBXCustomTabSet.SetTabAutofit(const Value: Boolean);
begin
if Assigned(FToolbar) then
Toolbar.TabAutofit := Value;
end;
function TSpTBXCustomTabSet.GetTabAutofitMaxSize: Integer;
begin
if Assigned(FToolbar) then
Result := Toolbar.TabAutofitMaxSize
else
Result := -1;
end;
procedure TSpTBXCustomTabSet.SetTabAutofitMaxSize(const Value: Integer);
begin
if Assigned(FToolbar) then
Toolbar.TabAutofitMaxSize := Value;
end;
function TSpTBXCustomTabSet.GetTabBackgroundBorders: Boolean;
begin
if Assigned(FToolbar) then
Result := Toolbar.TabBackgroundBorders
else
Result := True;
end;
procedure TSpTBXCustomTabSet.SetTabBackgroundBorders(const Value: Boolean);
begin
if Assigned(FToolbar) then Toolbar.TabBackgroundBorders := Value
end;
function TSpTBXCustomTabSet.GetTabBackgroundColor: TColor;
begin
if Assigned(FToolbar) then
Result := FToolbar.Color
else
Result := clNone;
end;
procedure TSpTBXCustomTabSet.SetTabBackgroundColor(const Value: TColor);
begin
if Assigned(FToolbar) then FToolbar.Color := Value
end;
function TSpTBXCustomTabSet.GetTabDragReorder: Boolean;
begin
Result := False;
if Assigned(FToolbar) then
Result := Toolbar.TabDragReorder;
end;
procedure TSpTBXCustomTabSet.SetTabDragReorder(const Value: Boolean);
begin
if Assigned(FToolbar) then
Toolbar.TabDragReorder := Value;
end;
function TSpTBXCustomTabSet.GetTabMaxSize: Integer;
begin
Result := -1;
if Assigned(FToolbar) then
Result := Toolbar.TabMaxSize;
end;
procedure TSpTBXCustomTabSet.SetTabMaxSize(const Value: Integer);
begin
if Assigned(FToolbar) then
Toolbar.TabMaxSize := Value;
end;
function TSpTBXCustomTabSet.GetTabToolbar: TSpTBXTabToolbar;
begin
Result := FToolbar as TSpTBXTabToolbar;
end;
function TSpTBXCustomTabSet.GetSkinType: TSpTBXSkinType;
begin
if Assigned(FToolbar) then
Result := Toolbar.SkinType
else
Result := sknNone;
end;
procedure TSpTBXCustomTabSet.SetSkinType(const Value: TSpTBXSkinType);
begin
if Assigned(FToolbar) then begin
Toolbar.SkinType := Value;
InvalidateBackground;
end;
end;
function TSpTBXCustomTabSet.GetTabPosition: TSpTBXTabPosition;
begin
if Assigned(FToolbar) then
Result := Toolbar.TabPosition
else
Result := ttpTop;
end;
procedure TSpTBXCustomTabSet.SetTabPosition(const Value: TSpTBXTabPosition);
var
T: TSpTBXTabToolbar;
begin
if Assigned(FToolbar) and Assigned(FDock) then begin
T := Toolbar;
if T.TabPosition <> Value then begin
T.Visible := False;
T.Parent := nil;
T.TabPosition := Value;
case Value of
ttpTop: FDock.Position := dpTop;
ttpBottom: FDock.Position := dpBottom;
end;
T.CurrentDock := FDock;
T.Visible := True;
InvalidateBackground;
end;
end;
end;
procedure TSpTBXCustomTabSet.SetTabVisible(const Value: Boolean);
begin
if FTabVisible <> Value then begin
FTabVisible := Value;
if Assigned(FDock) then
FDock.Visible := Value;
end;
end;
function TSpTBXCustomTabSet.GetTabSetHeight: Integer;
begin
if Assigned(FDock) then
Result := FDock.Height
else
Result := 0;
end;
procedure TSpTBXCustomTabSet.DoDrawBackground(ACanvas: TCanvas;
ARect: TRect; const PaintStage: TSpTBXPaintStage;
var PaintDefault: Boolean);
begin
if Assigned(FOnDrawBackground) then FOnDrawBackground(Self, ACanvas, ARect, PaintStage, PaintDefault);
end;
function TSpTBXCustomTabSet.DrawBackground(DC: HDC; ARect: TRect): Boolean;
var
ACanvas: TCanvas;
PaintDefault: Boolean;
R: TRect;
begin
Result := False;
if (csDestroying in ComponentState) or not Assigned(FDock) or
not Assigned(FBackground) or IsRectEmpty(ARect) then Exit;
ACanvas := TCanvas.Create;
try
ACanvas.Handle := DC;
R := Rect(0, 0, ARect.Right - ARect.Left, ARect.Bottom - ARect.Top);
if (FBackground.Width = R.Right) and (FBackground.Height = R.Bottom) and not Assigned(FOnDrawBackground) then
ACanvas.Draw(ARect.Left, ARect.Top, FBackground)
else begin
FBackground.Width := R.Right;
FBackground.Height := R.Bottom;
FBackground.Canvas.Brush.Color := clWhite;
FBackground.Canvas.FillRect(R);
PaintDefault := True;
DoDrawBackground(FBackground.Canvas, R, pstPrePaint, PaintDefault);
if PaintDefault then
SpDrawXPTabControlBackground(FBackground.Canvas, R, Color, TabPosition = ttpBottom, SkinType);
PaintDefault := True;
DoDrawBackground(FBackground.Canvas, R, pstPostPaint, PaintDefault);
ACanvas.Draw(ARect.Left, ARect.Top, FBackground);
end;
Result := True;
finally
ACanvas.Handle := 0;
ACanvas.Free;
end;
end;
procedure TSpTBXCustomTabSet.InvalidateBackground(InvalidateChildren: Boolean);
begin
// Force background repaint
if not (csDestroying in ComponentState) and Assigned(FToolbar) then begin
if Assigned(FBackground) then
FBackground.Width := 1;
SpInvalidateSpTBXControl(Self, InvalidateChildren, FResizing);
end;
end;
procedure TSpTBXCustomTabSet.ItemNotification(Ancestor: TTBCustomItem;
Relayed: Boolean; Action: TTBItemChangedAction; Index: Integer;
Item: TTBCustomItem);
var
I: Integer;
Tab: TSpTBXTabItem;
begin
inherited;
if Assigned(FToolbar) and not Relayed and not FToolbar.IsItemMoving then
case Action of
tbicSubitemsBeginUpdate:
begin
// When a Tab item is moved (TTBCustomItem.Move)
// tbicDeleting and tbicInserted change actions are fired
// but we don't want the associated TabSheet to be recreated
// because the children will be destroyed.
// When a TTBCustomItem is moved it is not recreated, it simply
// deletes and reinserts its reference in the items array.
// We need to find out if the item is being moved and stop the
// TabSheet recreation.
// The action sequence for a move operation is the following:
// tbicSubitemsBeginUpdate (FItemMoveCount = 1)
// tbicDeleting (FItemMoveCount = 2)
// tbicSubitemsBeginUpdate (FItemMoveCount = 1)
// tbicInserted (FItemMoveCount = 0)
// tbicSubitemsEndUpdate (FItemMoveCount = 0)
// tbicSubitemsEndUpdate (FItemMoveCount = 0)
FItemMoveCount := 1;
FItemMoved := nil;
end;
tbicSubitemsEndUpdate:
begin
// Destroy the TabSheet if the sequence was:
// tbicSubitemsBeginUpdate - tbicDeleting - tbicSubitemsEndUpdate
if FItemMoveCount = 2 then
TabDeleting(FItemMoved);
FItemMoveCount := 0;
FItemMoved := nil;
end;
tbicInserted:
if Assigned(Item) then begin
// Update the index if a new item is inserted before the ActiveTabIndex
I := Items.IndexOf(Item);
if (I > -1) and (I <= ActiveTabIndex) then begin
FUpdatingIndex := True;
try
// Don't change the ActiveTabIndex, just set the internal value
// because the page didn't change
Toolbar.FActiveTabIndex := Toolbar.FActiveTabIndex + 1;
finally
FUpdatingIndex := False;
end;
end;
if (Item is TSpTBXTabItem) then
TabInserted(Item as TSpTBXTabItem);
InvalidateBackground;
FItemMoveCount := 0;
FItemMoved := nil;
end;
tbicDeleting:
// The ItemViewer of the Item is not valid, it was destroyed by TTBView
// The Items array still has the Item.
if not (csDestroying in ComponentState) and Assigned(Item) then begin
FUpdatingIndex := True;
try
Tab := nil;
I := Items.IndexOf(Item);
if I > -1 then begin
if I < ActiveTabIndex then
// Don't change the ActiveTabIndex, just set the internal value
// because the page didn't change
Toolbar.FActiveTabIndex := Toolbar.FActiveTabIndex - 1
else
if I = ActiveTabIndex then
if I = 0 then begin
if (Items.Count > 1) and (Items[1] is TSpTBXTabItem) then begin
// The first tab was deleted, change the internal value of
// Update the checked tab on WM_INVALIDATETABBACKGROUND
Tab := Items[1] as TSpTBXTabItem;
Tab.Click;
end
else
Toolbar.FActiveTabIndex := -1;
end
else begin
Dec(I); // Prev tab
if (I > -1) and (I < Items.Count) and (Items[I] is TSpTBXTabItem) then
SetActiveTabIndex(I)
else
Toolbar.FActiveTabIndex := -1;
end;
end;
if (Item is TSpTBXTabItem) then
if FItemMoveCount = 1 then begin
FItemMoveCount := 2;
FItemMoved := Item as TSpTBXTabItem;
end
else begin
FItemMoveCount := 0;
TabDeleting(Item as TSpTBXTabItem);
end;
if (csDesigning in ComponentState) or Assigned(Tab) then
PostMessage(Handle, WM_INVALIDATETABBACKGROUND, 0, 0)
else
InvalidateBackground;
finally
FUpdatingIndex := False;
end;
end;
tbicInvalidate:
// When the Item.Checked property changes we must reset the ActiveTabIndex
if not FUpdatingIndex and Assigned(Item) and (Item is TSpTBXTabItem) and
Item.Checked and Item.Enabled then
begin
I := Items.IndexOf(Item);
if I <> ActiveTabIndex then begin
FUpdatingIndex := True;
try
SetActiveTabIndex(I);
finally
FUpdatingIndex := False;
end;
end;
end;
end;
end;
procedure TSpTBXCustomTabSet.TabDeleting(Item: TSpTBXTabItem;
FreeTabSheet: Boolean);
begin
if not (csDestroying in ComponentState) then
ScrollLeft;
end;
procedure TSpTBXCustomTabSet.TabInserted(Item: TSpTBXTabItem);
var
I: Integer;
IV: TTBItemViewer;
begin
if not (csLoading in ComponentState) and Assigned(Item) and (Item is TSpTBXTabItem) then
if Items.Count = 1 then
Item.Click // Select the first Tab
else begin
// If the item is inserted after a hidden tab, we should also hide it
I := Items.IndexOf(Item) - 1;
if I > -1 then
if Toolbar.FHiddenTabs.IndexOf(Items[I]) > -1 then begin
IV := View.Find(Item);
if Assigned(IV) then begin
Toolbar.BeginUpdate;
try
Toolbar.FHiddenTabs.Add(Item);
Item.Visible := False;
finally
Toolbar.EndUpdate;
end;
end;
end;
end;
end;
procedure TSpTBXCustomTabSet.CMColorchanged(var Message: TMessage);
begin
inherited;
if Assigned(FToolbar) then begin
Toolbar.TabColor := Color;
InvalidateBackground;
end;
end;
procedure TSpTBXCustomTabSet.CMSpTBXControlsInvalidate(var Message: TMessage);
begin
InvalidateBackground;
Message.Result := 1;
end;
procedure TSpTBXCustomTabSet.WMEraseBkgnd(var Message: TMessage);
var
R: TRect;
begin
if not DoubleBuffered or (Message.wParam = Message.lParam) then begin
if not (csDestroying in ComponentState) and GetFullRepaint then begin
R := ClientRect;
if FTabVisible then begin
case TabPosition of
ttpTop: Inc(R.Top, GetTabSetHeight - 2);
ttpBottom: Dec(R.Bottom, GetTabSetHeight - 2);
end;
end;
DrawBackground(TWMEraseBkgnd(Message).DC, R);
end;
end;
Message.Result := 1;
end;
procedure TSpTBXCustomTabSet.WMInvalidateTabBackground(var Message: TMessage);
var
Tab: TSpTBXTabItem;
I: Integer;
begin
if Assigned(FToolbar) then begin
Tab := Toolbar.ActiveTab;
if Assigned(Tab) then begin
I := Items.IndexOf(Tab);
FUpdatingIndex := True;
try
Toolbar.FActiveTabIndex := -1;
SetActiveTabIndex(I);
finally
FUpdatingIndex := False;
end;
end;
if HandleAllocated then
RedrawWindow(Handle, nil, 0, RDW_ERASE or RDW_INVALIDATE or RDW_ALLCHILDREN);
end;
end;
procedure TSpTBXCustomTabSet.WMWindowPosChanged(var Message: TWMWindowPosChanged);
begin
FResizing := True;
inherited;
if GetFullRepaint then
InvalidateBackground;
FResizing := False;
end;
//WMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWM
{ TSpTBXCustomTabControl }
constructor TSpTBXCustomTabControl.Create(AOwner: TComponent);
begin
inherited;
// ControlStyle := ControlStyle - [csAcceptsControls];
FPages := TList.Create;
// FEmptyTabSheet is used to hide the rest of the TabSheets
// when ActiveTabIndex = -1 at design time.
FEmptyTabSheet := TSpTBXTabSheet.Create(Self);
FEmptyTabSheet.Parent := Self;
FEmptyTabSheet.TabControl := Self;
FEmptyTabSheet.Item := nil;
FEmptyTabSheet.Visible := True;
FEmptyTabSheet.BringToFront;
FEmptyTabSheet.ControlStyle := FEmptyTabSheet.ControlStyle - [csAcceptsControls];
Width := 289;
Height := 193;
end;
destructor TSpTBXCustomTabControl.Destroy;
begin
FPages.Free;
inherited;
end;
procedure TSpTBXCustomTabControl.DoActiveTabChange(const ItemIndex: Integer);
begin
if ItemIndex = -1 then begin
FEmptyTabSheet.Visible := True;
FEmptyTabSheet.BringToFront;
end
else
FEmptyTabSheet.Visible := False;
inherited;
end;
procedure TSpTBXCustomTabControl.RealignTabSheets;
var
I, C: Integer;
begin
if HandleAllocated then begin
C := PagesCount;
for I := 0 to C - 1 do
Pages[I].Realign;
end;
end;
function TSpTBXCustomTabControl.GetFullRepaint: Boolean;
begin
if not (csDestroying in ComponentState) then
Result := not Assigned(FPages) or (FPages.Count = 0) or not Assigned(FToolbar) or
not Assigned(Toolbar.ActiveTab) or not Toolbar.ActiveTab.Checked
else
Result := False;
end;
function TSpTBXCustomTabControl.GetPage(Item: TSpTBXTabItem): TSpTBXTabSheet;
var
I: Integer;
begin
Result := nil;
I := Items.IndexOf(Item);
if (I > - 1) and Assigned(Item.Control) and (Item.Control is TSpTBXTabSheet) then
Result := Item.Control as TSpTBXTabSheet;
end;
function TSpTBXCustomTabControl.GetActivePage: TSpTBXTabSheet;
begin
if ActiveTabIndex > -1 then
Result := GetPage(Items[ActiveTabIndex] as TSpTBXTabItem)
else
Result := nil;
end;
procedure TSpTBXCustomTabControl.SetActivePage(const Value: TSpTBXTabSheet);
var
I: Integer;
begin
if Assigned(Value) and (FPages.IndexOf(Value) > -1) and Assigned(FToolbar) then begin
I := FToolbar.Items.IndexOf(Value.Item);
if I > -1 then ActiveTabIndex := I;
end;
end;
function TSpTBXCustomTabControl.GetPages(Index: Integer): TSpTBXTabSheet;
begin
Result := TSpTBXTabSheet(FPages[Index]);
end;
function TSpTBXCustomTabControl.GetPagesCount: Integer;
begin
Result := FPages.Count;
end;
procedure TSpTBXCustomTabControl.TabInserted(Item: TSpTBXTabItem);
var
T: TSpTBXTabSheet;
I: Integer;
begin
// Create a TabSheet and Link it to the TabItem, only if the Item is created
// at DesignTime.
// If the Item is created from the DFM stream then the TabSheet will be
// automatically created, because it will also be streamed, but it won't be
// linked to the Item, this is done in TabSheet.ReadState
if (csLoading in ComponentState) or not Assigned(Item) then Exit;
for I := 0 to FPages.Count - 1 do begin
T := TSpTBXTabSheet(FPages[I]);
if T.Item = Item then begin
Exit;
raise Exception.Create('TabSheet Already Exists');
end;
end;
// Find unique name
I := 1;
while Owner.FindComponent('SpTBXTabSheet' + IntToStr(I)) <> nil do
inc(I);
// The Form will be the owner, it will stream the tabsheet to the DFM
T := TSpTBXTabSheet.Create(Owner);
T.Name := 'SpTBXTabSheet' + IntToStr(I);
T.Parent := Self;
T.TabControl := Self;
T.Item := Item;
Item.Control := T;
T.SendToBack;
FPages.Add(T);
inherited;
end;
procedure TSpTBXCustomTabControl.TabDeleting(Item: TSpTBXTabItem;
FreeTabSheet: Boolean);
var
I: Integer;
T: TSpTBXTabSheet;
begin
inherited;
// The Toolbar will free the Items, and the Form will free the TabSheets
if (csDestroying in ComponentState) or not Assigned(Item) then Exit;
for I := 0 to FPages.Count - 1 do begin
T := TSpTBXTabSheet(FPages[I]);
if Assigned(T) and Assigned(T.Item) and (T.Item = Item) then begin
FPages[I] := nil;
FPages.Delete(I);
if FreeTabSheet then begin
T.Item := nil;
T.Free;
end
else begin
// TabSheet deleted at design time, free the linked Item
T.Item.Free;
T.Item := nil;
end;
Break;
end;
end;
end;
procedure TSpTBXCustomTabControl.CMSpTBXControlsInvalidate(var Message: TMessage);
var
I, C: Integer;
begin
// Force TabControl and TabSheets background repaint
inherited;
if not (csDestroying in ComponentState) and Assigned(FToolbar) then begin
C := PagesCount;
for I := 0 to C - 1 do
SpInvalidateSpTBXControl(Pages[I], True, True);
end;
Message.Result := 1;
end;
procedure TSpTBXCustomTabControl.WMSpSkinChange(var Message: TMessage);
begin
inherited;
RealignTabSheets;
end;
end.