
{*******************************************************}
{                                                       }
{       RichViewActions                                 }
{       TRVOfficeRadioButton, TRVOfficeRadioGroup v1.2  }
{       Selectors like in Microsoft Office              }
{                                                       }
{       Copyright (c) 2002-2007, Sergey Tkachenko       }
{       svt@trichview.com                               }
{       http://www.trichview.com                        }
{                                                       }
{*******************************************************}

{==============================================================================}
{

 "Radio button" and group of buttons that look like selectors from MS Office
 (sunken white rectangle with image and optional text label below, for
  example for choosing bullets and numbering).
 Support XP themes.

}
{==============================================================================}

unit RVOfficeRadioBtn;

{

Changes:
  v1.4
  - Charset is uses even when drawing themed text
  v1.3
  - Support for ThemeEngine (www.ksdev.com) 3.5.9+
  v1.2
  - UseXPThemes: Boolean (default True) - use or not themes of WinXP (if available)
  - Compatibility with TThemeManager by Mike Lischke (www.lischke-online.de)
    (older version caused a crash of theme manager)
  v1.1:
  - Square: Boolean - property for TRVOfficeRadioButton and TRVOfficeRadioGroup.
    If True, width and height of sunken rectangle will be equal
  - Height of text area is scaled proportionally to screen's PixelsPerInch
  - Fix: word-wrapping did not work

  ================ TRVOfficeRadioButton ======================

TRVOfficeRadioButton has all properties of TRadioButton
(actually, it is inherited from TRadioButton).

It has additional properties:
* FillColor: TColor (default clWindow)
  Background color of sunken rectangle
* SelColor: TColor (default clHighlight)
  Color of rectangle for showing selected button
* SelWidth : Integer (default 2)
  Width of rectangle for showing selected button
* TextAreaHeight: Integer (default 30)
  Height (in pixels) of text area for displaying Caption.
  This area can be below or above the sunken rectangle.
  Set it to 0 to hide Caption.
  Caption can be wrapped.
* TextPosition: TORBTextPosition (default orbtpBottom)
  Defines the position of Caption (Bottom or Top)
* Disabled3D: Boolean (default True)
  Set it to False to draw disabled Caption without 3D effect,
  like in MS Office
* Images: TRVImageList;
  (TRVImageList is an alias for TCustomImageList)
  Link to ImageList for drawing images
* ImageIndex: TImageIndex (default -1)
  Index of image in Images.
* Square: Boolean (default false)
  If True, width and height of sunken rectangle will be equal
  
Additional event:
* OnCustomDraw: TORBCustomDrawEvent;
  TORBCustomDrawEvent = procedure (Sender: TRVOfficeRadioButton;
    Canvas: TCanvas; const ARect: TRect;
    var DoDefault: Boolean) of object;
  Allows to draw your own image instead of (or together with, if you do
  not set DoDefault to false) image from image list.
  Draw your image onto specified Canvas, in specified ARect.

  ================ TRVOfficeRadioGroup ======================

This component is similar to TRadioGroup, with the following exceptions:
- this is a group of TRVOfficeRadioButtons;
- Items property is not a TStringList, but TCollection
- this component allows to disable, set hint and helpcontext for individual buttons
- right/left key moves focus to the button to the right/left (in TRadioGroup,
  they work just like down/up keys)
- there is an event that occurs when double-clicking individual button.

TRVOfficeRadioGroup has all properties of TRadioGroup.

Additional properties:
* Items: TOfficeGroupItems
  Collection of objects defining properties of individual buttons
  (FillColor,ImageIndex,Enabled,Hint,HelpContext,HelpKeyword,HelpType,Caption)
* Images,SelColor,SelWidth,TextAreaHeight,Disabled3D,TextPosition,Square
  Assigned to the corresponding properties of all radiobuttons in the group.
* Margin
  Spacing between columns, and between borders and outermost radiobuttons.
  Spacing between rows = 0 - use TextAreaHeight instead.

Additional events:
* OnDblClickItem: TNotifyEvent
  Occurs when user double clicks some radiobutton.
* OnCustomDraw: TORGCustomDrawEvent
  TORGCustomDrawEvent = procedure (Sender: TRVOfficeRadioGroup;
    ItemIndex: Integer; Canvas: TCanvas; const ARect: TRect;
    var DoDefault: Boolean) of object;
  Custom drawing of the ItemIndex-th radiobutton
}

interface
{$I RV_Defs.inc}
{$I RichViewActions.inc}
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls,
  StdCtrls, ExtCtrls,
  {$IFDEF RICHVIEWDEF4}
  ImgList,
  {$ENDIF}
  {$IFDEF USERVKSDEVTE}
  te_theme,
  {$ENDIF}
  Forms, RVXPTheme, RVUni;

const
  CM_DENYSUBCLASSING = CM_BASE + 2000; // For compatibility with TThemeManager by Mike Lischke

type
  {$IFNDEF RICHVIEWDEF5}
  TImageIndex = Integer;
  {$ENDIF}

  {$IFDEF RICHVIEWDEF4}
  TRVImageList = TCustomImageList;
  {$ELSE}
  TRVImageList = TImageList;
  {$ENDIF}

  TRVOfficeRadioButton = class;
  TRVOfficeRadioGroup = class;

  TORBTextPosition = (orbtpBottom, orbtpTop);

  TORBCustomDrawEvent = procedure (Sender: TRVOfficeRadioButton;
    Canvas: TCanvas; const ARect: TRect;
    var DoDefault: Boolean) of object;
  TORGCustomDrawEvent = procedure (Sender: TRVOfficeRadioGroup;
    ItemIndex: Integer; Canvas: TCanvas; const ARect: TRect;
    var DoDefault: Boolean) of object;

  TRVOfficeRadioButton = class(TRadioButton)
    private
      FFillColor: TColor;
      FTextAreaHeight: Integer;
      FSelColor: TColor;
      FSelWidth: Integer;
      FDisabled3d: Boolean;
      FImageIndex: TImageIndex;
      FImageChangeLink: TChangeLink;
      FTextPosition: TORBTextPosition;
      FOnCustomDraw: TORBCustomDrawEvent;
      FImages: TRVImageList;
      FSquare: Boolean;
      FThemeEdit, FThemeRadio: HTheme;
      FUseXPThemes: Boolean;
      FLargeSelection: Boolean;
      procedure WMThemeChanged(var Message: TMessage); message WM_THEMECHANGED;
      {$IFDEF USERVKSDEVTE}
      procedure SNMThemeMessage(var Msg: TMessage); message SNM_THEMEMESSAGE;
      {$ENDIF}
      procedure CNDrawItem(var Msg: TWMDrawItem); message CN_DRAWITEM;
      procedure DrawTo(Canvas: TCanvas; const ARect: TRect; AFocused: Boolean);
      procedure CNCommand(var Msg: TWMCommand); message CN_COMMAND;
      procedure BMSetCheck(var Msg: TMessage); message BM_SETCHECK;
      procedure WMEraseBkgnd(var Msg: TWMEraseBkgnd); message WM_ERASEBKGND;
      procedure CMDenySubclassing(var Msg: TMessage); message CM_DENYSUBCLASSING;
      procedure SetFillColor(Value: TColor);
      procedure SetTextAreaHeight(const Value: Integer);
      procedure SetSelColor(const Value: TColor);
      procedure SetSelWidth(const Value: Integer);
      procedure SetDisabled3d(const Value: Boolean);
      procedure SetImages(const Value: TRVImageList);
      procedure SetImageIndex(const Value: TImageIndex);
      procedure ImageListChange(Sender: TObject);
      procedure SetTextPosition(Value: TORBTextPosition);
      function DoCustomDraw(Canvas: TCanvas; R: TRect): Boolean;
      procedure SetSquare(const Value: Boolean);
      procedure CreateThemeHandle;
      procedure FreeThemeHandle;
      procedure SetUseXPThemes(const Value: Boolean);
      procedure SetLargeSelection(const Value: Boolean);
    protected
      procedure CreateParams(var Params: TCreateParams); override;
      procedure Notification(AComponent: TComponent; Operation: TOperation); override;
      procedure CreateWnd; override;
      procedure DestroyWnd; override;
    public
      constructor Create(AOwner: TComponent); override;
      destructor Destroy; override;
    published
      property FillColor: TColor read FFillColor write SetFillColor default clWindow;
      property SelColor: TColor read FSelColor write SetSelColor default clHighlight;
      property SelWidth: Integer read FSelWidth write SetSelWidth default 2;
      property TextAreaHeight: Integer read FTextAreaHeight write SetTextAreaHeight default 30;
      property Disabled3D: Boolean read FDisabled3d write SetDisabled3d default True;
      property Images: TRVImageList read FImages write SetImages;
      property ImageIndex: TImageIndex read FImageIndex write SetImageIndex default -1;
      property Square: Boolean read FSquare write SetSquare default False;
      property LargeSelection: Boolean read FLargeSelection write SetLargeSelection default False;
      property TextPosition: TORBTextPosition read FTextPosition write SetTextPosition default orbtpBottom;
      property OnCustomDraw: TORBCustomDrawEvent read FOnCustomDraw write FOnCustomDraw;
      property UseXPThemes: Boolean read FUseXPThemes write SetUseXPThemes default True;
      property TabStop;
  end;

  TOfficeGroupItem = class (TCollectionItem)
    private
      FEnabled: Boolean;
      FHint: String;
      FFillColor: TColor;
      FHelpContext: THelpContext;
      {$IFDEF RICHVIEWDEF6}
      FHelpType: THelpType;
      FHelpKeyword: String;
      {$ENDIF}
      FImageIndex: TImageIndex;
      FCaption: TCaption;
      procedure SetEnabled(Value: Boolean);
      procedure SetFillColor(Value: TColor);
      procedure SetHelpContext(Value: THelpContext);
      {$IFDEF RICHVIEWDEF6}
      procedure SetHelpKeyword(Value: String);
      procedure SetHelpType(Value: THelpType);
      {$ENDIF}
      procedure SetHint(Value: String);
      procedure SetImageIndex(Value: TImageIndex);
      procedure SetCaption(const Value: TCaption);
    public
      constructor Create(Collection: TCollection); override;
      procedure AssignTo(Dest: TPersistent); override;
      procedure Assign(Source: TPersistent); override;
    published
      property FillColor: TColor read FFillColor write SetFillColor default clWindow;
      property ImageIndex: TImageIndex read FImageIndex write SetImageIndex default -1;
      property Enabled: Boolean read FEnabled write SetEnabled default True;
      property Hint: String read FHint write SetHint;
      property HelpContext: THelpContext read FHelpContext write SetHelpContext default 0;
      {$IFDEF RICHVIEWDEF6}
      property HelpKeyword: String read FHelpKeyword write SetHelpKeyword;
      property HelpType: THelpType read FHelpType write SetHelpType default htContext;
      {$ENDIF}
      property Caption: TCaption read FCaption write SetCaption stored True;
  end;

  TOfficeGroupItems = class(TCollection)
    private
      FRadioGroup: TRVOfficeRadioGroup;
      function GetItem(Index: Integer): TOfficeGroupItem;
      procedure SetItem(Index: Integer; Value: TOfficeGroupItem);
    protected
      function GetOwner: TPersistent; override;
      procedure Update(Item: TCollectionItem); override;
    public
      constructor Create(RadioGroup: TRVOfficeRadioGroup);
      function Add: TOfficeGroupItem;
      {$IFDEF RICHVIEWDEF4}
      function Insert(Index: Integer): TOfficeGroupItem;
      {$ENDIF}
      property Items[Index: Integer]: TOfficeGroupItem read GetItem write SetItem; default;
  end;

  TRVOfficeRadioGroup = class(TCustomGroupBox)
    private
      FButtons: TList;
      FItems: TOfficeGroupItems;
      FItemIndex: Integer;
      FColumns: Integer;
      FReading: Boolean;
      FUpdating: Boolean;
      FMargin: Integer;
      FImages: TRVImageList;
      FDisabled3d: Boolean;
      FSelWidth: Integer;
      FTextAreaHeight: Integer;
      FSelColor: TColor;
      FTextPosition: TORBTextPosition;
      FOnCustomDraw: TORGCustomDrawEvent;
      FOnDblClickItem: TNotifyEvent;
      FSquare: Boolean;
      FUseXPThemes: Boolean;
      FTheme: HTheme;
      procedure ArrangeButtons;
      procedure ButtonClick(Sender: TObject);
      procedure SetItems(Value: TOfficeGroupItems);
      procedure UpdateButtons;
      procedure UpdateButtonsGlbl(Repaint: Boolean);
      procedure UpdateButton(Index: Integer);
      procedure CMEnabledChanged(var Msg: TMessage); message CM_ENABLEDCHANGED;
      procedure CMFontChanged(var Msg: TMessage); message CM_FONTCHANGED;
      procedure WMSize(var Msg: TWMSize); message WM_SIZE;
      procedure WMEraseBkgnd(var Msg: TWMEraseBkgnd); message WM_ERASEBKGND;
      procedure WMPaint(var Msg: TWMPaint); message WM_PAINT;
      {$IFDEF USERVKSDEVTE}
      procedure SNMThemeMessage(var Msg: TMessage); message SNM_THEMEMESSAGE;
      {$ENDIF}
      procedure WMThemeChanged(var Msg: TMessage); message WM_THEMECHANGED;
      procedure WMSetFocus(var Msg: TMessage); message WM_SETFOCUS;
      procedure CMDenySubclassing(var Msg: TMessage); message CM_DENYSUBCLASSING;
      procedure SetMargin(Value: Integer);
      procedure SetButtonCount(Value: Integer);
      procedure SetColumns(Value: Integer);
      procedure SetItemIndex(Value: Integer);
      procedure SetImages(Value: TRVImageList);
      procedure SetDisabled3d(Value: Boolean);
      procedure SetSelColor(Value: TColor);
      procedure SetSelWidth(Value: Integer);
      procedure SetTextAreaHeight(Value: Integer);
      procedure SetTextPosition(Value: TORBTextPosition);
      procedure DoCustomDraw(Sender: TRVOfficeRadioButton; Canvas: TCanvas; const ARect: TRect;
                 var DoDefault: Boolean);
      procedure SetCustomDraw(Value: TORGCustomDrawEvent);
      function GetAnother(Index: Integer; Key: Word): Integer;
      procedure SetSquare(const Value: Boolean);
      procedure SetUseXPThemes(const Value: Boolean);
      procedure PerformEraseBackground(DC: HDC; FullPainting: Boolean);
      procedure CreateThemeHandle;
      procedure FreeThemeHandle;
      function GetThemeState: Integer;
      function GetCaptionRect(DC: THandle): TRect;
    protected
      procedure Loaded; override;
      procedure ReadState(Reader: TReader); override;
      function CanModify: Boolean; virtual;
      procedure Notification(AComponent: TComponent; Operation: TOperation); override;
      procedure CreateWnd; override;
      procedure DestroyWnd; override;
      procedure Paint; override;
    public
      constructor Create(AOwner: TComponent); override;
      destructor Destroy; override;
      {$IFDEF RICHVIEWDEF4}
      procedure FlipChildren(AllLevels: Boolean); override;
      {$ENDIF}
      procedure GetChildren(Proc: TGetChildProc; Root: TComponent); override;
    published
      property ItemIndex: Integer read FItemIndex write SetItemIndex default -1;
      property Columns: Integer read FColumns write SetColumns default 1;
      property Items: TOfficeGroupItems read FItems write SetItems;
      property Margin: Integer read FMargin write SetMargin default 10;
      property Images: TRVImageList read FImages write SetImages;
      property SelColor: TColor read FSelColor write SetSelColor default clHighlight;
      property SelWidth: Integer read FSelWidth write SetSelWidth default 2;
      property TextAreaHeight: Integer read FTextAreaHeight write SetTextAreaHeight default 30;
      property Disabled3D: Boolean read FDisabled3d write SetDisabled3d default True;
      property TextPosition: TORBTextPosition read FTextPosition write SetTextPosition default orbtpBottom;
      property Square: Boolean read FSquare write SetSquare default False;
      property OnCustomDraw: TORGCustomDrawEvent read FOnCustomDraw write SetCustomDraw;
      property OnDblClickItem: TNotifyEvent read FOnDblClickItem write FOnDblClickItem;
      property UseXPThemes: Boolean read FUseXPThemes write SetUseXPThemes default True;
      property Align;
      {$IFDEF RICHVIEWDEF4}
      property Anchors;
      property BiDiMode;
      property DragKind;
      property Constraints;
      property ParentBiDiMode;
      property OnEndDock;
      property OnStartDock;
      {$ENDIF}
      property Caption;
      property Color;
      property Ctl3D;
      property DragCursor;
      property DragMode;
      property Enabled;
      property Font;
      property ParentColor;
      property ParentCtl3D;
      property ParentFont;
      property ParentShowHint;
      property PopupMenu;
      property ShowHint;
      property TabOrder;
      property TabStop default True;
      property Visible;
      property OnClick;
      {$IFDEF RICHVIEWDEF5}
      property OnContextPopup;
      {$ENDIF}
      property OnDragDrop;
      property OnDragOver;
      property OnEndDrag;
      property OnEnter;
      property OnExit;
      property OnStartDrag;
  end;

implementation

{==============================================================================}

function _GetWideString(const s: String; Charset: TFontCharset): WideString;
begin
  {$IFDEF RVUNICODESTR}
  Result := s;
  {$ELSE}
  Result := RVU_RawUnicodeToWideString(RVU_AnsiToUnicode(RVU_Charset2CodePage(Charset), s));
  {$ENDIF}
end;

procedure AdjustValue(var Value: Integer; Min,Max: Integer);
begin
  if Value<Min then
    Value := Min;
  if Value>Max then
    Value := Max;
end;

{============================ TRVOfficeRadioButton ==============================}
constructor TRVOfficeRadioButton.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  Width := 100;
  Height := 100;
  FFillColor := clWindow;
  FSelColor := clHighlight;
  FSelWidth := 2;
  FTextAreaHeight := 30;
  FDisabled3D := True;
  FImageIndex := -1;
  FUseXPThemes := True;
  FImageChangeLink := TChangeLink.Create;
  FImageChangeLink.OnChange := ImageListChange;
  {$IFDEF USERVKSDEVTE}
  AddThemeNotification(Self);
  {$ENDIF}
  //DoubleBuffered := True;
end;
{------------------------------------------------------------------------------}
destructor TRVOfficeRadioButton.Destroy;
begin
  {$IFDEF USERVKSDEVTE}
  RemoveThemeNotification(Self);
  {$ENDIF}
  FImageChangeLink.Free;
  inherited;
end;
{------------------------------------------------------------------------------}
procedure TRVOfficeRadioButton.CreateParams(var Params: TCreateParams);
begin
  inherited CreateParams(Params);
  Params.Style:=(Params.Style or BS_OWNERDRAW or BS_NOTIFY or WS_GROUP) and not BS_RADIOBUTTON;
end;
{------------------------------------------------------------------------------}
procedure TRVOfficeRadioButton.CNCommand(var Msg: TWMCommand);
begin
  inherited;
  case Msg.NotifyCode of
    BN_SETFOCUS:
      begin
        Checked := True;
      end;
  end;
end;
{------------------------------------------------------------------------------}
procedure TRVOfficeRadioButton.BMSetCheck(var Msg: TMessage);
begin
  Invalidate;
end;
{------------------------------------------------------------------------------}
procedure TRVOfficeRadioButton.WMEraseBkgnd(var Msg: TWMEraseBkgnd);
begin
  Msg.Result := 1;
  {$IFDEF USERVKSDEVTE}
  DrawControlBackground(Self, Msg.DC);
  {$ENDIF}
  if (FThemeEdit=0) then
    exit;
  {$IFNDEF USERVKSDEVTE}
  RV_DrawThemeParentBackground(Handle, Msg.DC, nil);
  {$ENDIF}
end;
{------------------------------------------------------------------------------}
procedure TRVOfficeRadioButton.CMDenySubclassing(var Msg: TMessage);
begin
  Msg.Result := 1;
end;
{------------------------------------------------------------------------------}
function TRVOfficeRadioButton.DoCustomDraw(Canvas: TCanvas;
  R: TRect): Boolean;
begin
  Result := True;
  if Assigned(FOnCustomDraw) then begin
    InflateRect(R,-4-SelWidth, -4-SelWidth);
    FOnCustomDraw(Self, Canvas, R, Result);
  end;
end;
{------------------------------------------------------------------------------}
procedure TRVOfficeRadioButton.DrawTo(Canvas: TCanvas; const ARect: TRect; AFocused: Boolean);
var r: TRect;
    Flags: LongInt;
    y: Integer;
    TAH: Integer;
    Form: TCustomForm;
    State: Integer;
    Draw3dBorder: Boolean;
    {...............................................}
    procedure MakeSquare(var r: TRect);
    begin
      if R.Right-R.Left>R.Bottom-R.Top then begin
        R.Left := ((R.Right-R.Left)-(R.Bottom-R.Top)) div 2;
        R.Right := R.Left+(R.Bottom-R.Top);
        end
      else if R.Right-R.Left<R.Bottom-R.Top then begin
        R.Top := ((R.Bottom-R.Top)-(R.Right-R.Left)) div 2;
        R.Bottom := R.Top+(R.Right-R.Left);
      end;
    end;
    {...............................................}
    procedure GetImageRect(var R: TRect);
    begin
      R := ARect;
      if TextPosition=orbtpBottom then
        dec(R.Bottom, TAH)
      else
        inc(R.Top, TAH);
      if FSquare then
        MakeSquare(R);
    end;
    {...............................................}
    procedure GetTextRect(var R: TRect);
    begin
      R := ARect;
      if TextPosition=orbtpBottom then
        R.Top := R.Bottom-TAH
      else
        R.Bottom := TAH;
    end;
    {...............................................}
    procedure RedrawBackground(DC: HDC);
    var
      LastOrigin: TPoint;
    begin
      GetWindowOrgEx(DC, LastOrigin);
      SetWindowOrgEx(DC, LastOrigin.X + Left, LastOrigin.Y + Top, nil);
      Parent.Perform(WM_ERASEBKGND, Integer(DC), Integer(DC));
      Parent.Perform(WM_PAINT, Integer(DC), 0);
      SetWindowOrgEx(DC, LastOrigin.X, LastOrigin.Y, nil);
    end;
begin
  Draw3dBorder := True;
  TAH := TextAreaHeight;
  Form := GetParentForm(Self);
  if (Form<>nil) and (Form is TForm) then
    TAH := TAH * Screen.PixelsPerInch div TForm(Form).PixelsPerInch;
  // Background
  {$IFDEF USERVKSDEVTE}
  if IsThemeAvailable(CurrentTheme) then
  begin
    GetImageRect(R);
    DrawControlBackground(Self, Canvas.Handle);
    if CurrentTheme.IsEditDefined(kescEdit) then begin
      CurrentTheme.EditDraw(kescEdit, Canvas, EditInfo(R, kedsNormal));
      Draw3dBorder := False;
    end;
  end
  else
  begin
    with ARect do
      IntersectClipRect(Canvas.Handle, Left, Top, Right, Bottom);
    DrawControlBackground(Self, Canvas.Handle);
    SelectClipRgn(Canvas.Handle, 0);
    if (FThemeEdit=0) {or
       (RV_DrawThemeParentBackground(Handle, Canvas.Handle, @ARect)<>S_OK)} then begin
      Canvas.Brush.Color := Color;
      Canvas.Brush.Style := bsSolid;
      Canvas.FillRect(ARect);
    end;
    // Rectangle
    GetImageRect(R);
    if Enabled then
      Canvas.Brush.Color := FillColor
    else
      Canvas.Brush.Color := clBtnFace;
    Canvas.FillRect(R);
    if FThemeEdit<>0 then
    begin
      if not Enabled then
        State := ETS_DISABLED
      else if AFocused then
        State := ETS_FOCUSED
      else
        State := ETS_NORMAL;
      RV_DrawThemeBackground(FThemeEdit, Canvas.Handle, EP_EDITTEXT, State, R, nil);
      Draw3dBorder := False;
    end;
  end;
  {$ELSE}
  if (FThemeEdit<>0) then begin
    with ARect do
      IntersectClipRect(Canvas.Handle, Left, Top, Right, Bottom);
    RedrawBackground(Canvas.Handle);
    SelectClipRgn(Canvas.Handle, 0);
  end;
  if (FThemeEdit=0) {or
     (RV_DrawThemeParentBackground(Handle, Canvas.Handle, @ARect)<>S_OK)} then begin
    Canvas.Brush.Color := Color;
    Canvas.Brush.Style := bsSolid;
    Canvas.FillRect(ARect);
  end;
  // Rectangle
  GetImageRect(R);
  if Enabled then
    Canvas.Brush.Color := FillColor
  else
    Canvas.Brush.Color := clBtnFace;
  Canvas.FillRect(R);
  if FThemeEdit<>0 then
  begin
    if not Enabled then
      State := ETS_DISABLED
    else if AFocused then
      State := ETS_FOCUSED
    else
      State := ETS_NORMAL;
    RV_DrawThemeBackground(FThemeEdit, Canvas.Handle, EP_EDITTEXT, State, R, nil);
    Draw3dBorder := False;
  end;
  {$ENDIF}

  // Image
  if DoCustomDraw(Canvas, R) and (Images<>nil) and (ImageIndex>=0) and
     (ImageIndex<Images.Count) then begin
    R.Left := (R.Left+R.Right - Images.Width) div 2;
    R.Top := (R.Top+R.Bottom - Images.Height) div 2;
    Images.Draw(Canvas, R.Left, R.Top, ImageIndex
      {$IFDEF RICHVIEWDEF4}, Enabled{$ENDIF});
  end;
  // Focus
  GetImageRect(R);
  if Draw3dBorder then
    DrawEdge(Canvas.Handle,R,EDGE_SUNKEN,BF_RECT);
  if AFocused and not LargeSelection then begin
    InflateRect(r,-3,-3);
    Canvas.Font.Color := clBlack;
    Canvas.Brush.Style := bsSolid;
    Canvas.Brush.Color := FillColor;
    Canvas.DrawFocusRect(R);
    InflateRect(r,-1,-1);
    end
  else if not LargeSelection then
    InflateRect(r,-4,-4);
  // Selection
  if Checked then begin
    Canvas.Pen.Color := SelColor;
    Canvas.Pen.Style := psInsideFrame;
    Canvas.Pen.Width := FSelWidth;
    Canvas.Brush.Style := bsClear;
    with R do
      Canvas.Rectangle(Left,Top,Right,Bottom);
  end;
  if TextAreaHeight>0 then begin
    // Text Background
    GetTextRect(R);
    //Canvas.Brush.Color := Color;
    //Canvas.FillRect(R);
    Canvas.Brush.Style := bsClear;
    Canvas.Font.Style := [fsBold];
    Canvas.Font.Handle;
    Canvas.Font := Font;
    Canvas.Font.Handle;    
    Flags := DT_CENTER or DT_WORDBREAK;
    {$IFDEF RICHVIEWDEF4}
    Flags := DrawTextBiDiModeFlags(Flags);
    {$ENDIF}
    if not Checked then begin
      if not Enabled then
        State := RBS_UNCHECKEDDISABLED
      else
        State := RBS_UNCHECKEDNORMAL;
      end
    else begin
      if not Enabled then
        State := RBS_CHECKEDDISABLED
      else
        State := RBS_CHECKEDNORMAL;
    end;
    if TextPosition=orbtpBottom then
      inc(R.Top,2)
    else begin
      y := R.Bottom-2;
      if FThemeRadio<>0 then
        RV_DrawThemeText(FThemeRadio, Canvas.Handle, BP_RADIOBUTTON, State,
          PWideChar(_GetWideString(Caption, Font.Charset)),
          Length(_GetWideString(Caption, Font.Charset)),
          Flags or DT_CALCRECT, 0, R)
      else
        DrawText(Canvas.Handle, PChar(Caption), Length(Caption), R, Flags or DT_CALCRECT);
      R := Rect(0, y - (R.Bottom-R.Top), ARect.Right, y);
    end;
    if not Enabled and Disabled3D and (FThemeRadio=0) then begin
      OffsetRect(R, 1, 1);
      Canvas.Font.Color := clBtnHighlight;
      DrawText(Canvas.Handle, PChar(Caption), Length(Caption), R, Flags);
      OffsetRect(R, -1, -1);
      Canvas.Font.Color := clBtnShadow;
      DrawText(Canvas.Handle, PChar(Caption), Length(Caption), R, Flags);
      end
    else begin
      if not Enabled then
        Canvas.Font.Color := clBtnShadow;
      if FThemeRadio<>0 then
        RV_DrawThemeText(FThemeRadio, Canvas.Handle, BP_RADIOBUTTON, State,
          PWideChar(_GetWideString(Caption, Font.Charset)),
          Length(_GetWideString(Caption, Font.Charset)),
          Flags, 0, R)
      else
        DrawText(Canvas.Handle, PChar(Caption), Length(Caption), R, Flags);
    end;
  end;
end;
{------------------------------------------------------------------------------}
procedure TRVOfficeRadioButton.CNDrawItem(var Msg: TWMDrawItem);
var Canvas: TCanvas;
    MemDC: HDC;
    MemBitmap, OldBitmap: HBITMAP;
begin
  if {$IFDEF USERVKSDEVTE} (CurrentTheme=nil) and {$ENDIF} (FThemeEdit<>0) then begin
    Canvas := TCanvas.Create;
    try
      Canvas.Handle := Msg.DrawItemStruct.hDC;
      DrawTo(Canvas, Msg.DrawItemStruct.rcItem, (Msg.DrawItemStruct.itemState and ODS_FOCUS) <> 0);
    finally
      Canvas.Handle := 0;
      Canvas.Free;
    end;
    exit;
  end;

  with Msg.DrawItemStruct.rcItem do
    MemBitmap := CreateCompatibleBitmap(Msg.DrawItemStruct.hDC, Right-Left, Bottom-Top);
  MemDC := CreateCompatibleDC(Msg.DrawItemStruct.hDC);
  OldBitmap := SelectObject(MemDC, MemBitmap);
  try
    Canvas := TCanvas.Create;
    try
      Canvas.Handle := MemDC;
      DrawTo(Canvas, Msg.DrawItemStruct.rcItem, (Msg.DrawItemStruct.itemState and ODS_FOCUS) <> 0);
    finally
      Canvas.Handle := 0;
      Canvas.Free;
    end;
    with Msg.DrawItemStruct.rcItem do
      BitBlt(Msg.DrawItemStruct.hDC, Left, Top, Right-Left, Bottom-Top, MemDC, 0, 0, SRCCOPY);
  finally
    SelectObject(MemDC, OldBitmap);
    DeleteDC(MemDC);
    DeleteObject(MemBitmap);
  end;
end;
{------------------------------------------------------------------------------}
procedure TRVOfficeRadioButton.SetFillColor(Value: TColor);
begin
  if FFillColor<>Value then begin
    FFillColor := Value;
    Invalidate;
  end;
end;
{------------------------------------------------------------------------------}
procedure TRVOfficeRadioButton.SetTextAreaHeight(const Value: Integer);
begin
  if FTextAreaHeight<>Value then begin
    FTextAreaHeight := Value;
    Invalidate;
  end;
end;
{------------------------------------------------------------------------------}
procedure TRVOfficeRadioButton.SetSelColor(const Value: TColor);
begin
  if FSelColor<>Value then begin
    FSelColor := Value;
    if Checked then
      Invalidate;
  end;
end;
{------------------------------------------------------------------------------}
procedure TRVOfficeRadioButton.SetSelWidth(const Value: Integer);
begin
  if FSelWidth<>Value then begin
    FSelWidth := Value;
    if Checked then
      Invalidate;
  end;
end;
{------------------------------------------------------------------------------}
procedure TRVOfficeRadioButton.SetDisabled3d(const Value: Boolean);
begin
  if FDisabled3D<>Value then begin
    FDisabled3D := Value;
    if not Enabled then
      Invalidate;
  end;
end;
{------------------------------------------------------------------------------}
procedure TRVOfficeRadioButton.SetTextPosition(Value: TORBTextPosition);
begin
  if FTextPosition<>Value then begin
    FTextPosition := Value;
    Invalidate;
  end;
end;
{------------------------------------------------------------------------------}
procedure TRVOfficeRadioButton.SetImages(const Value: TRVImageList);
begin
  if Images <> nil then
    Images.UnRegisterChanges(FImageChangeLink);
  FImages := Value;
  if Images <> nil then begin
    Images.RegisterChanges(FImageChangeLink);
    Images.FreeNotification(Self);
  end
end;
{------------------------------------------------------------------------------}
procedure TRVOfficeRadioButton.SetImageIndex(const Value: TImageIndex);
begin
  if FImageIndex<>Value then begin
    FImageIndex := Value;
    Invalidate;
  end;
end;
{------------------------------------------------------------------------------}
procedure TRVOfficeRadioButton.SetSquare(const Value: Boolean);
begin
  if FSquare<>Value then begin
    FSquare := Value;
    Invalidate;
  end;
end;
procedure TRVOfficeRadioButton.SetLargeSelection(const Value: Boolean);
begin
  if FLargeSelection<>Value then begin
    FLargeSelection := Value;
    Invalidate;
  end;
end;
{------------------------------------------------------------------------------}
procedure TRVOfficeRadioButton.Notification(AComponent: TComponent;
  Operation: TOperation);
begin
  inherited Notification(AComponent, Operation);
  if (Operation=opRemove) and (AComponent=FImages) then begin
      FImages := nil;
  end;
end;
{------------------------------------------------------------------------------}
procedure TRVOfficeRadioButton.ImageListChange(Sender: TObject);
begin
  Invalidate;
end;
{------------------------------------------------------------------------------}
procedure TRVOfficeRadioButton.CreateThemeHandle;
begin
  if UseXPThemes and Assigned(RV_IsAppThemed) and RV_IsAppThemed and RV_IsThemeActive then begin
    FThemeEdit := RV_OpenThemeData(Handle, PWideChar(WideString('Edit')));
    FThemeRadio := RV_OpenThemeData(Handle, PWideChar(WideString('Button')));
    end
  else begin
    FThemeEdit := 0;
    FThemeRadio := 0;
  end;
end;
{------------------------------------------------------------------------------}
procedure TRVOfficeRadioButton.FreeThemeHandle;
begin
  if FThemeEdit<>0 then
    RV_CloseThemeData(FThemeEdit);
  FThemeEdit := 0;
  if FThemeRadio<>0 then
    RV_CloseThemeData(FThemeRadio);
  FThemeRadio := 0;
end;
{------------------------------------------------------------------------------}
procedure TRVOfficeRadioButton.CreateWnd;
begin
  inherited;
  CreateThemeHandle;
end;
{------------------------------------------------------------------------------}
procedure TRVOfficeRadioButton.DestroyWnd;
begin
  inherited;
  FreeThemeHandle;
end;
{------------------------------------------------------------------------------}
procedure TRVOfficeRadioButton.WMThemeChanged(var Message: TMessage);
begin
  FreeThemeHandle;
  CreateThemeHandle;
  Invalidate;
  Message.Result := 1;
end;
{------------------------------------------------------------------------------}
{$IFDEF USERVKSDEVTE}
procedure TRVOfficeRadioButton.SNMThemeMessage(var Msg: TMessage);
begin
  case Msg.wParam of
    SMP_APPLYTHEME, SMP_CHANGETHEME, SMP_REPAINT, SMP_REMOVETHEME:
      Invalidate;
  end;
end;
{$ENDIF}
{------------------------------------------------------------------------------}
procedure TRVOfficeRadioButton.SetUseXPThemes(const Value: Boolean);
begin
  if FUseXPThemes<>Value then begin
    FUseXPThemes := Value;
    if HandleAllocated then begin
      FreeThemeHandle;
      CreateThemeHandle;
      Invalidate;
    end;
  end;
end;
{========================= TOfficeGroupItem ===================================}
procedure TOfficeGroupItem.AssignTo(Dest: TPersistent);
begin
  if Dest is TRVOfficeRadioButton then begin
    TRVOfficeRadioButton(Dest).FFillColor  := FFillColor;
    TRVOfficeRadioButton(Dest).FImageIndex := FImageIndex;
    TRVOfficeRadioButton(Dest).Enabled     := FEnabled;
    TRVOfficeRadioButton(Dest).Hint        := FHint;
    TRVOfficeRadioButton(Dest).HelpContext := FHelpContext;
    {$IFDEF RICHVIEWDEF6}
    TRVOfficeRadioButton(Dest).HelpKeyword := FHelpKeyword;
    TRVOfficeRadioButton(Dest).HelpType    := FHelpType;
    {$ENDIF}
    TRVOfficeRadioButton(Dest).Caption     := FCaption;
    end
  else
    inherited;
end;
{------------------------------------------------------------------------------}
procedure TOfficeGroupItem.Assign(Source: TPersistent);
begin
  if Source is TOfficeGroupItem then begin
    FFillColor   := TOfficeGroupItem(Source).FFillColor;
    FImageIndex  := TOfficeGroupItem(Source).FImageIndex;
    FEnabled     := TOfficeGroupItem(Source).FEnabled;
    FHint        := TOfficeGroupItem(Source).FHint;
    FHelpContext := TOfficeGroupItem(Source).FHelpContext;
    {$IFDEF RICHVIEWDEF6}
    FHelpKeyword := TOfficeGroupItem(Source).FHelpKeyword;
    FHelpType    := TOfficeGroupItem(Source).FHelpType;
    {$ENDIF}
    FCaption     := TOfficeGroupItem(Source).FCaption;
    end
  else
    inherited;
end;
{------------------------------------------------------------------------------}
constructor TOfficeGroupItem.Create(Collection: TCollection);
begin
  inherited Create(Collection);
  FFillColor  := clWindow;
  FImageIndex := -1;
  FEnabled    := True;
  {$IFDEF RICHVIEWDEF6}
  FHelpType   := htContext;
  {$ENDIF}
  FCaption    := 'Item';
end;
{------------------------------------------------------------------------------}
procedure TOfficeGroupItem.SetCaption(const Value: TCaption);
begin
  if FCaption<>Value then begin
    FCaption := Value;
    Changed(False);
  end;
end;
{------------------------------------------------------------------------------}
procedure TOfficeGroupItem.SetEnabled(Value: Boolean);
begin
  if FEnabled<>Value then begin
    FEnabled := Value;
    Changed(False);
  end;
end;
{------------------------------------------------------------------------------}
procedure TOfficeGroupItem.SetFillColor(Value: TColor);
begin
  if FFillColor<>Value then begin
    FFillColor := Value;
    Changed(False);
  end;
end;
{------------------------------------------------------------------------------}
procedure TOfficeGroupItem.SetHelpContext(Value: THelpContext);
begin
  if FHelpContext<>Value then begin
    FHelpContext := Value;
    Changed(False);
  end;
end;
{------------------------------------------------------------------------------}
{$IFDEF RICHVIEWDEF6}
procedure TOfficeGroupItem.SetHelpKeyword(Value: String);
begin
  if FHelpKeyword<>Value then begin
    FHelpKeyword := Value;
    Changed(False);
  end;
end;
{------------------------------------------------------------------------------}
procedure TOfficeGroupItem.SetHelpType(Value: THelpType);
begin
  if FHelpType<>Value then begin
    FHelpType := Value;
    Changed(False);
  end;
end;
{$ENDIF}
{------------------------------------------------------------------------------}
procedure TOfficeGroupItem.SetHint(Value: String);
begin
  if FHint<>Value then begin
    FHint := Value;
    Changed(False);
  end;
end;
{------------------------------------------------------------------------------}
procedure TOfficeGroupItem.SetImageIndex(Value: TImageIndex);
begin
  if FImageIndex<>Value then begin
    FImageIndex := Value;
    Changed(False);
  end;
end;
{========================== TOfficeGroupButton ================================}
type
  TOfficeGroupButton = class(TRVOfficeRadioButton)
    private
      FInClick: Boolean;
      procedure CNCommand(var Msg: TWMCommand); message CN_COMMAND;
      procedure WMGetDlgCode(var Msg: TWMGetDlgCode); message WM_GETDLGCODE;
    protected
      procedure KeyDown(var Key: Word; Shift: TShiftState); override;
      procedure KeyPress(var Key: Char); override;
      procedure DblClick; override;
    public
      constructor InternalCreate(RadioGroup: TRVOfficeRadioGroup);
      destructor Destroy; override;
  end;
{------------------------------------------------------------------------------}
constructor TOfficeGroupButton.InternalCreate(RadioGroup: TRVOfficeRadioGroup);
begin
  inherited Create(RadioGroup);
  RadioGroup.FButtons.Add(Self);
  Visible        := False;
  Enabled        := RadioGroup.Enabled;
  OnClick        := RadioGroup.ButtonClick;
  Parent         := RadioGroup;
end;
{------------------------------------------------------------------------------}
destructor TOfficeGroupButton.Destroy;
begin
  TRVOfficeRadioGroup(Owner).FButtons.Remove(Self);
  inherited Destroy;
end;
{------------------------------------------------------------------------------}
procedure TOfficeGroupButton.CNCommand(var Msg: TWMCommand);
begin
  if not FInClick then begin
    FInClick := True;
    try
      if ((Msg.NotifyCode = BN_CLICKED) or
        (Msg.NotifyCode = BN_DOUBLECLICKED) or
        (Msg.NotifyCode = BN_SETFOCUS)) and
        TRVOfficeRadioGroup(Parent).CanModify then
        inherited;
    except
      Application.HandleException(Self);
    end;
    FInClick := False;
  end;
end;
{------------------------------------------------------------------------------}
procedure TOfficeGroupButton.WMGetDlgCode(var Msg: TWMGetDlgCode);
begin
  inherited;
  Msg.Result := Msg.Result or DLGC_WANTARROWS;	
end;
{------------------------------------------------------------------------------}
procedure TOfficeGroupButton.KeyPress(var Key: Char);
begin
  inherited KeyPress(Key);
  TRVOfficeRadioGroup(Parent).KeyPress(Key);
  if ((Key = #8) or (Key = ' ')) and not TRVOfficeRadioGroup(Parent).CanModify then
    Key := #0;
end;
{------------------------------------------------------------------------------}
procedure TOfficeGroupButton.KeyDown(var Key: Word; Shift: TShiftState);
var Index: Integer;
begin
  if (Key=VK_LEFT) or (Key=VK_RIGHT) or (Key=VK_UP) or (Key=VK_DOWN) then begin
     Index := TRVOfficeRadioGroup(Parent).FButtons.IndexOf(Self);
     Index := TRVOfficeRadioGroup(Parent).GetAnother(Index, Key);
     if Index>=0 then
       TRVOfficeRadioButton(TRVOfficeRadioGroup(Parent).FButtons[Index]).SetFocus;
     Key := 0;
     exit;
  end;
  inherited KeyDown(Key, Shift);
  TRVOfficeRadioGroup(Parent).KeyDown(Key, Shift);
end;
{------------------------------------------------------------------------------}
procedure TOfficeGroupButton.DblClick;
begin
  inherited;
  if Assigned(TRVOfficeRadioGroup(Parent).OnDblClickItem) then
    TRVOfficeRadioGroup(Parent).OnDblClickItem(Parent);
end;
{============================ TOfficeGroupItems ===============================}
constructor TOfficeGroupItems.Create(RadioGroup: TRVOfficeRadioGroup);
begin
  inherited Create(TOfficeGroupItem);
  FRadioGroup := RadioGroup;
end;
{------------------------------------------------------------------------------}
function TOfficeGroupItems.Add: TOfficeGroupItem;
begin
  Result := TOfficeGroupItem(inherited Add);
end;
{------------------------------------------------------------------------------}
function TOfficeGroupItems.GetItem(Index: Integer): TOfficeGroupItem;
begin
  Result := TOfficeGroupItem(inherited GetItem(Index));
end;
{------------------------------------------------------------------------------}
function TOfficeGroupItems.GetOwner: TPersistent;
begin
  Result := FRadioGroup;
end;
{------------------------------------------------------------------------------}
{$IFDEF RICHVIEWDEF4}
function TOfficeGroupItems.Insert(Index: Integer): TOfficeGroupItem;
begin
  Result := TOfficeGroupItem(inherited Insert(Index));
end;
{$ENDIF}
{------------------------------------------------------------------------------}
procedure TOfficeGroupItems.SetItem(Index: Integer;
  Value: TOfficeGroupItem);
begin
  inherited SetItem(Index, Value);
end;
{------------------------------------------------------------------------------}
procedure TOfficeGroupItems.Update(Item: TCollectionItem);
begin
  if Item <> nil then
    FRadioGroup.UpdateButton(Item.Index)
  else
    FRadioGroup.UpdateButtons;
end;
{============================== TRVOfficeRadioGroup =============================}
constructor TRVOfficeRadioGroup.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  ControlStyle := [csSetCaption, csDoubleClicks];
  FButtons     := TList.Create;
  FItems       := TOfficeGroupItems.Create(Self);
  FItemIndex   := -1;
  FColumns     := 1;
  FMargin      := 10;
  FSelColor    := clHighlight;
  FSelWidth    := 2;
  FTextAreaHeight := 30;
  FUseXPThemes := True;
  FDisabled3D  := True;
  Width        := 200;
  Height       := 200;
  TabStop      := True;
  {$IFDEF USERVKSDEVTE}
  AddThemeNotification(Self);
  {$ENDIF}
  //DoubleBuffered := True;
end;
{------------------------------------------------------------------------------}
destructor TRVOfficeRadioGroup.Destroy;
begin
  SetButtonCount(0);
  FItems.Free;
  FButtons.Free;
  {$IFDEF USERVKSDEVTE}
  RemoveThemeNotification(Self);
  {$ENDIF}
  inherited Destroy;
end;
{------------------------------------------------------------------------------}
procedure TRVOfficeRadioGroup.Loaded;
begin
  inherited Loaded;
  ArrangeButtons;
end;
{------------------------------------------------------------------------------}
procedure TRVOfficeRadioGroup.Notification(AComponent: TComponent;
  Operation: TOperation);
begin
  inherited Notification(AComponent, Operation);
  if (Operation=opRemove) and (AComponent=FImages) then begin
    FImages := nil;
  end;
end;
{------------------------------------------------------------------------------}
procedure TRVOfficeRadioGroup.ReadState(Reader: TReader);
begin
  FReading := True;
  inherited ReadState(Reader);
  FReading := False;
  UpdateButtons;
end;
{------------------------------------------------------------------------------}
{$IFDEF RICHVIEWDEF4}
procedure TRVOfficeRadioGroup.FlipChildren(AllLevels: Boolean);
begin

end;
{$ENDIF}
{------------------------------------------------------------------------------}
procedure TRVOfficeRadioGroup.GetChildren(Proc: TGetChildProc; Root: TComponent);
begin

end;
{------------------------------------------------------------------------------}
procedure TRVOfficeRadioGroup.ArrangeButtons;
var
  ButtonsPerCol, ButtonWidth, ButtonHeight, TopMargin, i: Integer;
  DC: HDC;
  SaveFont: HFont;
  Metrics: TTextMetric;
  DeferHandle: THandle;
  ALeft: Integer;
begin
  if (FButtons.Count <> 0) and not FReading then begin
    DC := GetDC(0);
    SaveFont := SelectObject(DC, Font.Handle);
    GetTextMetrics(DC, Metrics);
    SelectObject(DC, SaveFont);
    ReleaseDC(0, DC);
    ButtonsPerCol := (FButtons.Count + FColumns - 1) div FColumns;
    ButtonWidth := (Width-Margin*2) div FColumns;
    TopMargin := Metrics.tmHeight + 1;
    ButtonHeight := (Height-TopMargin-Margin*2) div ButtonsPerCol;
    DeferHandle := BeginDeferWindowPos(FButtons.Count);
    try
      for i := 0 to FButtons.Count - 1 do
        with TOfficeGroupButton(FButtons[I]) do begin
          {$IFDEF RICHVIEWDEF4}
          BiDiMode := Self.BiDiMode;
          {$ENDIF}
          ALeft := (i div ButtonsPerCol) * ButtonWidth + Margin + Margin div 2;
          {$IFDEF RICHVIEWDEF4}
          if UseRightToLeftAlignment then
            ALeft := Self.ClientWidth - ALeft - ButtonWidth+Margin;
          {$ENDIF}
          DeferHandle := DeferWindowPos(DeferHandle, Handle, 0,
            ALeft,
            (I mod ButtonsPerCol) * ButtonHeight + TopMargin+Margin,
            ButtonWidth-Margin, ButtonHeight,
            SWP_NOZORDER or SWP_NOACTIVATE);
          Visible := True;
        end;
    finally
      EndDeferWindowPos(DeferHandle);
    end;
  end;
end;
{------------------------------------------------------------------------------}
procedure TRVOfficeRadioGroup.ButtonClick(Sender: TObject);
begin
  if not FUpdating then begin
    FItemIndex := FButtons.IndexOf(Sender);
    Changed;
    Click;
  end;
end;
{------------------------------------------------------------------------------}
procedure TRVOfficeRadioGroup.SetMargin(Value: Integer);
begin
  if FMargin<>Value then begin
    FMargin := Value;
    if not FReading then
      ArrangeButtons;
  end;
end;
{------------------------------------------------------------------------------}
procedure TRVOfficeRadioGroup.SetImages(Value: TRVImageList);
begin
  if FImages<>Value then begin
    FImages := Value;
    UpdateButtonsGlbl(True);
  end;
end;
{------------------------------------------------------------------------------}
procedure TRVOfficeRadioGroup.SetDisabled3d(Value: Boolean);
begin
  if FDisabled3d<>Value then begin
    FDisabled3d := Value;
    UpdateButtonsGlbl(True);
  end;
end;
{------------------------------------------------------------------------------}
procedure TRVOfficeRadioGroup.SetSelColor(Value: TColor);
begin
  if FSelColor<>Value then begin
    FSelColor := Value;
    UpdateButtonsGlbl(True);
  end;
end;
{------------------------------------------------------------------------------}
procedure TRVOfficeRadioGroup.SetSelWidth(Value: Integer);
begin
  if FSelWidth<>Value then begin
    FSelWidth := Value;
    UpdateButtonsGlbl(True);
  end;
end;
{------------------------------------------------------------------------------}
procedure TRVOfficeRadioGroup.SetTextAreaHeight(Value: Integer);
begin
  if FTextAreaHeight<>Value then begin
    FTextAreaHeight := Value;
    UpdateButtonsGlbl(True);
  end;
end;
{------------------------------------------------------------------------------}
procedure TRVOfficeRadioGroup.SetTextPosition(Value: TORBTextPosition);
begin
  if FTextPosition<>Value then begin
    FTextPosition := Value;
    UpdateButtonsGlbl(True);
  end;
end;
{------------------------------------------------------------------------------}
procedure TRVOfficeRadioGroup.SetSquare(const Value: Boolean);
begin
  if FSquare<>Value then begin
    FSquare := Value;
    UpdateButtonsGlbl(True);
  end;
end;
{------------------------------------------------------------------------------}
procedure TRVOfficeRadioGroup.SetUseXPThemes(const Value: Boolean);
begin
  if FUseXPThemes<>Value then begin
    FUseXPThemes := Value;
    FreeThemeHandle;
    CreateThemeHandle;
    UpdateButtonsGlbl(True);
    Invalidate;
  end;
end;
{------------------------------------------------------------------------------}
procedure TRVOfficeRadioGroup.SetCustomDraw(Value: TORGCustomDrawEvent);
begin
  FOnCustomDraw := Value;
  UpdateButtonsGlbl(True);
end;
{------------------------------------------------------------------------------}
procedure TRVOfficeRadioGroup.DoCustomDraw(Sender: TRVOfficeRadioButton;
  Canvas: TCanvas; const ARect: TRect; var DoDefault: Boolean);
begin
  if Assigned(FOnCustomDraw) then
    FOnCustomDraw(Self, FButtons.IndexOf(Sender), Canvas, ARect, DoDefault);
end;

{------------------------------------------------------------------------------}
procedure TRVOfficeRadioGroup.SetButtonCount(Value: Integer);
begin
  while FButtons.Count<Value do
    TOfficeGroupButton.InternalCreate(Self);
  while FButtons.Count>Value do
    TOfficeGroupButton(FButtons.Last).Free;
end;
{------------------------------------------------------------------------------}
procedure TRVOfficeRadioGroup.SetColumns(Value: Integer);
begin
  AdjustValue(Value, 1, 16);
  if FColumns<>Value then begin
    FColumns := Value;
    ArrangeButtons;
    Invalidate;
  end;
end;
{------------------------------------------------------------------------------}
procedure TRVOfficeRadioGroup.SetItemIndex(Value: Integer);
begin
  if FReading then
    FItemIndex := Value
  else begin
    AdjustValue(Value, -1, FButtons.Count-1);
    if FItemIndex<>Value then begin
      if FItemIndex >= 0 then
        TOfficeGroupButton(FButtons[FItemIndex]).Checked := False;
      FItemIndex := Value;
      if FItemIndex >= 0 then
        TOfficeGroupButton(FButtons[FItemIndex]).Checked := True;
    end;
  end;
end;
{------------------------------------------------------------------------------}
procedure TRVOfficeRadioGroup.SetItems(Value: TOfficeGroupItems);
begin
  FItems.Assign(Value);
end;
{------------------------------------------------------------------------------}
procedure TRVOfficeRadioGroup.UpdateButtons;
var i: Integer;
begin
  if not FReading then begin
    AdjustValue(FItemIndex, -1, FItems.Count-1);
    SetButtonCount(FItems.Count);
    UpdateButtonsGlbl(False);
    for i := 0 to FButtons.Count - 1 do
      TOfficeGroupButton(FButtons[i]).Assign(FItems[i]);
    if FItemIndex >= 0 then begin
      FUpdating := True;
      TOfficeGroupButton(FButtons[FItemIndex]).Checked := True;
      FUpdating := False;
    end;
    ArrangeButtons;
    Invalidate;
  end;
end;
{------------------------------------------------------------------------------}
procedure TRVOfficeRadioGroup.UpdateButtonsGlbl(Repaint: Boolean);
var i: Integer;
    btn: TOfficeGroupButton;
begin
  if not FReading then
    for i := 0 to FButtons.Count - 1 do begin
      btn := TOfficeGroupButton(FButtons[i]);
      btn.Images          := Images;
      btn.FSelColor       := SelColor;
      btn.FSelWidth       := SelWidth;
      btn.FTextAreaHeight := TextAreaHeight;
      btn.FDisabled3d     := Disabled3D;
      btn.FTextPosition   := TextPosition;
      btn.FSquare         := Square;
      btn.UseXPThemes     := UseXPThemes;
      if Assigned(FOnCustomDraw) then
        btn.OnCustomDraw := DoCustomDraw
      else
        btn.OnCustomDraw := nil;
      if Repaint then
        btn.Invalidate;
    end;
end;
{------------------------------------------------------------------------------}
procedure TRVOfficeRadioGroup.UpdateButton(Index: Integer);
begin
  if not FReading then begin
    TOfficeGroupButton(FButtons[Index]).Assign(FItems[Index]);
    TOfficeGroupButton(FButtons[Index]).Invalidate;
  end;
end;
{------------------------------------------------------------------------------}
procedure TRVOfficeRadioGroup.CMEnabledChanged(var Msg: TMessage);
var
  i: Integer;
begin
  inherited;
  for i := 0 to FButtons.Count-1 do
    TOfficeGroupButton(FButtons[i]).Enabled := Enabled;
end;
{------------------------------------------------------------------------------}
procedure TRVOfficeRadioGroup.CMFontChanged(var Msg: TMessage);
begin
  inherited;
  ArrangeButtons;
end;
{------------------------------------------------------------------------------}
procedure TRVOfficeRadioGroup.WMSize(var Msg: TWMSize);
begin
  inherited;
  ArrangeButtons;
end;
{------------------------------------------------------------------------------}
function TRVOfficeRadioGroup.CanModify: Boolean;
begin
  Result := True;
end;
{------------------------------------------------------------------------------}
function TRVOfficeRadioGroup.GetAnother(Index: Integer; Key: Word): Integer;
var ButtonsPerCol,i: Integer;
    AllDisabled: Boolean;
    {.......................................}
    function MakeStep(Index: Integer): Integer;
    begin
      case Key of
        VK_DOWN:
          Result := Index+1;
        VK_UP:
          Result := Index-1;
        VK_RIGHT:
          begin
            if Index=(Columns*ButtonsPerCol)-1 then
              Result := 0
            else begin
              Result := Index+ButtonsPerCol;
              if Index div ButtonsPerCol = Columns-1 then
                inc(Result);
            end;
          end;
        VK_LEFT:
          begin
            if Index=0 then
              Result := (Columns*ButtonsPerCol)-1
            else begin
              Result := Index-ButtonsPerCol;
              if Index div ButtonsPerCol = 0 then
                dec(Result);
            end;
          end;
        else
          Result := 0;
      end;
    end;
    {.......................................}
begin
  AllDisabled := True;
  for i := 0 to FButtons.Count-1 do
    if TRVOfficeRadioButton(FButtons[i]).CanFocus then begin
      AllDisabled := False;
      break;
    end;
  if AllDisabled then begin
    Result := -1;
    exit;
  end;
  ButtonsPerCol := (FButtons.Count + FColumns - 1) div FColumns;
  Result := Index;
  repeat
    Result := MakeStep(Result);
    if Result<0 then
      inc(Result,(Columns*ButtonsPerCol));
    Result := Result mod (Columns*ButtonsPerCol);
  until (Result<FButtons.Count) and TRVOfficeRadioButton(FButtons[Result]).CanFocus;
end;
{------------------------------------------------------------------------------}
procedure TRVOfficeRadioGroup.CreateThemeHandle;
begin
  if UseXPThemes and Assigned(RV_IsAppThemed) and RV_IsAppThemed and RV_IsThemeActive then
    FTheme := RV_OpenThemeData(Handle, PWideChar(WideString('Button')))
  else
    FTheme := 0;
end;
{------------------------------------------------------------------------------}
procedure TRVOfficeRadioGroup.FreeThemeHandle;
begin
  if FTheme<>0 then
    RV_CloseThemeData(FTheme);
  FTheme := 0;
end;
{------------------------------------------------------------------------------}
procedure TRVOfficeRadioGroup.CreateWnd;
begin
  inherited;
  CreateThemeHandle
end;
{------------------------------------------------------------------------------}
procedure TRVOfficeRadioGroup.DestroyWnd;
begin
  inherited;
  FreeThemeHandle;
end;
{------------------------------------------------------------------------------}
procedure TRVOfficeRadioGroup.WMThemeChanged(var Msg: TMessage);
begin
  FreeThemeHandle;
  CreateThemeHandle;
  Invalidate;
  Msg.Result := 1;
end;

function TRVOfficeRadioGroup.GetThemeState: Integer;
begin
  if Enabled then
    Result := GBS_NORMAL
  else
    Result := GBS_DISABLED;
end;

procedure TRVOfficeRadioGroup.PerformEraseBackground(DC: HDC; FullPainting: Boolean);
var
  LastOrigin: TPoint;
begin
  GetWindowOrgEx(DC, LastOrigin);
  SetWindowOrgEx(DC, LastOrigin.X + Left, LastOrigin.Y + Top, nil);
  Parent.Perform(WM_ERASEBKGND, Integer(DC), Integer(DC));
  if FullPainting then
    Parent.Perform(WM_PAINT, Integer(DC), 0);
  SetWindowOrgEx(DC, LastOrigin.X, LastOrigin.Y, nil);
end;

procedure TRVOfficeRadioGroup.WMEraseBkgnd(var Msg: TWMEraseBkgnd);
begin
  if not UseXPThemes or (FTheme=0) then begin
    inherited;
    exit;
  end;
  if DoubleBuffered then
    PerformEraseBackground(Msg.DC, False)
  else if RV_IsThemeBackgroundPartiallyTransparent(FTheme, BP_GROUPBOX, GetThemeState) then
    RV_DrawThemeParentBackground(Handle, Msg.DC, nil);
  Msg.Result := 1;
end;

function TRVOfficeRadioGroup.GetCaptionRect(DC: THandle): TRect;
var Size: TSize;
begin
  if Text <> '' then begin
    GetTextExtentPoint32(DC, PChar(Text), Length(Text), Size);
    Result := Rect(0, 0, Size.cx, Size.cy);
    if not UseRightToLeftAlignment then
      OffsetRect(Result, 8, 0)
    else
      OffsetRect(Result, Width - 8 - Result.Right, 0);
    end
  else
    Result := Rect(0, 0, 0, 0);
end;

procedure TRVOfficeRadioGroup.Paint;
var
    CaptionRect,
    OuterRect: TRect;
begin
  {$IFDEF USERVKSDEVTE}
  if IsObjectDefined(kgscGroupBox, ktoDefault) then begin
    if CurrentTheme.IsGroupTransparent(kgscGroupBox, ktoDefault) then
      DrawControlBackground(Self, Canvas.Handle);
    if not Enabled then
      Canvas.Font := CurrentTheme.Fonts[ktfGroupBoxTextDisabled]
    else
      Canvas.Font := CurrentTheme.Fonts[ktfGroupBoxTextNormal];
    CaptionRect := GetCaptionRect(Canvas.Handle);
    CurrentTheme.GroupDraw(kgscGroupBox, Canvas,
      GroupInfo(Rect(0, 0, Width, Height), CaptionRect, kgdsNormal));
    CurrentTheme.GroupDrawText(kgscGroupBox, Canvas,
      GroupInfo(Rect(0, 0, Width, Height), CaptionRect, kgdsNormal),
      TextInfo(CaptionRect, Text, DT_CENTER or DT_VCENTER or DT_SINGLELINE));
    exit;
  end;
  {$ENDIF}

  if not UseXPThemes or (FTheme=0) then begin
    inherited;
    exit;
  end;

  {$IFDEF USERVKSDEVTE}
  DrawControlBackground(Self, Canvas.Handle);
  {$ENDIF}
{
  if DoubleBuffered then
    PerformEraseBackground(Canvas.Handle, True)
  else if RV_IsThemeBackgroundPartiallyTransparent(FTheme, BP_GROUPBOX, GetThemeState) then
    RV_DrawThemeParentBackground(Handle, Canvas.Handle, nil);
 }
  Canvas.Font := Font;


  CaptionRect := GetCaptionRect(Canvas.Handle);

  OuterRect := ClientRect;
  OuterRect.Top := (CaptionRect.Bottom - CaptionRect.Top) div 2;
  with CaptionRect do
    ExcludeClipRect(Canvas.Handle, Left, Top, Right, Bottom);

  RV_DrawThemeBackground(FTheme, Canvas.Handle, BP_GROUPBOX, GetThemeState, OuterRect, nil);

  SelectClipRgn(Canvas.Handle, 0);
  if (Text <> '') then
    RV_DrawThemeText(FTheme, Canvas.Handle, BP_GROUPBOX, GetThemeState,
      PWideChar(_GetWideString(Text, Font.Charset)),
      Length(_GetWideString(Text, Font.Charset)), DT_LEFT, 0, CaptionRect);

  PaintControls(Canvas.Handle, nil);

end;

procedure TRVOfficeRadioGroup.WMPaint(var Msg: TWMPaint);
var PS: TPaintStruct;
    CaptionRect,
    OuterRect: TRect;
    LastFont: HFONT;
begin
  {$IFDEF USERVKSDEVTE}
  if CurrentTheme<>nil then begin
    inherited;
    exit;
  end;
  {$ENDIF}

  if not UseXPThemes or (FTheme=0) then begin
    inherited;
    exit;
  end;

  BeginPaint(Handle, PS);

  {$IFDEF USERVKSDEVTE}
  DrawControlBackground(Self, PS.hdc);
  {$ENDIF}

  LastFont := SelectObject(PS.hdc, Font.Handle);
  SetTextColor(PS.hdc, ColorToRGB(Font.Color));
  CaptionRect := GetCaptionRect(PS.hdc);

  OuterRect := ClientRect;
  OuterRect.Top := (CaptionRect.Bottom - CaptionRect.Top) div 2;
  with CaptionRect do
    ExcludeClipRect(PS.hdc, Left, Top, Right, Bottom);

  RV_DrawThemeBackground(FTheme, ps.hdc, BP_GROUPBOX, GetThemeState, OuterRect, nil);

  SelectClipRgn(PS.hdc, 0);
  if Text <> '' then
    RV_DrawThemeText(FTheme, PS.hdc, BP_GROUPBOX, GetThemeState,
      PWideChar(_GetWideString(Text, Font.Charset)),
      Length(_GetWideString(Text, Font.Charset)), DT_LEFT, 0, CaptionRect);

  SelectObject(PS.hdc, LastFont);

  PaintControls(PS.hdc, nil);
  EndPaint(Handle, PS);

  Msg.Result := 0;
end;

procedure TRVOfficeRadioGroup.WMSetFocus(var Msg: TMessage);
begin
  inherited;
  if not CanFocus then
    exit;
  if ItemIndex>=0 then
    TRVOfficeRadioButton(FButtons[ItemIndex]).SetFocus;
end;

procedure TRVOfficeRadioGroup.CMDenySubclassing(var Msg: TMessage);
begin
  Msg.Result := 1;
end;
{$IFDEF USERVKSDEVTE}
procedure TRVOfficeRadioGroup.SNMThemeMessage(var Msg: TMessage);
begin
  case Msg.wParam of
    SMP_APPLYTHEME, SMP_CHANGETHEME, SMP_REPAINT, SMP_REMOVETHEME:
      Invalidate;
  end;
end;
{$ENDIF}



end.
