fcbuttongroup.pas
上传用户:hylc_2004
上传日期:2014-01-23
资源大小:46800k
文件大小:36k
源码类别:

Delphi控件源码

开发平台:

Delphi

  1. unit fcButtonGroup;
  2. {
  3. //
  4. // Components : TfcButtonGroup
  5. //
  6. // Copyright (c) 1999 by Woll2Woll Software
  7. //
  8. // 5/13/99 - RSW - When transparent, paint should also paint the button area
  9. // 3/24/2000 - PYW - Need to check both Horizontal and Vertical
  10. // 9/5/00 - Index based on visible buttons only
  11. }
  12. interface
  13. {$i fcIfDef.pas}
  14. uses
  15.   Windows, Messages, SysUtils, Classes, TypInfo, Graphics, Controls,
  16.   Forms, Dialogs, StdCtrls, ExtCtrls, Math, fcChangeLink,
  17.   fcButton, fcClearPanel, fcCommon, fcShapeBtn, fcImgBtn, fcCollection;
  18. type
  19.   TfcButtonGroupItem = class;
  20.   TfcButtonGroupItems = class;
  21.   TfcCustomButtonGroup = class;
  22.   TfcButtonGroupItemClass = class of TfcButtonGroupItem;
  23.   TfcButtonGroupItemsClass = class of TfcButtonGroupItems;
  24. {
  25. // - TfcButtonGroupItem
  26. //
  27. // Properties:
  28. // - GroupIndex: Wrapper to GroupIndex property of the Item's control.
  29. //               This property is dependent on the GroupIndexPropName
  30. //               property of TfcButtonGroupItems.
  31. //
  32. // - Selected:   Wrapper to the property of the the Item's property that
  33. //               signifies selection.  This is usually the "Down" property.
  34. //
  35. // - PointerTag: The pointer equivalent to the Tag property.  (Yes, the
  36. //               Tag property can be used for this purpose also, but that
  37. //               looks really ugly in code.
  38. //
  39. // - Tag:        The standard Tag property.
  40. //
  41. // - Control:    The "Control" of the control group.  The type of this
  42. //               component is determined by the ControlClass property of
  43. //               the control group.  This property can NOT be published
  44. //               or Delphi's IDE will mistake the ButtonGroup for a form
  45. //               and generate errors.
  46. }
  47.   TfcButtonGroupItem = class(TfcCollectionItem)
  48.   private
  49.     // Property storage variables
  50.     FButton: TfcCustomBitBtn;
  51.     // Property access methods
  52.     function GetButtonGroup: TfcCustomButtonGroup; virtual;
  53.     function GetGroupIndex: Integer; virtual;
  54.     function GetSelected: Boolean; virtual;
  55.     procedure SetGroupIndex(Value: Integer); virtual;
  56.     procedure SetSelected(Value: Boolean); virtual;
  57.   protected
  58.     // Virtual Methods
  59.     function GetDisplayName: string; override;
  60.     procedure Loaded; virtual;
  61.     procedure SetIndex(Value: Integer); override;
  62.   public
  63.     constructor Create(Collection: TCollection); override;
  64.     destructor Destroy; override;
  65.     function GetInstance(const PropertyName: string): TPersistent; override;
  66.     property Button: TfcCustomBitBtn read FButton;
  67.     property ButtonGroup: TfcCustomButtonGroup read GetButtonGroup;
  68.     property GroupIndex: Integer read GetGroupIndex write SetGroupIndex;
  69.     property Selected: Boolean read GetSelected write SetSelected;
  70.   end;
  71. {
  72. // - TfcButtonGroupItems
  73. // Properties:
  74. // - Items:       The indexed array property that returns the
  75. //                TfcButtonGroupItem corresponding to the Index
  76. //                parameter.
  77. //
  78. // - Selected:    Returns the control that currently has its "Selected"
  79. //                property set to true.  If none, then returns nil.
  80. //
  81. // Methods:
  82. // - Add:         Adds a new item to the control group and returns the newly
  83. //                created item.
  84. //
  85. // - Clear:       Deletes all of the items in the TfcButtonGroupItems array
  86. //                and each of the associated Controls.
  87. //
  88. // - FindControl: Searches through the array of TfcButtonGroupItems and
  89. //                returns the item that has its Control property pointing
  90. //                to the AControl parameter.
  91. //
  92. // - FindPointerTag: Searches through the array of TfcButtonGroupItems
  93. //                and returns the item that has its PointerTag property
  94. //                pointing to the APointerTag parameter.
  95. }
  96.   TfcButtonGroupItems = class(TfcCollection)
  97.   private
  98.     FButtonGroup: TfcCustomButtonGroup;
  99.   protected
  100.     // Overriden methods
  101.     function GetOwner: TPersistent; override;
  102.     procedure Update(Item: TCollectionItem); override;
  103.     // Property access methods
  104.     function GetButtonGroup: TfcCustomButtonGroup; virtual;
  105.     function GetItems(Index: Integer): TfcButtonGroupItem;
  106.     function GetVisibleCount: Integer;
  107.     function GetVisibleItems(Index: Integer): TfcButtonGroupItem;
  108.   public
  109.     ArrangingControls: Boolean;
  110.     AddingControls: Boolean;
  111.     DeletingControl: Boolean;
  112.     constructor Create(AButtonGroup: TfcCustomButtonGroup; ACollectionItemClass: TfcButtonGroupItemClass); virtual;
  113.     function Add: TfcButtonGroupItem;
  114. //    function AddInLoading: TfcButtonGroupItem; { RSW - Don't check loading state }
  115.     function AddItem: TfcCollectionItem; override;
  116.     procedure ArrangeControls; virtual;
  117.     function FindButton(AButton: TfcCustomBitBtn): TfcButtonGroupItem; virtual;
  118.     function FindPointerTag(APointerTag: Pointer): TfcButtonGroupItem; virtual;
  119.     procedure Clear; virtual;
  120.     property ButtonGroup: TfcCustomButtonGroup read GetButtonGroup;
  121.     property Items[Index: Integer]: TfcButtonGroupItem read GetItems {stored False}; default;
  122.     property VisibleCount: Integer read GetVisibleCount;
  123.     property VisibleItems[Index: Integer]: TfcButtonGroupItem read GetVisibleItems;
  124.   end;
  125.   TfcButtonGroupChangeEvent = procedure(ButtonGroup: TfcCustomButtonGroup;
  126.     OldSelected, Selected: TfcButtonGroupItem) of object;
  127. {
  128. // - TfcCustomButtonGroup
  129. // Properties:
  130. // - ControlSpacing: The spacing between each of the controls.  This does
  131. //                   not include spacing on the outer edge; for that use
  132. //                   the standard BorderWidth property.
  133. //
  134. // - Columns:        This effect of this property is dependent on the
  135. //                   Layout property.  If Layout is vertical (the default),
  136. //                   then this property specifies the number of columns;
  137. //                   otherwise it specifies the number of rows.
  138. //
  139. // - Items:          The TfcButtonGroupItems array property.
  140. //
  141. // - Layout:         Determines the orientation of the controls -- how they
  142. //                   are arranged.  If this property is set to cglVertical,
  143. //                   then controls arranged in a top-down, left-to-right
  144. //                   fashion.  Otherwise, they are arranged in a left-to-
  145. //                   right, top-down fashion.
  146. //
  147. // - MaxControlSize: Normally, controls are sized to take the maximum amount
  148. //                   of space available given the size of the control group.
  149. //                   This property allows the user to specify the maximum size
  150. //                   of a button. (For example, the buttons on the Win95 Task
  151. //                   Bar can only be a maximum of ~150 pixels.
  152. //
  153. // Events:
  154. // - OnChange:             Occurs immediately after the currently selected
  155. //                         control has changed.
  156. //
  157. // - OnChanging:           Occurs immediately before the currently
  158. //                         selected control changes.
  159. }
  160.   TfcButtonGroupClickStyle = (bcsCheckList, bcsRadioGroup, bcsClick);
  161.   TfcCustomButtonGroup = class(TfcCustomTransparentPanel)
  162.   private
  163.     // Property storage variables
  164.     FAutoBold: Boolean;
  165.     FClickStyle: TfcButtonGroupClickStyle;
  166.     FControlSpacing: Integer;
  167.     FColumns: Integer;
  168.     FLayout: TfcLayout;
  169.     FMaxControlSize: Integer;
  170.     FOldSelected: TfcButtonGroupItem;
  171.     FOnChange: TfcButtonGroupChangeEvent;
  172.     FOnChanging: TfcButtonGroupChangeEvent;
  173.     FButtonClass: TfcCustomBitBtnClass;
  174.     FLastButtonRect: TRect;
  175.     FChangeLink: TfcChangeLink;
  176.     FShowDownAsUp:boolean;
  177.     FDisableThemes: boolean;
  178.     // Property Access Methods
  179.     function GetButton(Name: string): TfcCustomBitBtn;
  180.     function GetSelected: TfcButtonGroupItem; virtual;
  181.     procedure SetAutoBold(Value: Boolean);
  182.     procedure SetClickStyle(Value: TfcButtonGroupClickStyle);
  183.     procedure SetControlSpacing(Value: Integer);
  184.     procedure SetColumns(Value: Integer);
  185.     procedure SetItems(Value: TfcButtonGroupItems);
  186.     procedure SetLastButtonRect(Value: TRect);
  187.     procedure SetLayout(Value: TfcLayout);
  188.     procedure SetSelected(Value: TfcButtonGroupItem);
  189.     // Message Handlers
  190.     {$ifdef fcDelphi4Up}
  191.     procedure CMBorderChanged(var Message: TMessage); message CM_BORDERCHANGED;
  192.     {$endif}
  193.     procedure CMControlChange(var Message: TCMControlChange); message CM_CONTROLCHANGE;
  194.     procedure CMControlListChange(var Message: TCMControlListChange); message CM_CONTROLLISTCHANGE;
  195.     procedure WMGetDlgCode(var Message: TWMGetDlgCode); message WM_GETDLGCODE;
  196.     procedure SetShowDownAsUp(Value: boolean);
  197.   protected
  198.     SuspendNotification: Boolean;  // Flag to prevent access violations on notification method
  199.     FItems: TfcButtonGroupItems;
  200.     function GetCollectionClass: TfcButtonGroupItemsClass; virtual;
  201.     function ResizeToControl(Control: TControl; DoResize: Boolean): TSize; virtual;
  202.     procedure ButtonPressed(Sender: TObject); virtual;
  203.     procedure ButtonPressing(Sender: TObject); virtual;
  204.     procedure MouseMoveInLoop(Sender: TObject); virtual;
  205.     procedure DoChanging(OldSelected, Selected: TfcButtonGroupItem); virtual;
  206.     procedure DoChange(OldSelected, Selected: TfcButtonGroupItem); virtual;
  207.     // Overridden methods
  208.     function GetChildOwner: TComponent; override;
  209.     procedure AlignControls(AControl: TControl; var Rect: TRect); override;
  210.     procedure KeyDown(var Key: Word; Shift: TShiftState); override;
  211.     procedure Loaded; override;
  212.     procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  213.     procedure Paint; override;
  214.     procedure Resize; override;
  215.     procedure WriteState(Writer: TWriter); override;
  216.     // Virtual Property access methods
  217.     procedure SetName(const NewName: TComponentName); override;
  218.     function GetButtonClassName: string; virtual;
  219.     procedure SetButtonClass(Value: TfcCustomBitBtnClass); virtual;
  220.     procedure SetButtonClassName(Value: string); virtual;
  221.     procedure SetMaxControlSize(Value: Integer); virtual;
  222.     procedure UpdateBold(AAutoBold: Boolean); virtual;
  223.     procedure DefineProperties(Filer: TFiler);override;
  224.     function IsTransparent: boolean; override;
  225.     procedure WndProc(var Message: TMessage); override;
  226.     property ButtonClass: TfcCustomBitBtnClass read FButtonClass write SetButtonClass;
  227.     property OldSelected: TfcButtonGroupItem read FOldSelected;
  228.   public
  229.     ButtonGroupPatch: Variant;
  230.     constructor Create(Owner: TComponent); override;
  231.     destructor Destroy; override;
  232. //    function ControlSelected: Boolean; virtual;
  233.     property AutoBold: Boolean read FAutoBold write SetAutoBold;
  234.     property Buttons[Name: string]: TfcCustomBitBtn read GetButton;
  235.     property ClickStyle: TfcButtonGroupClickStyle read FClickStyle write SetClickStyle;
  236.     property ControlSpacing: Integer read FControlSpacing write SetControlSpacing;
  237.     property Columns: Integer read FColumns write SetColumns;
  238.     property ButtonItems: TfcButtonGroupItems read FItems write SetItems stored False;
  239.     property Layout: TfcLayout read FLayout write SetLayout;
  240.     property MaxControlSize: Integer read FMaxControlSize write SetMaxControlSize;
  241.     property ShowDownAsUp: boolean read FShowDownAsUp write SetShowDownAsUp default False;
  242.     property Selected: TfcButtonGroupItem read GetSelected write SetSelected;
  243.     property OnChange: TfcButtonGroupChangeEvent read FOnChange write FOnChange;
  244.     property OnChanging: TfcButtonGroupChangeEvent read FOnChanging write FOnChanging;
  245.     property ButtonClassName: string read GetButtonClassName write SetButtonClassName;
  246.     property LastButtonRect: TRect write SetLastButtonRect;
  247.     property DisableThemes : boolean read FDisableThemes write FDisableThemes default False;
  248.   end;
  249.   TfcButtonGroup = class(TfcCustomButtonGroup)
  250.   published
  251.     property DisableThemes;
  252.     {$ifdef fcDelphi4Up}
  253.     property Anchors;
  254.     property Constraints;
  255.     {$endif}
  256.     property Align;
  257.     property AutoBold;
  258.     property BevelInner;
  259.     property BevelOuter;
  260.     property BorderStyle;
  261.     property BorderWidth;
  262.     property ButtonClassName;
  263.     property ClickStyle;
  264.     property ControlSpacing;
  265.     property Columns;
  266.     property Color;
  267.     property Font;
  268.     property ParentFont;
  269.     property ButtonItems;
  270.     property Layout;
  271.     property MaxControlSize;
  272.     property PopupMenu;
  273.     property ShowDownAsUp;
  274.     property TabOrder;
  275.     property TabStop default True;
  276.     property Transparent;
  277.     property Visible;
  278.     property OnChange;
  279.     property OnChanging;
  280.     property OnClick;
  281.     property OnEnter;
  282.     property OnExit;
  283.     property OnKeyDown;
  284.     property OnKeyUp;
  285.     property OnKeyPress;
  286.     property OnMouseDown;
  287.     property OnMouseMove;
  288.     property OnMouseUp;
  289.     property OnResize;
  290.   end;
  291. implementation
  292. //{$ifdef fcDelphi4Up}
  293. //type TFormDesigner = IFormDesigner;
  294. //{$endif}
  295. // TfcButtonGroupItem
  296. constructor TfcButtonGroupItem.Create(Collection: TCollection);
  297. begin
  298.   inherited Create(Collection);
  299.   if not (csLoading in ButtonGroup.ComponentState) then
  300.   begin
  301.     ButtonGroup.FItems.ArrangingControls := True;
  302.     FButton := ButtonGroup.ButtonClass.Create(ButtonGroup.Owner);
  303.     FButton.Top := ButtonGroup.Height;
  304.     FButton.Parent := ButtonGroup;
  305.     FButton.ShowDownAsUp := ButtonGroup.ShowDownAsUp;
  306.     SetGroupIndex(1);
  307.     ButtonGroup.FItems.ArrangingControls := False;
  308.   end
  309.   else begin
  310.     if Button<>nil then Button.ShowDownAsUp := ButtonGroup.ShowDownAsUp;
  311.   end;
  312. end;
  313. destructor TfcButtonGroupItem.Destroy;
  314. begin
  315.   ButtonGroup.SuspendNotification := True;
  316.   ButtonGroup.FItems.DeletingControl := True;
  317.   FButton.Free;
  318.   ButtonGroup.FItems.DeletingControl := False;
  319.   ButtonGroup.SuspendNotification := False;
  320.   inherited;
  321. end;
  322. function TfcButtonGroupItem.GetButtonGroup: TfcCustomButtonGroup;
  323. begin
  324.   result := TfcButtonGroupItems(Collection).ButtonGroup;
  325. end;
  326. function TfcButtonGroupItem.GetGroupIndex: Integer;
  327. begin
  328.   result := Button.GroupIndex;
  329. end;
  330. function TfcButtonGroupItem.GetSelected: Boolean;
  331. begin
  332.   if Button=nil then result:= false  { Delphi 5 calls GetActivePage before button is initialized }
  333.   else result := Button.Selected;
  334. end;
  335. procedure TfcButtonGroupItem.SetGroupIndex(Value: Integer);
  336. begin
  337.   Button.GroupIndex := Value;
  338. end;
  339. procedure TfcButtonGroupItem.SetSelected(Value: Boolean);
  340. //var ParForm: TCustomForm;
  341. begin
  342.   Button.Down := Value;
  343.   ButtonGroup.FItems.ArrangeControls;
  344. {  if csDesigning in ButtonGroup.ComponentState then
  345.   begin
  346.     ParForm := GetParentForm(ButtonGroup);
  347.     if (ParForm <> nil) and (ParForm.Designer <> nil) then
  348.       ParForm.Designer.Modified;
  349.   end;}
  350. end;
  351. procedure TfcButtonGroupItem.Loaded;
  352. begin
  353.   FButton.OnSetName := SetButtonName;
  354.   FButton.ShowDownAsUp := ButtonGroup.ShowDownAsUp;
  355. end;
  356. procedure TfcButtonGroupItem.SetIndex(Value: Integer);
  357. begin
  358.   inherited;
  359. //  ShowMessage(Button.Name + '|' + InttoStr(Value));
  360.   if not (csLoading in ButtonGroup.ComponentState) then ButtonGroup.SetChildOrder(Button, Value);
  361. end;
  362. function TfcButtonGroupItem.GetDisplayName: string;
  363. begin
  364.   if Button <> nil then result := Button.Name;
  365. end;
  366. function TfcButtonGroupItem.GetInstance(const PropertyName: string): TPersistent;
  367. begin
  368.   result := Button;
  369. end;
  370. // TfcButtonGroupItems
  371. constructor TfcButtonGroupItems.Create(AButtonGroup: TfcCustomButtonGroup; ACollectionItemClass: TfcButtonGroupItemClass);
  372. begin
  373.   inherited Create(ACollectionItemClass);
  374.   FButtonGroup := AButtonGroup;
  375. end;
  376. function TfcButtonGroupItems.GetOwner: TPersistent;
  377. begin
  378.   result := FButtonGroup;
  379. end;
  380. procedure TfcButtonGroupItems.Update(Item: TCollectionItem);
  381. begin
  382.   inherited;
  383.   ArrangeControls;
  384. end;
  385. function TfcButtonGroupItems.GetButtonGroup: TfcCustomButtonGroup;
  386. begin
  387.   result := FButtonGroup;
  388. end;
  389. function TfcButtonGroupItems.GetItems(Index: Integer): TfcButtonGroupItem;
  390. begin
  391.   result := TfcButtonGroupItem(inherited Items[Index]);
  392. end;
  393. function TfcButtonGroupItems.GetVisibleCount: Integer;
  394. var i: Integer;
  395. begin
  396.   result := 0;
  397.   for i := 0 to Count - 1 do if Items[i].Button.Visible or (csDesigning in ButtonGroup.ComponentState) then inc(result);
  398. end;
  399. function TfcButtonGroupItems.GetVisibleItems(Index: Integer): TfcButtonGroupItem;
  400. var i: Integer;
  401.     CurIndex: Integer;
  402. begin
  403.   result := nil;
  404.   CurIndex := 0;
  405.   for i := 0 to Count - 1 do if Items[i].Button.Visible or (csDesigning in ButtonGroup.ComponentState) then
  406.   begin
  407.     if Index = CurIndex then
  408.     begin
  409.       result := Items[i];
  410.       Break;
  411.     end;
  412.     inc(CurIndex);
  413.   end;
  414. end;
  415. function TfcButtonGroupItems.Add: TfcButtonGroupItem;
  416.   function GetHighestGroupIndex: Integer;
  417.   var i: Integer;
  418.   begin
  419.     result := 0;
  420.     for i := 0 to Count - 1 do
  421.       if Items[i].GroupIndex > result then result := Items[i].GroupIndex;
  422.   end;
  423. begin
  424.   AddingControls := True;
  425.   result := TfcButtonGroupItem(inherited Add);
  426.   if (result.Button <> nil) then
  427.   begin
  428.     if (csDesigning in ButtonGroup.ComponentState) then
  429.       result.Button.Name := fcGenerateName(result.Button.Owner, ButtonGroup.Name + Copy(ButtonGroup.ButtonClassName, 2, Length(ButtonGroup.ButtonClassName)));
  430.     if Count > 1 then result.Button.Assign(Items[0].Button);
  431.     if ButtonGroup.ClickStyle = bcsCheckList then
  432.     begin
  433.       result.Button.GroupIndex := GetHighestGroupIndex + 1;
  434.       result.Button.AllowAllUp := True;
  435.     end;
  436.   end;
  437.   AddingControls := False;
  438.   ArrangeControls;
  439. end;
  440. function TfcButtonGroupItems.AddItem: TfcCollectionItem;
  441. begin
  442.   result := Add;
  443. end;
  444. procedure TfcButtonGroupItems.Clear;
  445. var i: Integer;
  446. begin
  447.   for i := Count - 1 downto 0 do
  448.     Items[i].Free;
  449. end;
  450. procedure TfcButtonGroupItems.ArrangeControls;
  451. var i: Integer;
  452.     Rows, Cols: Integer;
  453.     BtnWidth, BtnHeight: Integer;
  454.     RemainHeight, RemainWidth, PadHeight, PadWidth: Integer;
  455.     ExtraRows, ExtraCols: Integer;
  456.     ButtonGroupWidth, ButtonGroupHeight: Integer;
  457.     NewLeft, NewTop, NewWidth, NewHeight: Integer;
  458.     BtnSpacing: Integer;
  459.     OldTransparent: Boolean;
  460.   function Coord: TPoint;
  461.   begin
  462.     if ButtonGroup.Layout = loVertical then
  463.     begin
  464.       // Caculate Column
  465.       if i <= ExtraRows * (Rows + 1) then
  466.         result.x := (i - 1) div (Rows + 1)
  467.       else
  468.         result.x := ExtraRows +
  469.           (((i - 1) - (ExtraRows * (Rows + 1))) div Rows);
  470.       // Calculate Row
  471.       if i <= ExtraRows * (Rows + 1) then
  472.         result.y := (i - 1) mod (Rows + 1)
  473.       else
  474.         result.y := ((i - 1) - (ExtraRows * (Rows + 1))) mod Rows;
  475.     end else begin
  476.       result := Point(
  477.         (i - 1) div Rows,
  478.         (i - 1) mod Rows)
  479.     end;
  480.   end;
  481.   function ControlAtCoord(x, y: Integer): TControl;
  482.   var Index, i: Integer;
  483.   begin
  484.     if ButtonGroup.Layout = loVertical then
  485.     begin
  486.       if x < ExtraRows then
  487.         Index := (x * (Rows + 1)) + (y mod (Rows + 1))
  488.       else
  489.         Index := ((ExtraRows * (Rows + 1)) + ((x - ExtraRows) * Rows)) + (y mod Rows);
  490.       // 9/5/00 - Index based on visible buttons only
  491.       for i:= 0 to Index do
  492.       begin
  493.          if not Items[i].Button.visible then Index:= Index+1;
  494.       end;
  495.       result:= Items[Index].Button;
  496.     end else begin
  497.       result := Items[x * Rows + y].Button;
  498.     end;
  499.   end;
  500.   function GetRows: Integer;
  501.   begin
  502.     result := Rows + ord(ExtraRows > 0);
  503.   end;
  504.   function GetCols: Integer;
  505.   begin
  506.     result := Cols + ord(ExtraCols > 0);
  507.   end;
  508. begin
  509.   if (Count = 0) or (ArrangingControls) or AddingControls then Exit;
  510.   ArrangingControls := True;
  511.   OldTransparent := ButtonGroup.FTransparent;
  512.   ButtonGroup.FTransparent := False;
  513.   BtnSpacing := ButtonGroup.ControlSpacing;
  514.   PadHeight := 0; RemainHeight := 0;
  515.   PadWidth := 0; RemainWidth := 0;
  516.   Rows := Count div ButtonGroup.Columns;
  517.   Cols := ButtonGroup.Columns;
  518.   ExtraRows := Count mod ButtonGroup.Columns;
  519.   ExtraCols := 0;
  520.   if ButtonGroup.Layout <> loVertical then
  521.     with Rect(Rows, Cols, ExtraRows, ExtraCols) do
  522.     begin
  523.       Rows := Top;
  524.       Cols := Left;
  525.       ExtraRows := Bottom;
  526.       ExtraCols := Right;
  527.     end;
  528.   ButtonGroupWidth := fcRectWidth(ButtonGroup.ClientRect) - 2 * ButtonGroup.BorderWidth;
  529.   ButtonGroupHeight := fcRectHeight(ButtonGroup.ClientRect) - 2 * ButtonGroup.BorderWidth;
  530.   BtnWidth := (ButtonGroupWidth - (BtnSpacing * (GetCols - 1))) div GetCols;
  531.   BtnHeight := (ButtonGroupHeight - (BtnSpacing * (GetRows - 1))) div GetRows;
  532.   if ButtonGroup.MaxControlSize > 0 then
  533.   begin
  534.     if ButtonGroup.Layout = loVertical then
  535.       BtnHeight := fcMin(BtnHeight, ButtonGroup.MaxControlSize)
  536.     else BtnWidth := fcMin(BtnWidth, ButtonGroup.MaxControlSize);
  537.   end;
  538.   // 3/24/2000 - PYW - Need to check both Horizontal and Vertical
  539.   if ((ButtonGroup.Layout=loHorizontal) and (BtnWidth <> ButtonGroup.MaxControlSize)) or
  540.      ((ButtonGroup.Layout=loVertical) and (BtnHeight <> ButtonGroup.MaxControlSize)) then begin
  541.     RemainHeight := ButtonGroupHeight - ((GetRows * BtnHeight) + ((GetRows - 1) * BtnSpacing));
  542.     PadHeight := Ceil(RemainHeight / GetRows);
  543.     RemainWidth := ButtonGroupWidth - ((GetCols * BtnWidth) + ((GetCols - 1) * BtnSpacing));
  544.     PadWidth := Ceil(RemainWidth / GetCols);
  545.   end;
  546.   for i := 1 to VisibleCount do with VisibleItems[i - 1].Button do
  547.   begin
  548.     with Coord do
  549.     begin
  550.       // Calc Left
  551.       if (x = 0) and (y = 0) then NewLeft := ButtonGroup.BorderWidth
  552.       else if y = 0 then NewLeft := ControlAtCoord(x - 1, y).BoundsRect.Right + BtnSpacing
  553.       else NewLeft := ControlAtCoord(x, y - 1).Left;
  554.       // Calc Top
  555.       if y = 0 then NewTop := ButtonGroup.BorderWidth
  556.       else NewTop := ControlAtCoord(x, y - 1).BoundsRect.Bottom + BtnSpacing;
  557.       // Calc Width
  558.       if y = 0 then
  559.       begin
  560.         NewWidth := BtnWidth + fcMin(PadWidth, RemainWidth);
  561.         dec(RemainWidth, PadWidth);
  562.         if RemainWidth < 0 then RemainWidth := 0;
  563.       end else NewWidth := ControlAtCoord(x, y - 1).Width;
  564.       // Calc Height
  565.       if x = 0 then
  566.       begin
  567.         NewHeight := BtnHeight + fcMin(PadHeight, RemainHeight);
  568.         dec(RemainHeight, PadHeight);
  569.         if RemainHeight < 0 then RemainHeight := 0;
  570.       end else NewHeight := ControlAtCoord(x - 1, y).Height;
  571.       SetBounds(NewLeft, NewTop, NewWidth, NewHeight)
  572. //      SetWindowPos(Handle, 0, NewLeft, NewTop, NewWidth, NewHeight, SWP_NOREDRAW or SWP_NOZORDER);
  573.     end;
  574.   end;
  575. //  ButtonGroup.Invalidate;
  576.   ButtonGroup.FTransparent := OldTransparent;
  577.   ArrangingControls := False;
  578. end;
  579. function TfcButtonGroupItems.FindButton(AButton: TfcCustomBitBtn): TfcButtonGroupItem;
  580. var i: Integer;
  581. begin
  582.   result := nil;
  583.   for i := 0 to Count - 1 do
  584.     if Items[i].Button = AButton then
  585.     begin
  586.       result := Items[i];
  587.       Break;
  588.     end;
  589. end;
  590. function TfcButtonGroupItems.FindPointerTag(APointerTag: Pointer): TfcButtonGroupItem;
  591. var i: Integer;
  592. begin
  593.   result := nil;
  594.   for i := 0 to Count - 1 do
  595.     if Items[i].PointerTag = APointerTag then
  596.     begin
  597.       result := Items[i];
  598.       Break;
  599.     end;
  600. end;
  601. // TfcCustomButtonGroup
  602. constructor TfcCustomButtonGroup.Create(Owner: TComponent);
  603. begin
  604.   inherited;
  605.   FButtonClass := TfcShapeBtn;
  606.   FAutoBold := True;
  607.   FItems := GetCollectionClass.Create(self, TfcButtonGroupItem);
  608.   FItems.ArrangingControls := True;
  609.   FClickStyle := bcsRadioGroup;
  610.   FColumns := 1;
  611.   FControlSpacing := 1;
  612.   FChangeLink := TfcChangeLink.Create;
  613.   FChangeLink.OnChange := ButtonPressed;
  614.   FChangeLink.OnChanging := ButtonPressing;
  615.   BevelInner := bvNone;
  616.   BevelOuter := bvNone;
  617.   ControlStyle := ControlStyle - [csSetCaption{, csAcceptsControls}];
  618.   Height := 100;
  619.   TabStop := True;
  620.   Transparent := False;
  621.   Width := 75;
  622. end;
  623. destructor TfcCustomButtonGroup.Destroy;
  624. begin
  625.   FItems.ArrangingControls := True;
  626.   FChangeLink.Free;
  627.   FItems.Free;
  628.   FItems := nil;
  629.   inherited;
  630. end;
  631. {function TfcCustomButtonGroup.ControlSelected: Boolean;
  632. var i: Integer;
  633.     Selections: TComponentList;
  634. begin
  635.   result := False;
  636.   Selections := TComponentList.Create;
  637.   TFormDesigner(GetParentForm(self).Designer).GetSelections(Selections);
  638.   for i := 0 to Selections.Count - 1 do
  639.     if (Selections[i] is ButtonClass) and
  640.        ((Selections[i] as ButtonClass).Parent = self) then
  641.     begin
  642.       result := True;
  643.       Exit;
  644.     end;
  645. end;
  646. }
  647. procedure TfcCustomButtonGroup.UpdateBold(AAutoBold: Boolean);
  648. var i: Integer;
  649. begin
  650.   if not AAutoBold then Exit;
  651.   for i := 0 to FItems.Count - 1 do with FItems[i].Button.Font do
  652.     Style := Style - [fsBold];
  653.   if (Selected <> nil) and AutoBold and (ClickStyle = bcsRadioGroup) then with Selected.Button.Font do
  654.     Style := Style + [fsBold];
  655. end;
  656. {$ifdef fcDelphi4Up}
  657. procedure TfcCustomButtonGroup.CMBorderChanged(var Message: TMessage);
  658. begin
  659.   inherited;
  660.   FItems.ArrangeControls;
  661. end;
  662. {$endif}
  663. procedure TfcCustomButtonGroup.CMControlListChange(var Message: TCMControlListChange);
  664. var i: Integer;
  665. begin
  666.   if (Message.Control is TfcCustomBitBtn) then with (Message.Control as TfcCustomBitBtn) do
  667.   begin
  668.     if Message.Inserting then RegisterChanges(FChangeLink)
  669.     else UnRegisterChanges(FChangeLink);
  670.   end;
  671.   if (FItems <> nil) and not SuspendNotification and not (Message.Inserting) then
  672.     for i := 0 to FItems.Count - 1 do
  673.       if FItems[i].Button = Message.Control then
  674.       begin
  675.         FItems.DeletingControl := True;
  676.         FItems[i].FButton := nil;  // Prevent access violations, otherwise random memory would be attempted to be freed in the destructor of the item
  677.         FItems[i].Free;
  678.         FItems.DeletingControl := False;
  679.         Invalidate;
  680.         Break;
  681.       end;
  682.   inherited;
  683. end;
  684. procedure TfcCustomButtonGroup.CMControlChange(var Message: TCMControlChange);
  685. var curItem: TfcButtonGroupItem;
  686. begin
  687.   inherited;
  688.   if Message.Inserting and (Message.Control is TfcCustomBitBtn) and
  689.      (csLoading in ComponentState) and not FItems.AddingControls then
  690.   begin
  691.      curItem:= ButtonItems.Add;
  692.      with curItem do begin
  693.        FItems.AddingControls := True;
  694.        FButton := Message.Control as TfcCustomBitBtn;
  695.        FItems.AddingControls := False;
  696.      end
  697.   end;
  698. end;
  699. procedure TfcCustomButtonGroup.WMGetDlgCode(var Message: TWMGetDlgCode);
  700. begin
  701.   Message.result := DLGC_WANTARROWS;
  702. end;
  703. function TfcCustomButtonGroup.GetCollectionClass: TfcButtonGroupItemsClass;
  704. begin
  705.   result := TfcButtonGroupItems;
  706. end;
  707. procedure TfcCustomButtonGroup.ButtonPressing(Sender: TObject);
  708.   function CalcSelected: TfcButtonGroupItem;
  709.   var i: Integer;
  710.   begin
  711.     result := FOldSelected;
  712.     for i := 0 to FItems.Count - 1 do
  713.       if FItems[i].Selected then
  714.       begin
  715.         result := FItems[i];
  716.         Break;
  717.       end;
  718.   end;
  719. begin
  720.   if not (csLoading in ComponentState) then
  721.   begin
  722.     FOldSelected := CalcSelected;
  723.     DoChanging(FOldSelected, FItems.FindButton(Sender as TfcCustomBitBtn));
  724.   end;
  725. end;
  726. procedure TfcCustomButtonGroup.MouseMoveInLoop(Sender: TObject);
  727. var i: Integer;
  728. begin
  729.   for i := 0 to FItems.Count - 1 do
  730.     if FItems[i].Button <> Sender then Perform(CM_MOUSELEAVE, 0, 0);
  731. end;
  732. procedure TfcCustomButtonGroup.ButtonPressed(Sender: TObject);
  733. var NewSelected: TfcButtonGroupItem;
  734. begin
  735.   if not (csLoading in ComponentState) then
  736.   begin
  737.     NewSelected := FItems.FindButton(Sender as TfcCustomBitBtn);
  738.     if (Sender as TfcCustombitBtn).Selected then
  739.     begin
  740.       FItems.ArrangeControls;
  741.       UpdateBold(AutoBold);
  742.       if FOldSelected <> NewSelected then DoChange(FOldSelected, NewSelected);
  743.     end;
  744.   end;
  745. end;
  746. procedure TfcCustomButtonGroup.DoChanging(OldSelected, Selected: TfcButtonGroupItem);
  747. begin
  748.   if Assigned(FOnChanging) then FOnChanging(self, OldSelected, Selected);
  749. end;
  750. procedure TfcCustomButtonGroup.DoChange(OldSelected, Selected: TfcButtonGroupItem);
  751. begin
  752.   if Assigned(FOnChange) then FOnChange(self, OldSelected, Selected);
  753. end;
  754. function TfcCustomButtonGroup.ResizeToControl(Control: TControl; DoResize: Boolean): TSize;
  755. begin
  756.   if FItems.Count = 0 then Exit;
  757.   if Control = nil then Control := FItems[0].Button;
  758.   if Control = nil then Exit;
  759.   if Layout = loVertical then
  760.   begin
  761.     result.cx := ((Control.Width + ControlSpacing) * Columns);
  762.     result.cy := (Control.Height + ControlSpacing) * Ceil(FItems.Count/Columns); { 10/24/99 - RSW - Changed div to / }
  763.   end else begin
  764.     result.cx := (Control.Width + ControlSpacing) * Ceil(FItems.Count/Columns);
  765.     result.cy := ((Control.Height + ControlSpacing) * Columns);
  766.   end;
  767.   dec(result.cx, ControlSpacing);
  768.   dec(result.cy, ControlSpacing);
  769.   if DoResize then
  770.   begin
  771.     Width := result.cx;
  772.     Height := result.cy;
  773.   end;
  774. end;
  775. function TfcCustomButtonGroup.GetButtonClassName: string;
  776. begin
  777.   result := ButtonClass.ClassName;
  778. end;
  779. function TfcCustomButtonGroup.GetSelected: TfcButtonGroupItem;
  780. var i: Integer;
  781. begin
  782.   result := nil;
  783.   for i := 0 to FItems.Count - 1 do
  784.     if FItems[i].Selected then
  785.     begin
  786.       result := FItems[i];
  787.       break;
  788.     end;
  789. end;
  790. procedure TfcCustomButtonGroup.SetButtonClass(Value: TfcCustomBitBtnClass);
  791. begin
  792.   if FButtonClass <> Value then
  793.   begin
  794.     FButtonClass := Value;
  795.     if not (csLoading in ComponentState) then
  796.     begin
  797.       FItems.BeginUpdate;
  798.       FItems.Clear;
  799.       FItems.EndUpdate;
  800.     end;
  801.     if FItems.Designer <> nil then FItems.Designer.Update;
  802.   end;
  803. end;
  804. procedure TfcCustomButtonGroup.SetButtonClassName(Value: string);
  805. begin
  806.   if (csLoading in ComponentState) or not (csDesigning in ComponentState) or
  807.      (FItems.Count = 0) or
  808.      ((FItems.Count > 0) and (MessageDlg('Setting this property will clear your items. Continue?',
  809.      mtConfirmation, [mbYes, mbNo], 0) = mrYes)) then
  810.     ButtonClass := TfcCustomBitBtnClass(FindClass(Value));
  811. end;
  812. procedure TfcCustomButtonGroup.SetMaxControlSize(Value: Integer);
  813. begin
  814.   if FMaxControlSize <> Value then
  815.   begin
  816.     FMaxControlSize := Value;
  817.     FItems.ArrangeControls;
  818.   end;
  819. end;
  820. procedure TfcCustomButtonGroup.SetLastButtonRect(Value: TRect);
  821. begin
  822.   if not IsRectEmpty(FLastButtonRect) and (Parent <> nil) then
  823.   begin
  824.     OffsetRect(FLastButtonRect, Left, Top);
  825.     InflateRect(FLastButtonRect, 3, 3);
  826.     InvalidateRect(Parent.Handle, @FLastButtonRect, True);
  827.   end;
  828.   FLastButtonRect := Value;
  829. end;
  830. procedure TfcCustomButtonGroup.SetItems(Value: TfcButtonGroupItems);
  831. begin
  832.   FItems.Assign(Value);
  833. end;
  834. procedure TfcCustomButtonGroup.SetLayout(Value: TfcLayout);
  835. begin
  836.   if FLayout <> Value then
  837.   begin
  838.     FLayout := Value;
  839.     if not (csLoading in ComponentState) then
  840.       FItems.ArrangeControls;
  841.   end;
  842. end;
  843. procedure TfcCustomButtonGroup.SetShowDownAsUp(Value: boolean);
  844. var i:integer;
  845. begin
  846.    if FShowDownAsUp <> Value then
  847.    begin
  848.       FShowDownAsUp := Value;
  849.       for i:= 0 to Buttonitems.count-1 do begin
  850.          ButtonItems[i].Button.ShowDownAsUp := Value;
  851.       end;
  852.    end;
  853. end;
  854. procedure TfcCustomButtonGroup.SetSelected(Value: TfcButtonGroupItem);
  855. begin
  856.   if Value <> nil then Value.Selected := True;
  857. end;
  858. function TfcCustomButtonGroup.GetButton(Name: string): TfcCustomBitBtn;
  859. begin
  860.             //8/5/99- PYW- Check the owner for the button control.
  861.   result := Owner.FindComponent(Name) as TfcCustomBitBtn;
  862. end;
  863. procedure TfcCustomButtonGroup.SetAutoBold(Value: Boolean);
  864. begin
  865.   if FAutoBold <> Value then
  866.   begin
  867.     FAutoBold := Value;
  868.     UpdateBold(True);
  869.   end;
  870. end;
  871. procedure TfcCustomButtonGroup.SetClickStyle(Value: TfcButtonGroupClickStyle);
  872. var i: Integer;
  873. begin
  874.   if FClickStyle <> Value then
  875.   begin
  876.     FClickStyle := Value;
  877.     for i := 0 to FItems.Count - 1 do with FItems[i].Button do
  878.     begin
  879.       case FClickStyle of
  880.         bcsCheckList: begin
  881.           GroupIndex := i + 1;
  882.           AllowAllUp := True;
  883.         end;
  884.         bcsRadioGroup: begin
  885.           GroupIndex := 1;
  886.           AllowAllUp := False;
  887.         end;
  888.         bcsClick: begin
  889.           GroupIndex := 0;
  890.           AllowAllUp := False;
  891.         end;
  892.       end;
  893.     end;
  894. //    if (Items.Count > 0) and (FClickStyle = bcsRadioGroup) then Items[0].Selected := True;
  895.   end;
  896. end;
  897. procedure TfcCustomButtonGroup.SetControlSpacing(Value: Integer);
  898. begin
  899.   if FControlSpacing <> Value then
  900.   begin
  901.     FControlSpacing := Value;
  902.     FItems.ArrangeControls;
  903.   end;
  904. end;
  905. procedure TfcCustomButtonGroup.SetColumns(Value: Integer);
  906. begin
  907.   if FColumns <> Value then
  908.   begin
  909.     if Value < 0 then Exit;
  910.     FColumns := Value;
  911.     if not (csLoading in ComponentState) then
  912.       FItems.ArrangeControls;
  913.   end;
  914. end;
  915. procedure TfcCustomButtonGroup.SetName(const NewName: TComponentName);
  916. var i: Integer;
  917. begin
  918.   for i := 0 to FItems.Count - 1 do
  919.   begin
  920.     if (Copy(FItems[i].Button.Name, 1, Length(Name)) = Name) and (Name <> '') and (csDesigning in ComponentState) then
  921.       FItems[i].Button.Name := NewName + fcSubstring(FItems[i].Button.Name, Length(Name) + 1, 0);
  922.   end;
  923.   inherited;
  924. end;
  925. function TfcCustomButtonGroup.GetChildOwner: TComponent;
  926. begin
  927.   result := inherited GetChildOwner;
  928. end;
  929. procedure TfcCustomButtonGroup.AlignControls(AControl: TControl; var Rect: TRect);
  930. begin
  931.   inherited;
  932.   if FItems.DeletingControl then Exit;
  933.   if (not FItems.AddingControls) and (not FItems.ArrangingControls) and
  934.      not (csLoading in ComponentState) then
  935.     if (AControl <> nil) and (AControl is ButtonClass) then ResizeToControl(AControl, True);
  936.   if (not FItems.ArrangingControls) and not FItems.DeletingControl and (AControl is TfcCustomBitBtn) then
  937.     FItems.ArrangeControls;
  938. end;
  939. procedure TfcCustomButtonGroup.KeyDown(var Key: Word; Shift: TShiftState);
  940. var NextIndex: Integer;
  941. begin
  942.   inherited;
  943.   if Key in [VK_RIGHT, VK_DOWN, VK_LEFT, VK_UP, VK_HOME, VK_END] then
  944.   begin
  945.     NextIndex := 0;
  946.     if Selected <> nil then
  947.       case Key of
  948.         VK_RIGHT, VK_DOWN: NextIndex := Selected.Index + 1;
  949.         VK_LEFT, VK_UP: NextIndex := Selected.Index - 1;
  950.         VK_HOME: NextIndex := 0;
  951.         VK_END: NextIndex := FItems.Count - 1;
  952.       end;
  953.     if NextIndex < 0 then NextIndex := FItems.Count - 1
  954.     else if NextIndex >= FItems.Count then NextIndex := 0;
  955.     FItems[NextIndex].Selected := True;
  956.   end;
  957. end;
  958. procedure TfcCustomButtonGroup.Loaded;
  959. var i, j: Integer;
  960. begin
  961.   inherited;
  962.   for i := 0 to ControlCount - 1 do if Controls[i] is TWinControl then
  963.     if Controls[i] is ButtonClass then
  964.       for j := 0 to FItems.Count - 1 do begin
  965.         if FItems[j].Button = nil then
  966.         begin
  967.           FItems[j].FButton := Controls[i] as TfcCustomBitBtn;
  968.           Break;
  969.         end;
  970.         FItems[j].Button.ShowDownAsUp := ShowDownAsUp;
  971.       end;
  972.   FItems.ArrangingControls := False;
  973.   FItems.ArrangeControls; // Make sure controls are arranged in case it skipped arrangecontrols earlier
  974.   for i := 0 to FItems.Count - 1 do FItems[i].Loaded;
  975. end;
  976. procedure TfcCustomButtonGroup.Notification(AComponent: TComponent; Operation: TOperation);
  977. begin
  978.   inherited;
  979. end;
  980. procedure TfcCustomButtonGroup.Paint;
  981. var i: integer;
  982. begin
  983.   if (not Transparent) and fcUseThemes(self) then
  984.   begin
  985.     Canvas.Brush.Color:= Color;
  986.     Canvas.FillRect(ClientRect);
  987.   end;
  988.   inherited;
  989.   
  990.   { 5/13/99 - RSW - When transparent, paint should also paint the button area }
  991.   if IsTransparent then
  992.      for i:= 0 to ButtonItems.Count-1 do
  993.      begin
  994.         ButtonItems[i].Button.invalidate;
  995.      end;
  996.   if (csDesigning in ComponentState) and
  997.      (BorderStyle = bsNone) and (BevelInner = bvNone) and
  998.      (BevelOuter = bvNone) and (FItems.Count = 0) then with Canvas do
  999.   begin
  1000.     Pen.Color := clBlack;
  1001.     Pen.Style := psDash;
  1002.     if IsTransparent then Brush.Style := bsClear;
  1003.     Rectangle(0, 0, Width, Height);
  1004.   end;
  1005. end;
  1006. procedure TfcCustomButtonGroup.Resize;
  1007. begin
  1008.   inherited;
  1009.   if not (csDestroying in ComponentState) then FItems.ArrangeControls;
  1010. end;
  1011. procedure TfcCustomButtonGroup.DefineProperties(Filer: TFiler);
  1012. begin
  1013.   inherited DefineProperties(Filer);
  1014. //  Filer.DefineProperty('Item', nil, nil, True);
  1015. end;
  1016. procedure TfcCustomButtonGroup.WriteState(Writer: TWriter);
  1017. var i: Integer;
  1018. begin
  1019.   for i := FItems.Count - 1 downto 0 do SetChildOrder(FItems[i].Button, 0);
  1020.   inherited;
  1021. end;
  1022. Function TfcCustomButtonGroup.IsTransparent: boolean;
  1023. begin
  1024.    result:= (inherited IsTransparent)  and not (csDesigning in ComponentState);
  1025. end;
  1026. procedure TfcCustomButtonGroup.WndProc(var Message: TMessage);
  1027. begin
  1028.   inherited;
  1029. end;
  1030. initialization
  1031.   RegisterClasses([TfcShapeBtn, TfcImageBtn]);
  1032. end.