home *** CD-ROM | disk | FTP | other *** search
/ Night Owl 25 / nopv25.iso / 040A / CAPCTRL.ZIP / SOURCE / CAPCTRL.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1997-03-21  |  42.1 KB  |  1,424 lines

  1. (******************************************************************************)
  2. (* CaptionControl                   Development tool: Borland Delphi 2.0      *)
  3. (* version 1.00                     Operating system: Microsoft Windows 95    *)
  4. (*                                                                            *)
  5. (* Read the accompanying documentation for information.                       *)
  6. (*                                                                            *)
  7. (* Copyright 1996, 1997 Yorai Aminov                                          *)
  8. (*                                                                            *)
  9. (*              yaminov@trendline.co.il (preffered)                           *)
  10. (*              CompuServe - 100274,720                                       *)
  11. (******************************************************************************)
  12.  
  13. unit CapCtrl;
  14.  
  15. interface
  16.  
  17. uses
  18.   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  19.   DsgnIntf, Menus;
  20.  
  21. type
  22.   { Exceptions }
  23.   ECaptionError = class(Exception);
  24.  
  25.   { Types }
  26.   TDirection = (dirLeft, dirRight);
  27.   TCaptionButtonKind = (cbkCustom, cbkOk, cbkRollUp, cbkRollDown, cbkDocument,
  28.     cbkFolder, cbkWindow, cbkMail, cbkDownArrow, cbkUpArrow, cbkLeftArrow,
  29.     cbkRightArrow, cbkMore, cbkFlag, cbkCool);
  30.   TCaptionGradient = (cgNone, cgActive, cgAlways);
  31.  
  32.   { Events }
  33.   TDrawCaptionEvent = procedure(Sender: TObject; var CaptionText: String;
  34.     DC: HDC; Rect: TRect; var Drawn: boolean) of object;
  35.   TCaptionButtonDrawEvent = procedure(Sender: TObject; ButtonIndex: Integer;
  36.     DC: HDC; Rect: TRect; var Drawn: boolean) of object;
  37.   TCaptionButtonClickEvent = procedure(Sender: TObject; ButtonIndex: Integer;
  38.     var Pushed: Boolean) of object;
  39.  
  40.   { TCaptionButton }
  41.   TCaptionButton = class
  42.   private
  43.     FCaption: String;
  44.     FEnabled: Boolean;
  45.     FVisible: Boolean;
  46.     FPushed: Boolean;
  47.     FKind: TCaptionButtonKind;
  48.   public
  49.     constructor Create;
  50.     function Draw(DC: HDC; Rect: TRect): Boolean; virtual;
  51.     function GetBtnKindStr: String;
  52.     procedure SetBtnKindStr(KindStr: String);
  53.   published
  54.     property Caption: String read FCaption write FCaption;
  55.     property Enabled: Boolean read FEnabled write FEnabled default True;
  56.     property Visible: Boolean read FVisible write FVisible default True;
  57.     property Pushed: Boolean read FPushed write FPushed default False;
  58.     property Kind: TCaptionButtonKind read FKind write FKind default cbkCustom;
  59.   end;
  60.  
  61.   { TCaptionButtonsList }
  62.   TCaptionButtonsList = class(TPersistent)
  63.   private
  64.     FButtonsList: TStringList;
  65.     procedure SetButton(Index: Integer; Value: TCaptionButton);
  66.     function GetButton(Index: Integer): TCaptionButton;
  67.     function GetCount: Integer;
  68.   protected
  69.     { property storage }
  70.     procedure DefineProperties(Filer: TFiler); override;
  71.     procedure ReadButtons(Reader: TReader);
  72.     procedure WriteButtons(Writer: TWriter);
  73.   public
  74.     constructor Create(AOwner: TComponent);
  75.     destructor Destroy; override;
  76.     procedure Add(Button: TCaptionButton);
  77.     procedure Insert(Index: Integer; Button: TCaptionButton);
  78.     procedure Delete(Index: Integer);
  79.     procedure Clear;
  80.     procedure AddButton(Caption: String; Enabled, Visible, Pushed: Boolean;
  81.       Kind: TCaptionButtonKind);
  82.     property Buttons[Index: Integer]: TCaptionButton read GetButton write SetButton; default;
  83.     property Count: Integer read GetCount;
  84.   published
  85.   end;
  86.  
  87.   { TCaptionButtonsListProperty }
  88.   TCaptionButtonsListProperty = class(TClassProperty)
  89.   public
  90.     procedure Edit; override;
  91.     function GetAttributes: TPropertyAttributes; override;
  92.     function GetValue: String; override;
  93.   end;
  94.  
  95.   { TCaptionControl }
  96.   TCaptionControl = class(TComponent)
  97.   private
  98.     { Internal fields }
  99.     Colors: array[0..1, 0..255] of TColorRef;
  100.     CaptionFont: HFONT;
  101.     DefWinProc: TFarProc;
  102.     DefWinProcInstance: Pointer;
  103.     FOnFormDestroy: TNotifyEvent;
  104.     BtnWidth: Integer;
  105.     DrawPushed: Boolean;
  106.     Pushed: Integer;
  107.     RestoreWndProc: Boolean;
  108.     rgn: HRGN;
  109.     FWindowActive: Boolean;
  110.     FMaximized: Boolean;
  111.     FButtonsLeft: Integer;
  112.     FRightPushed: Boolean;
  113.     { Property fields }
  114.     FEnabled: Boolean;
  115.     FCaptionGradient: TCaptionGradient;
  116.     FColorBands: Integer;
  117.     FShowButtons: Boolean;
  118.     FCaptionDirection: TDirection;
  119.     FButtonsDirection: TDirection;
  120.     FWindowDirection: TDirection;
  121.     FRtlReading: Boolean;
  122.     FButtons: TCaptionButtonsList;
  123.     FPopupMenu: TPopupMenu;
  124.     { Event fields }
  125.     FOnDrawCaption: TDrawCaptionEvent;
  126.     FOnButtonDraw: TCaptionButtonDrawEvent;
  127.     FOnButtonClick: TCaptionButtonClickEvent;
  128.     { Internal methods }
  129.     procedure CalculateColors;
  130.     function GetCaptionRect: TRect;
  131.     procedure OnCaptionControlDestroy(Sender: TObject);
  132.     procedure WinProc(var Message: TMessage);
  133.     function GetCoordButton(Point: TPoint): Integer;
  134.     { Drawing }
  135.     function DrawAllCaption(FormDC: HDC): TRect;
  136.     procedure DrawMenuIcon(DC: HDC; var R: TRect);
  137.     procedure FillRectGradient(DC: HDC; const R: TRect; Active: boolean);
  138.     procedure FillRectCaption(DC: HDC; const R: TRect; Active: boolean);
  139.     procedure DrawCaptionText(DC: HDC; R: TRect);
  140.     procedure DrawCaptionButtons(DC: HDC; var R: TRect);
  141.     { Property methods }
  142.     procedure SetCaptionGradient(Value: TCaptionGradient);
  143.     procedure SetShowButtons(Value: Boolean);
  144.     procedure SetCaptionDirection(Value: TDirection);
  145.     procedure SetButtonsDirection(Value: TDirection);
  146.     procedure SetWindowDirection(Value: TDirection);
  147.     procedure SetRtlReading(Value: Boolean);
  148.     procedure SetEnabled(Value: Boolean);
  149.     procedure SetColorBands(Value: Integer);
  150.     procedure SetPopupMenu(Value: TPopupMenu);
  151.   public
  152.     { Public methods }
  153.     constructor Create(AOwner: TComponent); override;
  154.     destructor Destroy; override;
  155.     procedure Refresh;
  156.   published
  157.     { Value properties }
  158.     property CaptionGradient: TCaptionGradient read FCaptionGradient write SetCaptionGradient default cgActive;
  159.     property ShowButtons: Boolean read FShowButtons write SetShowButtons default True;
  160.     property CaptionDirection: TDirection read FCaptionDirection write SetCaptionDirection default dirLeft;
  161.     property ButtonsDirection: TDirection read FButtonsDirection write SetButtonsDirection default dirRight;
  162.     property WindowDirection: TDirection read FWindowDirection write SetWindowDirection default dirLeft;
  163.     property RtlReading: Boolean read FRtlReading write SetRtlReading default False;
  164.     property Enabled: Boolean read FEnabled write SetEnabled default True;
  165.     property Buttons: TCaptionButtonsList read FButtons write FButtons;
  166.     property PopupMenu: TPopupMenu read FPopupMenu write SetPopupMenu;
  167.     property ColorBands: Integer read FColorBands write SetColorBands default 64;
  168.     { Events }
  169.     property OnDrawCaption: TDrawCaptionEvent read FOnDrawCaption write FOnDrawCaption;
  170.     property OnButtonDraw: TCaptionButtonDrawEvent read FOnButtonDraw write FOnButtonDraw;
  171.     property OnButtonClick: TCaptionButtonClickEvent read FOnButtonClick write FOnButtonClick;
  172.   end;
  173.  
  174. procedure Register;
  175.  
  176. implementation
  177.  
  178. uses
  179.   CapEdit;
  180.  
  181. type
  182.   TRGBRec = packed record
  183.     case Integer of
  184.       1: (RGBVal: LongInt);
  185.       0: (Red, Green, Blue, None: Byte);
  186.   end;
  187.  
  188. procedure Register;
  189. begin
  190.   RegisterComponents('Extended', [TCaptionControl]);
  191.   RegisterPropertyEditor(TypeInfo(TCaptionButtonsList), nil, '', TCaptionButtonsListProperty);
  192. end;
  193.  
  194. { TCaptionButton }
  195. constructor TCaptionButton.Create;
  196. begin
  197.   inherited;
  198.   FEnabled := True;
  199.   FVisible := True;
  200.   FPushed := False;
  201.   FKind := cbkCustom;
  202. end;
  203.  
  204. function TCaptionButton.Draw(DC: HDC; Rect: TRect): Boolean;
  205. var
  206.   NCM: TNonClientMetrics;
  207.   WingFont, ButtonFont, OldFont: HFont;
  208.   WingLogFont: TLogFont;
  209.   OldColor: TColorRef;
  210.   OldMode: Integer;
  211.   S: String;
  212.   Brush: HBrush;
  213.   R: TRect;
  214.   Pen, GrayPen: HPen;
  215.   ROffset: Integer;
  216.  
  217. procedure BeginDraw;
  218. begin
  219.   if FEnabled then
  220.   begin
  221.     Pen := SelectObject(DC, GetStockObject(BLACK_PEN));
  222.     Brush := SelectObject(DC, GetStockObject(BLACK_BRUSH));
  223.     GrayPen := 0;
  224.   end else
  225.   begin
  226.     Pen := SelectObject(DC, GetStockObject(WHITE_PEN));
  227.     GrayPen := CreatePen(PS_SOLID, 0, GetSysColor(COLOR_BTNSHADOW));
  228.     Brush := SelectObject(DC, GetStockObject(WHITE_BRUSH));
  229.   end;
  230.   R := Rect;
  231.   ROffset := (Rect.Right - Rect.Left)*17 div 100;
  232.   R.Left := Rect.Left+ROffset;
  233.   R.Top := Rect.Top+ROffset;
  234.   R.Right := Rect.Right-ROffset-1;
  235.   R.Bottom := Rect.Bottom-ROffset-1;
  236. end;
  237.  
  238. procedure EndDraw;
  239. begin
  240.   SelectObject(DC, Pen);
  241.   SelectObject(DC, Brush);
  242.   if GrayPen<>0 then DeleteObject(GrayPen);
  243. end;
  244.  
  245. procedure DrawRoll(Down: Boolean);
  246. begin
  247.   BeginDraw;
  248.   if not(FEnabled) then
  249.   begin
  250.     OffsetRect(R, 1, 1);
  251.     Rectangle(DC, R.Left, R.Top, R.Right, R.Top+ROffset);
  252.     if Down then
  253.       Rectangle(DC, R.Left, R.Bottom-ROffset, R.Right, R.Bottom);
  254.     OffsetRect(R, -1, -1);
  255.     SelectObject(DC, GrayPen);
  256.     SelectObject(DC, GetStockObject(DKGRAY_BRUSH));
  257.   end;
  258.   Rectangle(DC, R.Left, R.Top, R.Right, R.Top+ROffset);
  259.   if Down then
  260.     Rectangle(DC, R.Left, R.Bottom-ROffset, R.Right, R.Bottom);
  261.   EndDraw;
  262. end;
  263.  
  264. procedure DrawOk;
  265. var
  266.   p: array[0..6] of TPoint;
  267.   i: Integer;
  268. begin
  269.   BeginDraw;
  270.   p[0].x := R.Left;
  271.   p[0].y := R.top + (R.Bottom-R.Top) div 2;
  272.   p[1].x := R.Left + (R.Right-R.Left) div 2 - ROffset;
  273.   p[1].y := R.bottom;
  274.   p[2].x := p[1].x + ROffset-1;
  275.   P[2].y := p[1].y;
  276.   p[3].x := R.Right;
  277.   p[3].y := R.top;
  278.   p[4].x := p[3].x-ROffset+1;
  279.   p[4].y := p[3].y;
  280.   p[5].x := p[1].x + (ROffset) div 3;
  281.   p[5].y := p[1].y - ROffset;
  282.   p[6].x := p[0].x + ROffset;
  283.   p[6].y := p[0].y;
  284.   if not(FEnabled) then
  285.   begin
  286.     for i:=0 to 6 do
  287.     begin
  288.       Inc(p[i].x);
  289.       Inc(p[i].y);
  290.     end;
  291.     Polygon(DC, p, 7);
  292.     for i:=0 to 6 do
  293.     begin
  294.       Dec(p[i].x);
  295.       Dec(p[i].y);
  296.     end;
  297.     SelectObject(DC, GrayPen);
  298.     SelectObject(DC, GetStockObject(DKGRAY_BRUSH));
  299.   end;
  300.   Polygon(DC, p, 7);
  301.   EndDraw;
  302. end;
  303.  
  304. procedure DrawWindow;
  305. var
  306.   ColorBrush, SaveBrush: HBrush;
  307. begin
  308.   BeginDraw;
  309.   if not(FEnabled) then
  310.   begin
  311.     OffsetRect(R, 1, 1);
  312.     SelectObject(DC, GetStockObject(LTGRAY_BRUSH));
  313.     Rectangle(DC, R.Left, R.Top, R.Right, R.Bottom);
  314.     Rectangle(DC, R.Left, R.Top, R.Right, R.Top+ROffset+1);
  315.     OffsetRect(R, -1, -1);
  316.     SelectObject(DC, GrayPen);
  317.     Rectangle(DC, R.Left, R.Top, R.Right, R.Bottom);
  318.     SelectObject(DC, GetStockObject(DKGRAY_BRUSH));
  319.     Rectangle(DC, R.Left, R.Top, R.Right, R.Top+ROffset+1);
  320.   end else
  321.   begin
  322.     SelectObject(DC, GetStockObject(BLACK_PEN));
  323.     SelectObject(DC, GetStockObject(WHITE_BRUSH));
  324.     Rectangle(DC, R.Left, R.Top, R.Right, R.Bottom);
  325.     ColorBrush := CreateSolidBrush(GetSysColor(COLOR_ACTIVECAPTION));
  326.     SaveBrush := SelectObject(DC, ColorBrush);
  327.     Rectangle(DC, R.Left, R.Top, R.Right, R.Top+ROffset+1);
  328.     SelectObject(DC, SaveBrush);
  329.     DeleteObject(ColorBrush);
  330.   end;
  331.   EndDraw;
  332. end;
  333.  
  334. begin
  335.   Result := True;
  336.   NCM.cbSize := SizeOf(NCM);
  337.   if SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, @NCM, 0) then
  338.     ButtonFont := CreateFontIndirect(NCM.lfSmCaptionFont)
  339.   else
  340.     ButtonFont := 0;
  341.   FillChar(WingLogFont, SizeOf(WingLogFont), 0);
  342.   with WingLogFont do
  343.   begin
  344.     lfHeight := ((Rect.Top-Rect.Bottom)*31) div 40;
  345.     lfCharSet := SYMBOL_CHARSET;
  346.     lfOutPrecision := OUT_DEFAULT_PRECIS;
  347.     lfClipPrecision := CLIP_DEFAULT_PRECIS;
  348.     lfQuality := DEFAULT_QUALITY;
  349.     lfPitchAndFamily := DEFAULT_PITCH or FF_DONTCARE;
  350.     lfFaceName := 'Wingdings'
  351.   end;
  352.   WingFont := CreateFontIndirect(WingLogFont);
  353.   if (WingFont<>0) and
  354.      (FKind in [cbkOk, cbkDocument, cbkFolder, cbkMail,
  355.                 cbkDownArrow, cbkUpArrow, cbkLeftArrow, cbkRightArrow,
  356.                 cbkFlag, cbkCool]) then
  357.   begin
  358.     if ButtonFont<>0 then
  359.       DeleteObject(ButtonFont);
  360.     ButtonFont := WingFont;
  361.   end;
  362.   if FKind in [cbkRollUp, cbkRollDown, cbkWindow, cbkOk] then
  363.   begin
  364.     if ButtonFont<>0 then
  365.       DeleteObject(ButtonFont);
  366.     Result := True;
  367.     case FKind of
  368.       cbkRollUp: DrawRoll(False);
  369.       cbkRollDown: DrawRoll(True);
  370.       cbkWindow: DrawWindow;
  371.       cbkOk: DrawOk;
  372.     else
  373.       Result := False;
  374.     end;
  375.   end else
  376.   begin
  377.     Result := False;
  378.     case FKind of
  379.       cbkOk: S := #252;
  380.       cbkDocument: S := '2';
  381.       cbkFolder: S := '0';
  382.       cbkMail: S := '+';
  383.       cbkDownArrow: S := #234;
  384.       cbkUpArrow: S := #233;
  385.       cbkLeftArrow: S := #231;
  386.       cbkRightArrow: S := #232;
  387.       cbkMore: S := '...';
  388.       cbkFlag: S := 'O';
  389.       cbkCool: S := 'J';
  390.       cbkCustom: S := FCaption;
  391.     else
  392.       S := ' ';
  393.     end;
  394.     if ButtonFont<>0 then
  395.     begin
  396.       OldFont := SelectObject(DC, ButtonFont);
  397.       OldMode := SetBkMode(DC, TRANSPARENT);
  398.       OldColor := SetTextColor(DC, GetSysColor(COLOR_BTNTEXT));
  399.       if not(FEnabled) then
  400.       begin
  401.         SetTextColor(DC, GetSysColor(COLOR_BTNHILIGHT));
  402.         OffsetRect(Rect, 1, 1);
  403.       end;
  404.       DrawText(DC, PChar(S), -1, Rect,
  405.         DT_CENTER or DT_VCENTER or DT_SINGLELINE);
  406.       if not(FEnabled) then
  407.       begin
  408.         OffsetRect(Rect, -1, -1);
  409.         SetTextColor(DC, GetSysColor(COLOR_BTNSHADOW));
  410.         DrawText(DC, PChar(S), -1, Rect,
  411.           DT_CENTER or DT_VCENTER or DT_SINGLELINE);
  412.       end;
  413.       SetTextColor(DC, OldColor);
  414.       SetBkMode(DC, OldMode);
  415.       SelectObject(DC, OldFont);
  416.       DeleteObject(ButtonFont);
  417.       Result := True;
  418.     end;
  419.   end;
  420. end;
  421.  
  422. function TCaptionButton.GetBtnKindStr: String;
  423. begin
  424.   case FKind of
  425.     cbkCustom: Result := 'cbkCustom';
  426.     cbkOk: Result := 'cbkOk';
  427.     cbkRollUp: Result := 'cbkRollUp';
  428.     cbkRollDown: Result := 'cbkRollDown';
  429.     cbkDocument: Result := 'cbkDocument';
  430.     cbkFolder: Result := 'cbkFolder';
  431.     cbkWindow: Result := 'cbkWindow';
  432.     cbkMail: Result := 'cbkMail';
  433.     cbkDownArrow: Result := 'cbkDownArrow';
  434.     cbkUpArrow: Result := 'cbkUpArrow';
  435.     cbkLeftArrow: Result := 'cbkLeftArrow';
  436.     cbkRightArrow: Result := 'cbkRightArrow';
  437.     cbkMore: Result := 'cbkMore';
  438.     cbkFlag: Result := 'cbkFlag';
  439.     cbkCool: Result := 'cbkCool';
  440.   else
  441.     Result := 'cbkCustom';
  442.   end;
  443. end;
  444.  
  445. procedure TCaptionButton.SetBtnKindStr(KindStr: String);
  446. begin
  447.   if KindStr='cbkCustom' then FKind := cbkCustom else
  448.   if KindStr='cbkOk' then FKind := cbkOk else
  449.   if KindStr='cbkRollUp' then FKind := cbkRollUp else
  450.   if KindStr='cbkRollDown' then FKind := cbkRollDown else
  451.   if KindStr='cbkDocument' then FKind := cbkDocument else
  452.   if KindStr='cbkFolder' then FKind := cbkFolder else
  453.   if KindStr='cbkWindow' then FKind := cbkWindow else
  454.   if KindStr='cbkMail' then FKind := cbkMail else
  455.   if KindStr='cbkDownArrow' then FKind := cbkDownArrow else
  456.   if KindStr='cbkUpArrow' then FKind := cbkUpArrow else
  457.   if KindStr='cbkLeftArrow' then FKind := cbkLeftArrow else
  458.   if KindStr='cbkRightArrow' then FKind := cbkRightArrow else
  459.   if KindStr='cbkMore' then FKind := cbkMore else
  460.   if KindStr='cbkFlag' then FKind := cbkFlag else
  461.   if KindStr='cbkCool' then FKind := cbkCool else
  462.   FKind := cbkCustom;
  463. end;
  464.  
  465. { TCaptionButtonsList }
  466. constructor TCaptionButtonsList.Create(AOwner: TComponent);
  467. begin
  468.   inherited Create;
  469.   FButtonsList := TStringList.Create;
  470. end;
  471.  
  472. destructor TCaptionButtonsList.Destroy;
  473. begin
  474.   Clear;
  475.   FButtonsList.Free;
  476.   inherited;
  477. end;
  478.  
  479. procedure TCaptionButtonsList.SetButton(Index: Integer; Value: TCaptionButton);
  480. begin
  481.   if Index>=FButtonsList.Count then exit;
  482.   TCaptionButton(FButtonsList.Objects[Index]).Free;
  483.   FButtonsList.Objects[Index] := Value;
  484. end;
  485.  
  486. function TCaptionButtonsList.GetButton(Index: Integer): TCaptionButton;
  487. begin
  488.   if Index>=FButtonsList.Count then Result := nil else
  489.     Result := TCaptionButton(FButtonsList.Objects[Index]);
  490. end;
  491.  
  492. function TCaptionButtonsList.GetCount: Integer;
  493. begin
  494.   Result := FButtonsList.Count;
  495. end;
  496.  
  497. procedure TCaptionButtonsList.Add(Button: TCaptionButton);
  498. begin
  499.   FButtonsList.AddObject('', Button);
  500. end;
  501.  
  502. procedure TCaptionButtonsList.Insert(Index: Integer; Button: TCaptionButton);
  503. begin
  504.   if Index<FButtonsList.Count then
  505.     FButtonsList.InsertObject(Index, '', Button);
  506. end;
  507.  
  508. procedure TCaptionButtonsList.Delete(Index: Integer);
  509. begin
  510.   if Index<FButtonsList.Count then
  511.   begin
  512.     if FButtonsList.Objects[Index]<>nil then
  513.       TCaptionButton(FButtonsList.Objects[Index]).Free;
  514.     FButtonsList.Delete(Index);
  515.   end;
  516. end;
  517.  
  518. procedure TCaptionButtonsList.Clear;
  519. begin
  520.   while FButtonsList.Count>0 do
  521.     Delete(0);
  522. end;
  523.  
  524. procedure TCaptionButtonsList.AddButton(Caption: String;
  525.   Enabled, Visible, Pushed: Boolean; Kind: TCaptionButtonKind);
  526. var
  527.   b: TCaptionButton;
  528. begin
  529.   b := TCaptionButton.Create;
  530.   b.Caption := Caption;
  531.   b.Enabled := Enabled;
  532.   b.Visible := Visible;
  533.   b.Pushed := Pushed;
  534.   b.Kind := Kind;
  535.   Add(b);
  536. end;
  537.  
  538. procedure TCaptionButtonsList.DefineProperties(Filer: TFiler);
  539. begin
  540.   inherited;
  541.   Filer.DefineProperty('Buttons', ReadButtons, WriteButtons, Count>0);
  542. end;
  543.  
  544. procedure TCaptionButtonsList.ReadButtons(Reader: TReader);
  545. begin
  546.   Clear;
  547.   Reader.ReadListBegin;
  548.   while not Reader.EndOfList do
  549.   begin
  550.     Add(TCaptionButton.Create);
  551.     with Buttons[Count-1] do
  552.     begin
  553.       Caption := Reader.ReadString;
  554.       Enabled := Reader.ReadBoolean;
  555.       Visible := Reader.ReadBoolean;
  556.       Pushed := Reader.ReadBoolean;
  557.       SetBtnKindStr(Reader.ReadString);
  558.     end;
  559.   end;
  560.   Reader.ReadListEnd;
  561. end;
  562.  
  563. procedure TCaptionButtonsList.WriteButtons(Writer: TWriter);
  564. var
  565.   i: Integer;
  566. begin
  567.   Writer.WriteListBegin;
  568.   if FButtonsList.Count>0 then
  569.     for i:=0 to FButtonsList.Count-1 do
  570.       with FButtonsList.Objects[i] as TCaptionButton do
  571.       begin
  572.         Writer.WriteString(Caption);
  573.         Writer.WriteBoolean(Enabled);
  574.         Writer.WriteBoolean(Visible);
  575.         Writer.WriteBoolean(Pushed);
  576.         Writer.WriteString(GetBtnKindStr);
  577.       end;
  578.   Writer.WriteListEnd;
  579. end;
  580.  
  581. { TCaptionButtonListProperty }
  582. procedure TCaptionButtonsListProperty.Edit;
  583. begin
  584.   if EditCaptionButtons(TCaptionButtonsList(GetOrdValue),
  585.     TCaptionControl(GetComponent(0))) then Modified;
  586. end;
  587.  
  588. function TCaptionButtonsListProperty.GetAttributes: TPropertyAttributes;
  589. begin
  590.   Result := [paDialog, paReadOnly];
  591. end;
  592.  
  593. function TCaptionButtonsListProperty.GetValue: String;
  594. begin
  595.   Result := '(Buttons)';
  596. end;
  597.  
  598. { TCaptionControl }
  599. constructor TCaptionControl.Create(AOwner: TComponent);
  600. var
  601.   NCM: TNonClientMetrics;
  602.   VI: TOSVersionInfo;
  603.   iCount: Integer;
  604. begin
  605.   inherited;
  606.   FButtons := TCaptionButtonsList.Create(Self);
  607.   DrawPushed := False;
  608.   Pushed := -1;
  609.   FRightPushed := False;
  610.   rgn := 0;
  611.   FEnabled := True;
  612.   FColorBands := 64;
  613.   if not (Owner is TForm) then
  614.     raise ECaptionError.Create('Owner must be a form.');
  615.   if TForm(Owner).ComponentCount>0 then
  616.     for iCount := 0 to TForm(Owner).ComponentCount-1 do
  617.       if (TForm(Owner).Components[iCount] is TCaptionControl) and
  618.          (TForm(Owner).Components[iCount]<>Self) then
  619.         raise ECaptionError.Create('Only one TCaptionControl per form is allowed.');
  620.   FillChar(VI, SizeOf(VI), 0);
  621.   VI.dwOSVersionInfoSize := SizeOf(VI);
  622.   GetVersionEx(VI);
  623.   if (VI.dwMajorVersion<4) or (VI.dwPlatformId=VER_PLATFORM_WIN32S) then
  624.     raise ECaptionError.Create('Operating system must be Windows 95/NT 4.0 or greater.');
  625.   FWindowActive := False;
  626.   FMaximized := False;
  627.   FEnabled := True;
  628.   FCaptionDirection := dirLeft;
  629.   FButtonsDirection := dirRight;
  630.   FWindowDirection := dirLeft;
  631.   FRtlReading := False;
  632.   with TForm(Owner) do
  633.   begin
  634.     DefWinProcInstance := MakeObjectInstance(WinProc);
  635.     DefWinProc := Pointer(SetWindowLong(Handle, GWL_WNDPROC, Longint(DefWinProcInstance)));
  636.     FOnFormDestroy := OnDestroy;
  637.     OnDestroy := OnCaptionControlDestroy;
  638.     FCaptionGradient := cgActive;
  639.     CalculateColors;
  640.     NCM.cbSize := SizeOf(NCM);
  641.     if SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, @NCM, 0) then begin
  642.       if BorderStyle in [bsToolWindow, bsSizeToolWin] then
  643.         CaptionFont := CreateFontIndirect(NCM.lfSmCaptionFont)
  644.       else
  645.         CaptionFont := CreateFontIndirect(NCM.lfCaptionFont);
  646.     end else
  647.       CaptionFont := 0;
  648.   end;
  649. end;
  650.  
  651. destructor TCaptionControl.Destroy;
  652. var
  653.   proc: TNotifyEvent;
  654. begin
  655.   try
  656.     if not RestoreWndProc then
  657.     begin
  658.       SetWindowLong(TForm(Owner).Handle, GWL_WNDPROC, Longint(DefWinProc));
  659.       FreeObjectInstance(DefWinProcInstance);
  660.       RestoreWndProc := True;
  661.     end;
  662.     proc := OnCaptionControlDestroy;
  663.     if Assigned(Owner) and (@proc = @TForm(Owner).OnDestroy) then
  664.       TForm(Owner).OnDestroy := FOnFormDestroy;
  665.   finally
  666.     if rgn <> 0 then
  667.       DeleteObject( rgn );
  668.     if CaptionFont <> 0 then
  669.       DeleteObject(CaptionFont);
  670.     FButtons.Free;
  671.     inherited;
  672.   end;
  673. end;
  674.  
  675. procedure TCaptionControl.OnCaptionControlDestroy(Sender: TObject);
  676. begin
  677.   try
  678.     if not RestoreWndProc then
  679.     begin
  680.       SetWindowLong(TForm(Owner).Handle, GWL_WNDPROC, Longint(DefWinProc));
  681.       FreeObjectInstance(DefWinProcInstance);
  682.       RestoreWndProc := True;
  683.     end;
  684.     if Assigned(FOnFormDestroy) then
  685.       FOnFormDestroy(Sender);
  686.   except end;
  687. end;
  688.  
  689. procedure TCaptionControl.SetShowButtons(Value: Boolean);
  690. begin
  691.   if Value <> FShowButtons then
  692.   begin
  693.     FShowButtons := Value;
  694.     Refresh;
  695.   end;
  696. end;
  697.  
  698. procedure TCaptionControl.WinProc(var Message: TMessage);
  699. var
  700.   DC: HDC;
  701.   WR, R: TRect;
  702.   MyRgn: HRGN;
  703.   DeleteRgn: boolean;
  704.   PushState: Boolean;
  705.  
  706.   procedure DefaultProc;
  707.   begin
  708.     with Message do
  709.       Result := CallWindowProc(DefWinProc, TForm(Owner).Handle, Msg, wParam, lParam);
  710.   end;
  711.  
  712.   function InButton(InClient: Boolean): Boolean;
  713.   var
  714.     p: TPoint;
  715.   begin
  716.     p.x := Message.lParamLo;
  717.     p.y := Smallint(Message.lParamHi);
  718.     if InClient then
  719.       ClientToScreen(TForm(Owner).Handle, p);
  720.     Dec(p.x, TForm(Owner).Left);
  721.     Dec(p.y, TForm(Owner).Top);
  722.     Result := Pushed=GetCoordButton(p);
  723.   end;
  724.  
  725.   function InAnyButton(InClient: Boolean): Boolean;
  726.   var
  727.     p: TPoint;
  728.   begin
  729.     p.x := Message.lParamLo;
  730.     p.y := Smallint(Message.lParamHi);
  731.     if InClient then
  732.       ClientToScreen(TForm(Owner).Handle, p);
  733.     Dec(p.x, TForm(Owner).Left);
  734.     Dec(p.y, TForm(Owner).Top);
  735.     Pushed := GetCoordButton(p);
  736.     Result := Pushed>=0;
  737.   end;
  738.  
  739.   procedure ShowPopup(InClient: Boolean);
  740.   var
  741.     sp: TSmallPoint;
  742.     p: Tpoint;
  743.   begin
  744.     sp := TWMMouse(Message).Pos;
  745.     p.x := sp.x;
  746.     p.y := sp.y;
  747.     if InClient then
  748.       ClientToScreen(TForm(Owner).Handle, p);
  749.     FPopupMenu.Popup(p.x, p.y);
  750.   end;
  751.  
  752. begin
  753.   with Message do
  754.     case Msg of
  755.       WM_NCACTIVATE:
  756.       begin
  757.         FWindowActive := (Message.wParam<>0);
  758.         DefaultProc;
  759.         if not(Enabled) then Exit;
  760.         DC := GetWindowDC(TForm(Owner).Handle);
  761.         try
  762.           DrawAllCaption(DC);
  763.         except end;
  764.         ReleaseDC(TForm(Owner).Handle, DC);
  765.       end;
  766.       WM_NCPAINT:
  767.       begin
  768.         if not(Enabled) then
  769.         begin
  770.           DefaultProc;
  771.           Exit;
  772.         end;
  773.         DeleteRgn := FALSE;
  774.         MyRgn := Message.wParam;
  775.         DC := GetWindowDC(TForm(Owner).Handle);
  776.         try
  777.           GetWindowRect(TForm(Owner).Handle, WR);
  778.           if SelectClipRgn(DC, MyRgn) = ERROR then
  779.           begin
  780.              with WR do
  781.                MyRgn := CreateRectRgn(Left, Top, Right, Bottom);
  782.              SelectClipRgn(DC, MyRgn);
  783.              DeleteRgn := TRUE;
  784.           end;
  785.           OffsetClipRgn(DC, -WR.Left, -WR.Top);
  786.           R := DrawAllCaption(DC);
  787.           ExcludeClipRect(DC, R.Left, R.Top, R.Right, R.Bottom);
  788.           OffsetClipRgn(DC, WR.Left, WR.Top);
  789.           GetClipRgn(DC, MyRgn);
  790.           with Message do
  791.             Result := CallWindowProc(DefWinProc, TForm(Owner).Handle, Msg, MyRgn, lParam);
  792.         finally
  793.           if DeleteRgn then
  794.             DeleteObject(MyRgn);
  795.           ReleaseDC(TForm(Owner).Handle, DC); 
  796.         end;
  797.       end;
  798.       WM_SIZE:
  799.       begin
  800.         FMaximized := (wParam=SIZE_MAXIMIZED);
  801.         DefaultProc;
  802.         if not(Enabled) then Exit;
  803.         // Redraw to set proper maximize/restore icon
  804.         DC := GetWindowDC(TForm(Owner).Handle);
  805.         try
  806.           DrawAllCaption(DC);
  807.         except end;
  808.         ReleaseDC(TForm(Owner).Handle, DC);
  809.       end;
  810.       WM_MOUSEMOVE:
  811.       begin
  812.         if not(Enabled) then
  813.         begin
  814.           DefaultProc;
  815.           Exit;
  816.         end;
  817.         if Pushed>=0 then
  818.         begin
  819.           if not InButton(True) then
  820.           begin
  821.             if DrawPushed then
  822.             begin
  823.               DrawPushed := False;
  824.               Refresh;
  825.             end;
  826.           end
  827.           else
  828.           begin
  829.             if not DrawPushed then
  830.             begin
  831.               DrawPushed := True;
  832.               Refresh;
  833.             end;
  834.           end;
  835.           Result := 1;
  836.         end
  837.         else
  838.           DefaultProc;
  839.       end;
  840.       WM_LBUTTONUP, WM_LBUTTONDBLCLK:
  841.       begin
  842.         if not(Enabled) then
  843.         begin
  844.           DefaultProc;
  845.           Exit;
  846.         end;
  847.         DrawPushed := False;
  848.         if Pushed>=0 then
  849.         begin
  850.           if InButton(True) then
  851.           begin
  852.             PushState := Buttons[Pushed].Pushed;
  853.             if Assigned(FOnButtonClick) then
  854.               FOnButtonClick(Self, Pushed, PushState);
  855.             Buttons[Pushed].Pushed := PushState;
  856.           end;
  857.           Refresh;
  858.           Result := 1;
  859.         end
  860.         else
  861.           DefaultProc;
  862.         Pushed := -1;
  863.         ReleaseCapture;
  864.       end;
  865.       WM_NCLBUTTONDOWN, WM_NCLBUTTONDBLCLK:
  866.       begin
  867.         if not(Enabled) then
  868.         begin
  869.           DefaultProc;
  870.           Exit;
  871.         end;
  872.         if InAnyButton(False) then
  873.         begin
  874.           SetCapture(TForm(Owner).Handle);
  875.           if (not(Buttons[Pushed].Enabled)) or (Buttons[Pushed].Caption='-') then DrawPushed := True;
  876.           Refresh;
  877.           Result := 1;
  878.         end
  879.         else if Msg = WM_NCLBUTTONDBLCLK then
  880.           DefaultProc;
  881.         if Msg = WM_NCLBUTTONDOWN then
  882.           DefaultProc;
  883.       end;
  884.       WM_RBUTTONUP, WM_RBUTTONDBLCLK:
  885.       begin
  886.         if not(Enabled) then
  887.         begin
  888.           DefaultProc;
  889.           Exit;
  890.         end;
  891.         if FRightPushed and Assigned(FPopupMenu) and (FPopupMenu.AutoPopup) then
  892.         begin
  893.           ShowPopup(True);
  894.           Result := 1;
  895.         end else
  896.           DefaultProc;
  897.         FRightPushed := False;
  898.         ReleaseCapture;
  899.       end;
  900.       WM_NCRBUTTONDOWN:
  901.       begin
  902.         if not(Enabled) then DefaultProc else
  903.         begin
  904.           SetCapture(TForm(Owner).Handle);
  905.           FRightPushed := True;
  906.           Result := 1;
  907.         end;
  908.       end;
  909.       WM_SYSCOLORCHANGE:
  910.       begin
  911.         CalculateColors;
  912.         DefaultProc;
  913.       end;
  914.       WM_SETTEXT:
  915.       begin
  916.         DefaultProc;
  917.         Refresh;
  918.       end;
  919.       // magic number
  920.       $003F:
  921.       begin
  922.         DefaultProc;
  923.         Refresh;
  924.       end;
  925.       else
  926.         DefaultProc;
  927.     end;
  928. end;
  929.  
  930. procedure TCaptionControl.SetCaptionGradient(Value: TCaptionGradient);
  931. begin
  932.   if FCaptionGradient = Value then exit;
  933.   FCaptionGradient := Value;
  934.   Refresh;
  935. end;
  936.  
  937. procedure TCaptionControl.CalculateColors;
  938. var
  939.   SysColor: TRGBRec;
  940.   RedPercent,
  941.   GreenPercent,
  942.   BluePercent: Extended;
  943.   x, Band: Byte;
  944. begin
  945.   for x := 0 to 1 do begin
  946.     if x = 0 then
  947.       SysColor.RGBVal := GetSysColor(COLOR_INACTIVECAPTION)
  948.     else
  949.       SysColor.RGBVal := GetSysColor(COLOR_ACTIVECAPTION);
  950.     with SysColor do begin
  951.       RedPercent   := Red / (FColorBands-1);
  952.       GreenPercent := Green / (FColorBands-1);
  953.       BluePercent  := Blue / (FColorBands-1);
  954.     end;
  955.     for Band := 0 to FColorBands-1 do
  956.       Colors[x][Band] := RGB(round(RedPercent * (Band)),
  957.                              round(GreenPercent * (Band)),
  958.                              round(BluePercent * (Band)));
  959.   end;
  960. end;
  961.  
  962. function TCaptionControl.GetCaptionRect: TRect;
  963. begin
  964.   with TForm(Owner) do
  965.   begin
  966.     if BorderStyle = bsNone then
  967.       SetRectEmpty(Result)
  968.     else begin
  969.       GetWindowRect(Handle, Result);
  970.       OffsetRect(Result, -Result.Left, -Result.Top);
  971.       case BorderStyle of
  972.         bsToolWindow, bsSingle, bsDialog:
  973.             InflateRect(Result, -GetSystemMetrics(SM_CXFIXEDFRAME),
  974.                                 -GetSystemMetrics(SM_CYFIXEDFRAME));
  975.         bsSizeable, bsSizeToolWin:
  976.             InflateRect(Result, -GetSystemMetrics(SM_CXSIZEFRAME),
  977.                                 -GetSystemMetrics(SM_CYSIZEFRAME));
  978.       end;
  979.       if BorderStyle in [bsToolWindow, bsSizeToolWin] then
  980.         Result.Bottom := Result.Top + GetSystemMetrics(SM_CYSMCAPTION) - 1
  981.       else
  982.         Result.Bottom := Result.Top + GetSystemMetrics(SM_CYCAPTION) - 1;
  983.     end;
  984.   end; {with}
  985. end;
  986.  
  987. // Paint the icon for the system menu
  988. procedure TCaptionControl.DrawMenuIcon(DC: HDC; var R: TRect);
  989. var
  990.   Size: Integer;
  991.   TempBmp: TBitmap;
  992. begin
  993.   // Draw system icon by using Windows' DrawCaption function
  994.   // Original source code contributed by Rolf Frei
  995.   with R do
  996.   begin
  997.     Size := Bottom-Top;
  998.     // Drawing is done on a Delphi bitmap.
  999.     TempBmp := TBitmap.Create;
  1000.     try
  1001.       with TempBmp do
  1002.       begin
  1003.         Width := Size;
  1004.         Height := Size;
  1005.         if (FCaptionGradient=cgNone) then
  1006.         begin
  1007.           if FWindowActive then
  1008.             Canvas.Brush.Color := GetSysColor(COLOR_ACTIVECAPTION) else
  1009.             Canvas.Brush.Color := GetSysColor(COLOR_INACTIVECAPTION);
  1010.         end else
  1011.           Canvas.Brush.Color := clBlack;
  1012.         DrawCaption(TForm(Owner).Handle, Canvas.Handle, R, DC_ICON);
  1013.         if not((FCaptionGradient=cgActive) and (not(FWindowActive))) then
  1014.           Canvas.BrushCopy(Canvas.ClipRect, TempBmp, Canvas.Cliprect, clInactiveCaption);
  1015.       end;
  1016.       BitBlt(DC, Left-2, Top, Size, Size, TempBmp.Canvas.Handle, 0, 0, SRCCOPY);
  1017.     finally
  1018.       TempBmp.Free;
  1019.     end;
  1020.     Inc(R.Left, Size-1);
  1021.   end;
  1022. end;
  1023.  
  1024. // Paint the given rectangle with the gradient pattern.
  1025. procedure TCaptionControl.FillRectGradient(DC: HDC; const R: TRect; Active: boolean);
  1026. var
  1027.   OldBrush,
  1028.   Brush: HBrush;
  1029.   Step: real;
  1030.   Band: integer;
  1031.   H: integer;
  1032. begin
  1033.   // Determine how large each band should be in order to cover the
  1034.   // rectangle (one band for every color intensity level).
  1035.   Step := (R.Right - R.Left) / FColorBands;
  1036.   H := R.Bottom - R.Top;
  1037.   // Start filling bands
  1038.   for Band := 0 to FColorBands-1 do begin
  1039.     // Create a brush with the appropriate color for this band
  1040.     Brush := CreateSolidBrush(Colors[ord(Active)][Band]);
  1041.     // Select that brush into the temporary DC.
  1042.     OldBrush := SelectObject(DC, Brush);
  1043.     try
  1044.       // Fill the rectangle using the selected brush -- PatBlt is faster than FillRect
  1045.       PatBlt(DC, round(Band*Step), 0, round((Band+1)*Step), H, PATCOPY);
  1046.     finally
  1047.       // Clean up the brush
  1048.       SelectObject(DC, OldBrush);
  1049.       DeleteObject(Brush);
  1050.     end;
  1051.   end; // for
  1052. end;
  1053.  
  1054. // Paint the given rectangle with the caption color
  1055. procedure TCaptionControl.FillRectCaption(DC: HDC; const R: TRect; Active: boolean);
  1056. var
  1057.   OldBrush,
  1058.   Brush: HBrush;
  1059. begin
  1060.   if Active then
  1061.     Brush := CreateSolidBrush(GetSysColor(COLOR_ACTIVECAPTION))
  1062.   else
  1063.     Brush := CreateSolidBrush(GetSysColor(COLOR_INACTIVECAPTION));
  1064.   OldBrush := SelectObject(DC, Brush);
  1065.   PatBlt(DC, R.Left, 0, R.Right, R.Bottom-R.top, PATCOPY);
  1066.   SelectObject(DC, OldBrush);
  1067.   DeleteObject(Brush);
  1068. end;
  1069.  
  1070. procedure TCaptionControl.DrawCaptionText(DC: HDC; R: TRect);
  1071. var
  1072.   OldColor: TColorRef;
  1073.   OldMode: integer;
  1074.   OldFont: HFont;
  1075.   FmtOpt: LongInt;
  1076.   Drawn: Boolean;
  1077.   Text: String;
  1078. begin
  1079.   with TForm(Owner) do
  1080.   begin
  1081.     Inc(R.Left, 2);
  1082.     // text color should be white ONLY when there's a gradient 
  1083.     if (FCaptionGradient=cgNone) then
  1084.     begin
  1085.       if FWindowActive then
  1086.         OldColor := SetTextColor(DC, GetSysColor(COLOR_CAPTIONTEXT)) else
  1087.         OldColor := SetTextColor(DC, GetSysColor(COLOR_INACTIVECAPTIONTEXT));
  1088.     end else
  1089.     if (FCaptionGradient=cgActive) and (not(FWindowActive)) then
  1090.       OldColor := SetTextColor(DC, GetSysColor(COLOR_INACTIVECAPTIONTEXT)) else
  1091.       OldColor := SetTextColor(DC, RGB(255,255,255));
  1092.     OldMode := SetBkMode(DC, TRANSPARENT);
  1093.     // Select in the system defined caption font (see Create constructor).
  1094.     if CaptionFont <> 0 then
  1095.       OldFont := SelectObject(DC, CaptionFont)
  1096.     else
  1097.       OldFont := 0;
  1098.     try
  1099.       if FCaptionDirection=dirLeft then
  1100.         FmtOpt := DT_LEFT else
  1101.         FmtOpt := DT_RIGHT;
  1102.       if FRtlReading then FmtOpt := FmtOpt or DT_RTLREADING;
  1103.       // Draw the text making it centered vertically, allowing no line breaks.
  1104.       Text := Caption;
  1105.       if Assigned(FOnDrawCaption) then
  1106.       begin
  1107.         Drawn := False;
  1108.         FOnDrawCaption(Self, Text, DC, R, Drawn);
  1109.       end;
  1110.       if not(Drawn) then
  1111.         DrawText(DC, PChar(Text), -1, R,
  1112.           FmtOpt or DT_VCENTER or DT_SINGLELINE or DT_END_ELLIPSIS or DT_MODIFYSTRING);
  1113.     finally
  1114.       // Clean up all the drawing objects.
  1115.       if OldFont <> 0 then
  1116.         SelectObject(DC, OldFont);
  1117.       SetBkMode(DC, OldMode);
  1118.       SetTextColor(DC, OldColor);
  1119.     end;
  1120.   end;
  1121. end;
  1122.  
  1123. procedure TCaptionControl.DrawCaptionButtons(DC: HDC; var R: TRect);
  1124. var
  1125.   Flag: UINT;
  1126.   TempR: TRect;
  1127.   i: Integer;
  1128.   Style: LongInt;
  1129.   Drawn: Boolean;
  1130.   SendR: TRect;
  1131. begin
  1132.   TempR := R;
  1133.   with TForm(Owner) do
  1134.   begin
  1135.     InflateRect(TempR, -2, -2);
  1136.     if BorderStyle in [bsToolWindow, bsSizeToolWin] then begin
  1137.       // Tool windows only have the close button, nothing else.
  1138.       TempR.Left := TempR.Right - GetSystemMetrics(SM_CXSMSIZE) + 2;
  1139.       DrawFrameControl(DC, TempR, DFC_CAPTION, DFCS_CAPTIONCLOSE);
  1140.       Dec(R.Right, R.Right-TempR.Left+2);
  1141.     end else begin
  1142.       { Apparent Window 95 bug - SM_CXSMSIZE and SM_CYSMSIZE always return
  1143.         15 - even after size change. We're using the icon's size instead.
  1144.         The old line read:
  1145.        BtnWidth := GetSystemMetrics(SM_CXSMSIZE)-1;}
  1146.       BtnWidth := GetSystemMetrics(SM_CXSMICON)-2;
  1147.       TempR.Left := TempR.Right - BtnWidth - 2;
  1148.       // if it has system menu, it has a close button.
  1149.       if biSystemMenu in BorderIcons then begin
  1150.         DrawFrameControl(DC, TempR, DFC_CAPTION, DFCS_CAPTIONCLOSE);
  1151.       end;
  1152.       // Minimize and Maximized don't show up at all if BorderStyle is bsDialog
  1153.       if BorderStyle <> bsDialog then begin
  1154.         if (biSystemMenu in BorderIcons) and
  1155.           ((biMaximize in BorderIcons) or (biMinimize in BorderIcons)) then
  1156.         begin
  1157.           if biSystemMenu in BorderIcons then OffsetRect(TempR, -BtnWidth-4, 0);
  1158.           if FMaximized then
  1159.             Flag := DFCS_CAPTIONRESTORE else
  1160.             Flag := DFCS_CAPTIONMAX;
  1161.           if not (biMaximize in BorderIcons) then
  1162.             Flag := Flag or DFCS_INACTIVE;
  1163.           DrawFrameControl(DC, TempR, DFC_CAPTION, Flag);
  1164.           OffsetRect(TempR, -BtnWidth-2, 0);
  1165.  
  1166.           Flag := DFCS_CAPTIONMIN;
  1167.           if not (biMinimize in BorderIcons) then
  1168.             Flag := Flag or DFCS_INACTIVE;
  1169.           DrawFrameControl(DC, TempR, DFC_CAPTION, Flag);
  1170.         end;
  1171.       end;
  1172.       // Help appears only if no Min/Max buttons appear
  1173.       if ((GetWindowLong(Handle, GWL_EXSTYLE) and WS_EX_CONTEXTHELP)<>0) and
  1174.          ((GetWindowLong(Handle, GWL_STYLE) and (WS_MINIMIZEBOX or WS_MAXIMIZEBOX))=0) then
  1175.       begin
  1176.         if biSystemMenu in BorderIcons then OffsetRect(TempR, -BtnWidth-4, 0);
  1177.         DrawFrameControl(DC, TempR, DFC_CAPTION, DFCS_CAPTIONHELP);
  1178.       end;
  1179.       if biSystemMenu in BorderIcons then
  1180.         Dec(R.Right, R.Right-TempR.Left+2);
  1181.       if (FShowButtons) and (Buttons.Count>0) then
  1182.       begin
  1183.         if FButtonsDirection=dirRight then
  1184.         begin
  1185.           for i:=0 to Buttons.Count-1 do
  1186.             if Buttons[i].Caption='-' then
  1187.               Dec(TempR.Left, 4) else
  1188.               Dec(TempR.Left, BtnWidth+2);
  1189.           R.Right := TempR.Left - 2;
  1190.         end else
  1191.         begin
  1192.           TempR := R;
  1193.           InflateRect(TempR, -2, -2);
  1194.         end;
  1195.         FButtonsLeft := TempR.Left;
  1196.         TempR.Right := TempR.Left + BtnWidth + 2;
  1197.         for i:=0 to Buttons.Count-1 do
  1198.         begin
  1199.           Style := DFCS_BUTTONPUSH;
  1200.           if (Buttons[i].Pushed) or
  1201.              ((Pushed=i) and (DrawPushed) and (Buttons[i].Enabled)) then
  1202.             Style := Style or DFCS_PUSHED;
  1203.           if Buttons[i].Caption<>'-' then
  1204.           begin
  1205.             if Buttons[i].Visible then
  1206.             begin
  1207.               DrawFrameControl(DC, TempR, DFC_BUTTON, Style);
  1208.               Drawn := False;
  1209.               SendR := TempR;
  1210.               if (Buttons[i].Pushed) or
  1211.                  ((Pushed=i) and (DrawPushed) and (Buttons[i].Enabled)) then
  1212.               begin
  1213.                 Inc(SendR.Left, 2);
  1214.                 Inc(SendR.Top, 2);
  1215.               end;
  1216.               if Assigned(FOnButtonDraw) then
  1217.                 FOnButtonDraw(Self, i, DC, SendR, Drawn);
  1218.               if not(Drawn) then
  1219.                 Buttons[i].Draw(DC, SendR);
  1220.             end;
  1221.             if i<Buttons.Count-1 then
  1222.               OffsetRect(TempR, BtnWidth+2, 0);
  1223.           end else
  1224.           begin
  1225.             if i<Buttons.Count-1 then
  1226.               OffsetRect(TempR, 2, 0);
  1227.           end;
  1228.         end;
  1229.         if FButtonsDirection=dirLeft then
  1230.           Inc(R.Left, TempR.Right-R.Left+2);
  1231.       end;
  1232.     end;
  1233.   end;
  1234. end;
  1235.  
  1236. function TCaptionControl.DrawAllCaption(FormDC: HDC): TRect;
  1237. var
  1238.   R: TRect;
  1239.   OldBmp,
  1240.   Bmp: HBitmap;
  1241.   BmpDC: HDC;
  1242.   W, H: Integer;
  1243. begin
  1244.   with TForm(Owner) do
  1245.   begin
  1246.     R := GetCaptionRect;
  1247.     Result := R;
  1248.     OffsetRect(R, -R.Left, -R.Top);
  1249.     W := R.Right - R.Left;
  1250.     H := R.Bottom - R.Top;
  1251.     { Create a temporary device context to draw on }
  1252.     BmpDC := CreateCompatibleDC(FormDC);
  1253.     Bmp := CreateCompatibleBitmap(FormDC, W, H);
  1254.     OldBmp := SelectObject(BmpDC, Bmp);
  1255.     try
  1256.       if (FCaptionGradient=cgNone) or
  1257.         ((FCaptionGradient=cgActive) and (not(FWindowActive))) then
  1258.         FillRectCaption(BmpDC, R, FWindowActive)
  1259.       else
  1260.         FillRectGradient(BmpDC, R, FWindowActive);
  1261.       Inc(R.Left, 1);
  1262.       if (biSystemMenu in BorderIcons) and (BorderStyle in [bsSingle, bsSizeable]) then
  1263.         DrawMenuIcon(BmpDC, R);
  1264.       DrawCaptionButtons(BmpDC, R);
  1265.       DrawCaptionText(BmpDC, R);
  1266.       BitBlt(FormDC, Result.Left, Result.Top, W, H, BmpDC, 0, 0, SRCCOPY);
  1267.     finally
  1268.       SelectObject(BmpDC, OldBmp);
  1269.       DeleteObject(Bmp);
  1270.       DeleteDC(BmpDC);
  1271.     end;
  1272.   end;
  1273. end;
  1274.  
  1275. procedure TCaptionControl.SetCaptionDirection(Value: TDirection);
  1276. var
  1277.   l: LongInt;
  1278. begin
  1279.   if FCaptionDirection<>Value then
  1280.   begin
  1281.     FCaptionDirection := Value;
  1282.     with Owner as TForm do
  1283.     begin
  1284.       l:=GetWindowLong(Handle, GWL_EXSTYLE);
  1285.       if FCaptionDirection = dirRight then
  1286.         l:=(l or WS_EX_RIGHT) else
  1287.         l := l and (not(WS_EX_RIGHT));
  1288.       SetWindowLong(Handle, GWL_EXSTYLE, l);
  1289.     end;
  1290.   end;
  1291. end;
  1292.  
  1293. procedure TCaptionControl.SetButtonsDirection(Value: TDirection);
  1294. begin
  1295.   if FButtonsDirection<>Value then
  1296.   begin
  1297.     FButtonsDirection := Value;
  1298.     Refresh;
  1299.   end;
  1300. end;
  1301.  
  1302. procedure TCaptionControl.SetWindowDirection(Value: TDirection);
  1303. var
  1304.   l: LongInt;
  1305. begin
  1306.   if GetSystemMetrics(SM_MIDEASTENABLED)=0 then
  1307.   begin
  1308.     if FWindowDirection=dirLeft then Exit;
  1309.     FWindowDirection := dirLeft;
  1310.     Refresh;
  1311.   end;
  1312.   if FWindowDirection<>Value then
  1313.   begin
  1314.     FWindowDirection := Value;
  1315.     with Owner as TForm do
  1316.     begin
  1317.       l:=GetWindowLong(Handle, GWL_EXSTYLE);
  1318.       if FWindowDirection = dirRight then
  1319.         l:=(l or WS_EX_LEFTSCROLLBAR) else
  1320.         l := l and (not(WS_EX_LEFTSCROLLBAR));
  1321.       SetWindowLong(Handle, GWL_EXSTYLE, l);
  1322.     end;
  1323.   end;
  1324. end;
  1325.  
  1326. procedure TCaptionControl.SetRtlReading(Value: Boolean);
  1327. var
  1328.   l: LongInt;
  1329. begin
  1330.   if GetSystemMetrics(SM_MIDEASTENABLED)=0 then
  1331.   begin
  1332.     if not(FRtlReading) then Exit;
  1333.     FRtlReading := False;
  1334.     Refresh;
  1335.   end;
  1336.   if FRtlReading<>Value then
  1337.   begin
  1338.     FRtlReading := Value;
  1339.     with Owner as TForm do
  1340.     begin
  1341.       l:=GetWindowLong(Handle, GWL_EXSTYLE);
  1342.       if FRtlReading=True then
  1343.         l:=(l or WS_EX_RTLREADING) else
  1344.         l := l and (not (WS_EX_RTLREADING));
  1345.       SetWindowLong(Handle, GWL_EXSTYLE, l);
  1346.     end;
  1347.   end;
  1348. end;
  1349.  
  1350. procedure TCaptionControl.SetEnabled(Value: Boolean);
  1351. begin
  1352.   if FEnabled<>Value then
  1353.   begin
  1354.     FEnabled := Value;
  1355.     Refresh;
  1356.   end;
  1357. end;
  1358.  
  1359. procedure TCaptionControl.SetColorBands(Value: Integer);
  1360. begin
  1361.   if (FColorBands<>Value) and (Value>=8) and (Value<=255) then
  1362.   begin
  1363.     FColorBands := Value;
  1364.     CalculateColors;
  1365.     Refresh;
  1366.   end;
  1367. end;
  1368.  
  1369. procedure TCaptionControl.SetPopupMenu(Value: TPopupMenu);
  1370. begin
  1371.   if Value<>FPopupMenu then
  1372.     FPopupMenu := Value;
  1373. end;
  1374.  
  1375. procedure TCaptionControl.Refresh;
  1376. begin
  1377.   SetWindowPos(TForm(Owner).Handle, 0, 0, 0, 0, 0,
  1378.     SWP_DRAWFRAME or SWP_NOACTIVATE or SWP_NOMOVE or SWP_NOSIZE or SWP_NOZORDER);
  1379. end;
  1380.  
  1381. function TCaptionControl.GetCoordButton(Point: TPoint): Integer;
  1382. var
  1383.   i: Integer;
  1384.   R: TRect;
  1385.   RealR: TRect;
  1386. begin
  1387.   Result := -1;
  1388.   if Buttons.Count=0 then Exit;
  1389.   R := GetCaptionRect;
  1390.   R.Left := FButtonsLeft + 2;
  1391.  
  1392.   R.Right := R.Left + BtnWidth + 2;
  1393.   RealR := R;
  1394.   i:=0;
  1395.   if (Buttons[i].Caption='-') or
  1396.      (not(Buttons[i].Visible)) then
  1397.   begin
  1398.     RealR.Left := Point.X+1;
  1399.     RealR.Top := Point.Y+1;
  1400.     RealR.Right := RealR.Left+1;
  1401.     RealR.Bottom := RealR.Top+1;
  1402.   end;
  1403.   while (i<Buttons.Count) and (not(PtInRect(RealR, Point))) do
  1404.   begin
  1405.     Inc(i);
  1406.     if i=Buttons.Count then break;
  1407.     if Buttons[i].Caption='-' then
  1408.       OffsetRect(R, 2, 0) else
  1409.       OffsetRect(R, BtnWidth+2, 0);
  1410.     RealR := R;
  1411.     if (Buttons[i].Caption='-') or
  1412.        (not(Buttons[i].Visible)) then
  1413.     begin
  1414.       RealR.Left := Point.X+1;
  1415.       RealR.Top := Point.Y+1;
  1416.       RealR.Right := RealR.Left+1;
  1417.       RealR.Bottom := RealR.Top+1;
  1418.     end;
  1419.   end;
  1420.   if i<Buttons.Count then Result := i;
  1421. end;
  1422.  
  1423. end.
  1424.