home *** CD-ROM | disk | FTP | other *** search
/ Chip: Shareware for Win 95 / Chip-Shareware-Win95.bin / ostatni / delphi / delphi1 / form3d.exe / FORM3D.PAS < prev    next >
Pascal/Delphi Source File  |  1995-09-13  |  18KB  |  559 lines

  1. unit Form3d;
  2.  
  3. {
  4.   TForm3D Class
  5.   Copyright ⌐ 1995  Alan Ciemian  All Rights Reserved
  6.  
  7.   The TForm3D class is a descendant of TForm that provides
  8.     3D borders for non-dialog forms. Also allows form sizing to be
  9.     enabled/disabled.
  10.  
  11.   NOTES:
  12.       - Requires that form have bsSizeable border style.
  13.       - Sizing can be enabled/disabled with AllowResize property.
  14.       - Handles all Title bar icon combinations.
  15.       - Handles forms with or without menus(including multiline).
  16.       - Handles all combinations of scroll bars.
  17.       - DOES NOT work properly for MDI Frame or MDI Child windows.
  18.  
  19.   05/01/95 - Initial Release (000)
  20.  
  21.   05/16/95 - Release (001)
  22.            - Added FEnable3D field to store whether 3D drawing should be used
  23.                or not depending on running Windows version.
  24.            - Added check for iconic state before performing 3D drawing.
  25.            - Modified caption drawing to left align caption if it is too wide
  26.                for the available area, ala windows.
  27.  
  28.   07/19/95 - Release (002)
  29.            - Added WM_SetText handler to correct redraw problem on forms with
  30.                no menu when caption is changed.
  31. }
  32.  
  33.  
  34. interface
  35.  
  36. uses
  37.   Messages, WinTypes,
  38.   Classes, Controls, Forms;
  39.  
  40.  
  41. const
  42.   CaptionH_STD = 20;
  43.   MenuH_STD    = 18;
  44.  
  45.  
  46. type
  47.   TForm3D_NCPaintMode =
  48.     (
  49.     NCPaint_All,
  50.     NCPaint_Activate,
  51.     NCPaint_Deactivate
  52.     );
  53.  
  54. type
  55.   TForm3D = class(TForm)
  56.   private
  57.     FEnable3D     : Boolean;  { Flag to identify if can use 3D effects }
  58.     FSysMenuW     : Integer;  { Width of system menu,     0 if no sysmenu }
  59.     FMinMaxW      : Integer;  { Width of min/max buttons, 0 if no min/max btns }
  60.     FAllowResize  : Boolean;
  61.     { Private procedures }
  62.     procedure NCPaint3D(const Mode: TForm3D_NCPaintMode);
  63.     procedure ComputeNonClientDimensions;
  64.     function ScrollBarVisible
  65.       (
  66.       const Code    : Word;  { SB_VERT or SB_HORZ }
  67.       const WndRect : TRect
  68.       ): Boolean;
  69.     { Message Handlers }
  70.     procedure WMNCHitTest (var Msg: TWMNCHitTest);  message WM_NCHitTest;
  71.     procedure WMSetText   (var Msg: TWMSetText);    message WM_SetText;
  72.     procedure WMNCPaint   (var Msg: TWMNCPaint);    message WM_NCPaint;
  73.     procedure WMNCActivate(var Msg: TWMNCActivate); message WM_NCActivate;
  74.   protected
  75.     procedure CreateWnd; override;
  76.   public
  77.     constructor Create(AOwner: TComponent); override;
  78.     { Properties }
  79.     property AllowResize: Boolean
  80.              read FAllowResize
  81.              write FAllowResize
  82.              default False;
  83.   end;
  84.  
  85.  
  86. implementation
  87.  
  88.  
  89. uses
  90.   WinProcs,
  91.   SysUtils, Graphics,
  92.   SysMet;
  93.  
  94.  
  95. function TForm3D.ScrollBarVisible
  96.   (
  97.   const Code    : Word;  { SB_VERT or SB_HORZ }
  98.   const WndRect : TRect
  99.   ): Boolean;
  100. var
  101.   PtInScroll : TPoint;
  102.   HVis       : Boolean;
  103. begin
  104.   Result := False;
  105.  
  106.   with  WndRect, SysMetrics  do
  107.     begin
  108.     { Determine if Horz scroll bar is visible. Need this for both horz and }
  109.     {   vert scroll bars. }
  110.     { Two checks need to be satisfied, Style identifies scroll bar and }
  111.     {   windows recognizes HitTest in scroll bar. }
  112.     { Hit Test check is required because there are cases when the window }
  113.     {   gets very small that windows decides not to draw the scroll bars }
  114.     {   even though they exist. }
  115.     PtInScroll := Point(Left + Frame.cx + 1, Bottom - Frame.cy - 1);
  116.     HVis := ( ((GetWindowLong(Handle, GWL_STYLE) and WS_HSCROLL) <> 0) and
  117.               (Perform(WM_NCHITTEST, 0, LongInt(PtInScroll)) = HTHSCROLL) );
  118.  
  119.     if ( Code = SB_HORZ ) then
  120.       begin  { Done, return result computed above }
  121.       Result := HVis;
  122.       end
  123.     else
  124.       begin  { Perform same procedure as above for vertical }
  125.       PtInScroll := Point(Right - Frame.cx - 1, Bottom - Frame.cy - 1);
  126.       if ( HVis ) then Dec(PtInScroll.y, HScrollBtn.cy);
  127.       Result := ( ((GetWindowLong(Handle, GWL_STYLE) and WS_VSCROLL) <> 0) and
  128.                   (Perform(WM_NCHITTEST, 0, LongInt(PtInScroll)) = HTVSCROLL) );
  129.       end;
  130.     end;
  131. end;
  132.  
  133.  
  134.  
  135. constructor TForm3D.Create
  136.   (
  137.   AOwner: TComponent
  138.   );
  139. begin
  140.   inherited Create(AOwner);
  141.  
  142.   { Set property defaults }
  143.   FAllowResize  := False;
  144. end;
  145.  
  146.  
  147. {
  148.   CreateWnd is overriden so we can force certain properties before
  149.     the window is created, and compute some parameters needed to
  150.     do the 3D non-client drawing.
  151. }
  152. procedure TForm3D.CreateWnd;
  153. var
  154.   AdjustHeight : Integer;
  155.   Version      : TWindowsVersion;
  156.   VerMajor     : Word;
  157.   VerMinor     : Word;
  158. begin
  159.   { Border Style must be bsSizeable }
  160.   BorderStyle := bsSizeable;
  161.  
  162.   { Compute height adjustments for font caption and menu.      }
  163.   { In large fonts video modes the client area would otherwise }
  164.   {   be reduced. }
  165.   AdjustHeight := 0;
  166.   with  SysMetrics  do
  167.     begin
  168.     Inc(AdjustHeight, CaptionH - CaptionH_STD);
  169.     { Note: Only adjusts for a single line menu bar }
  170.     if ( Menu <> nil ) then Inc(AdjustHeight, MenuH - MenuH_STD);
  171.     end;
  172.  
  173.   { Let Form create }
  174.   inherited CreateWnd;
  175.  
  176.   { Enforce the height adjustment }
  177.   Height := Height + AdjustHeight;
  178.  
  179.   { 3D Drawing requires Win 3.x default behavior. }
  180.   GetWindowsVersion(Version, VerMajor, VerMinor);
  181.   FEnable3D := ( (VerMajor = 3) and (VerMinor = 10) );
  182.  
  183.   { Precompute dimensions of key non-client areas for later use }
  184.   {   in drawing the 3D effects. }
  185.   if ( FEnable3D ) then ComputeNonClientDimensions;
  186. end;
  187.  
  188.  
  189. {
  190. ComputeNonClientDimensions precomputes some dimensions of non-client items
  191.   to avoid doing it repeatedly during painting.
  192. }
  193. procedure TForm3D.ComputeNonClientDimensions;
  194.  
  195.   { We'd like to use the SM_CXSIZE system metrics value for the size of icons }
  196.   {  in the title bar but it is NOT correct for some video drivers/modes }
  197.   function BitmapWidth(const BM_ID: Integer): Integer;
  198.   var
  199.     BM     : THandle;
  200.     BMInfo : WinTypes.TBitmap;
  201.   begin
  202.     BM := LoadBitmap(0, MakeIntResource(BM_ID));
  203.     try
  204.       GetObject(BM, SizeOf(BMInfo), @BMInfo);
  205.       Result := BMInfo.bmWidth;
  206.     finally
  207.       DeleteObject(BM);
  208.     end;
  209.   end;
  210.  
  211. begin
  212.   FSysMenuW := 0;
  213.   if ( biSystemMenu in BorderIcons ) then
  214.     begin
  215.     { Note: Close bitmap contains 2 bitmaps, app close and MDI child close }
  216.     Inc(FSysMenuW, BitmapWidth(OBM_CLOSE) div 2);
  217.     end;
  218.  
  219.   FMinMaxW := 0;
  220.   if ( biMinimize in BorderIcons ) then
  221.     begin
  222.     Inc(FMinMaxW, BitmapWidth(OBM_REDUCE));
  223.     end;
  224.   if ( biMaximize in BorderIcons ) then
  225.     begin
  226.     Inc(FMinMaxW, BitmapWidth(OBM_ZOOM));
  227.     end;
  228. end;
  229.  
  230.  
  231. {
  232. NCPaint3D handles the 3D specific painting for the form.
  233. }
  234. procedure TForm3D.NCPaint3D
  235.   (
  236.   const Mode: TForm3D_NCPaintMode
  237.   );
  238. var
  239.   WndRect     : TRect;
  240.   ClientRect  : TRect;
  241.   ClientH     : Integer;
  242.   ScrollH     : Integer;
  243.   DC          : HDC;
  244.   NCCanvas    : TCanvas;
  245.   Extra       : Integer;
  246.   CaptionRect : TRect;
  247.   CaptionPt   : TPoint;
  248.   TM          : TTextMetric;
  249.   CaptionBuf  : array[0..255] of Char;
  250. begin
  251.   { Get window rect }
  252.   WinProcs.GetWindowRect(Handle, WndRect);
  253.   { Need to know if horz scroll bar present }
  254.   ScrollH := 0;
  255.   if ( ScrollBarVisible(SB_HORZ, WndRect) ) then
  256.     begin
  257.     ScrollH := SysMetrics.HScrollBtn.cy - 1;
  258.     end;
  259.   { Convert window rect to (0, 0) origin }
  260.   with  WndRect  do
  261.     begin
  262.     Right  := Right - Left;
  263.     Left   := 0;
  264.     Bottom := Bottom - Top;
  265.     Top    := 0;
  266.     end;
  267.   WinProcs.GetClientRect(Handle, ClientRect);
  268.   ClientH := ClientRect.Bottom - ClientRect.Top;
  269.   if ( 0 < ClientH ) then Inc(ClientH);
  270.  
  271.   { Get a Window DC and wrap it in a Delphi Canvas }
  272.   DC       := GetWindowDC(Self.Handle);
  273.   NCCanvas := TCanvas.Create;
  274.   NCCanvas.Handle := DC;
  275.   try
  276.     with NCCanvas, WndRect, SysMetrics do
  277.       begin
  278.       if ( Mode = NCPaint_All ) then
  279.         begin
  280.         { Draw Left and Top edges of window frame, outer }
  281.         Pen.Color := clBtnShadow;
  282.         PolyLine([ Point(Left,  Bottom - 1),
  283.                    Point(Left,  Top),
  284.                    Point(Right, Top) ]);
  285.         { Draw Bottom and Right edges of window frame, outer }
  286.         Pen.Color := clWindowFrame;
  287.         PolyLine([ Point(Left,  Bottom - 1),
  288.                    Point(Right - 1, Bottom - 1),
  289.                    Point(Right - 1, Top - 1) ]);
  290.         { Draw Left and Top edges of window frame, 1-pixel in }
  291.         Pen.Color := clBtnHighlight;
  292.         PolyLine([ Point(Left  + 1, Bottom - 2),
  293.                    Point(Left  + 1, Top    + 1),
  294.                    Point(Right - 1, Top    + 1) ]);
  295.         { Draw Right and Bottom edges of window frame, 1-pixel in }
  296.         Pen.Color := clBtnShadow;
  297.         PolyLine([ Point(Left  + 1, Bottom - 2),
  298.                    Point(Right - 2, Bottom - 2),
  299.                    Point(Right - 2, Top) ]);
  300.  
  301.         { Fill Remainder of Sizing border }
  302.         Pen.Color := clBtnFace;
  303.         for Extra := 2 to (Frame.cx - 1) do
  304.           begin
  305.           Brush.Color := clBtnFace;
  306.           FrameRect(Rect(Left + Extra, Top + Extra,
  307.                          Right - Extra, Bottom - Extra));
  308.           end;
  309.  
  310.         { Draw Left and Top Edge of Caption Area }
  311.         Pen.Color := clBtnShadow;
  312.         PolyLine([ Point(Frame.cx - 1, Bottom - 1 - Frame.cy - ClientH - ScrollH),
  313.                    Point(Frame.cx - 1, Frame.cy - 1),
  314.                    Point(Right - Frame.cx, Frame.cy - 1) ]);
  315.         { Draw Bottom and Right Edge of Caption Area }
  316.         Pen.Color := clBtnHighlight;
  317.         PolyLine([ Point(Frame.cx - 1,     Bottom - Frame.cy - ClientH - ScrollH),
  318.                    Point(Right - Frame.cx, Bottom - Frame.cy - ClientH - ScrollH),
  319.                    Point(Right - Frame.cx, Frame.cy - 1) ]);
  320.         end;
  321.  
  322.       { Draw Caption }
  323.       CaptionRect := Rect(Frame.cx + FSysMenuW + 1, Frame.cy,
  324.                           Right - Frame.cx - FMinMaxW,
  325.                           Frame.cy - 1 + CaptionH - 1);
  326.       if ( (Mode = NCPaint_Activate) or
  327.            ((Mode = NCPaint_All) and (GetActiveWindow = Self.Handle)) ) then
  328.         begin  { Need 'Active' Caption }
  329.         Brush.Color := clActiveCaption;
  330.         Font.Color  := clCaptionText;
  331.         end
  332.       else
  333.         begin  { Need 'InActive' Caption }
  334.         Brush.Color := clInactiveCaption;
  335.         Font.Color  := clInactiveCaptionText;
  336.         end;
  337.       FillRect(CaptionRect);
  338.       with  CaptionRect  do
  339.         begin
  340.         { Assume center aligned }
  341.         SetTextAlign(DC, TA_CENTER or TA_TOP);
  342.         GetTextMetrics(DC, TM);
  343.         CaptionPt := Point((Left + Right) div 2,
  344.                            Top + ((CaptionH - 1) - TM.tmHeight) div 2);
  345.         if ( (Right - Left) < TextWidth(Caption) ) then
  346.           begin { Switch caption to left align to mimic windows }
  347.           SetTextAlign(DC, TA_LEFT or TA_TOP);
  348.           CaptionPt.X := Left + 1;
  349.           end;
  350.         TextRect(CaptionRect, CaptionPt.X, CaptionPt.Y, Caption);
  351.         end;
  352.       end;
  353.   finally
  354.     NCCanvas.Free;
  355.     ReleaseDC(Handle, DC);
  356.   end; { try-finally }
  357. end;
  358.  
  359.  
  360. {
  361. WMNCHitTest handles the WM_NCHITTEST message.
  362. Modifies sizing hit codes to support fixed size windows.
  363. }
  364. procedure TForm3D.WMNCHitTest
  365.   (
  366.   var Msg: TWMNCHitTest
  367.   );
  368. var
  369.   HitCode : LongInt;
  370. begin
  371.   inherited;
  372.   HitCode := Msg.Result;
  373.  
  374.   { Lets resurrect the size corner }
  375.   if ( HitCode = HTSIZE ) then HitCode := HTBOTTOMRIGHT;
  376.  
  377.   if ( not AllowResize ) then
  378.     begin
  379.     if ( (HitCode = HTLEFT)     or (HitCode = HTRIGHT)      or
  380.          (HitCode = HTTOP)      or (HitCode = HTBOTTOM)     or
  381.          (HitCode = HTTOPLEFT)  or (HitCode = HTBOTTOMLEFT) or
  382.          (HitCode = HTTOPRIGHT) or (HitCode = HTBOTTOMRIGHT) ) then
  383.       begin
  384.       HitCode := HTNOWHERE;
  385.       end;
  386.     end;
  387.  
  388.   Msg.Result := HitCode;
  389. end;
  390.  
  391.  
  392. {
  393. WMNCPaint handles WM_NCPAINT message.
  394. Calls default handler to paint non-client areas that have standard appearance.
  395. Calls NCPaint3D to paint modified non-client areas
  396. NOTE: Uses undocumented aspect of WM_NCPAINT message which allows a clipping
  397.       region handle to be passed in the wParam of the message.
  398.       This is used to avoid seeing the standard non-client areas flash before
  399.       they are repainted by the 3D code.
  400.       Ref. Undocumented Windows pg. 527, Thanks Andrew.
  401. }
  402. procedure TForm3D.WMNCPaint
  403.   (
  404.   var Msg: TWMNCPaint
  405.   );
  406. var
  407.   WndRect    : TRect;
  408.   ClientRect : TRect;
  409.   ClientH    : Integer;
  410.   ScrollH    : Integer;
  411.   ClipRect   : TRect;
  412.   ClipRgn    : THandle;
  413.   HScrollVis : Boolean;
  414.   VScrollVis : Boolean;
  415. begin
  416.   if ( FEnable3D and (not IsIconic(Handle)) ) then
  417.     begin
  418.     { Let Windows draw the non-client areas that will not change }
  419.     { Form props for window pos and size incorrect during resize here. }
  420.     { Get Position directly from windows }
  421.     WinProcs.GetWindowRect(Handle, WndRect);
  422.     WinProcs.GetClientRect(Handle, ClientRect);
  423.     ClientH := ClientRect.Bottom - ClientRect.Top;
  424.     if ( 0 < ClientH ) then Inc(ClientH);
  425.  
  426.     HScrollVis := ScrollBarVisible(SB_HORZ, WndRect);
  427.     VScrollVis := ScrollBarVisible(SB_VERT, WndRect);
  428.  
  429.     ScrollH := 0;
  430.     if ( HScrollVis ) then ScrollH := SysMetrics.HScrollBtn.cy - 1;
  431.  
  432.     with  WndRect, SysMetrics  do
  433.       begin
  434.       { System Menu }
  435.       if ( biSystemMenu in BorderIcons ) then
  436.         begin
  437.         ClipRect := Rect(Left + Frame.cx, Top + Frame.cy,
  438.                          Left + Frame.cx + TitleBitmap.cx + 1,
  439.                          Top  + Frame.cy + TitleBitmap.cy);
  440.         ClipRgn := CreateRectRgnIndirect(ClipRect);
  441.         TMessage(Msg).wParam := ClipRgn;
  442.         (self as TWinControl).DefaultHandler(Msg);
  443.         DeleteObject(ClipRgn);
  444.         end;
  445.       { Min/Max buttons }
  446.       if ( 0 < FMinMaxW ) then
  447.         begin
  448.         ClipRect := Rect(Right - Frame.cx - FMinMaxW, Top  + Frame.cy,
  449.                          Right - Frame.cx, Top + Frame.cy + TitleBitmap.cy);
  450.         ClipRgn := CreateRectRgnIndirect(ClipRect);
  451.         TMessage(Msg).wParam := ClipRgn;
  452.         (self as TWinControl).DefaultHandler(Msg);
  453.         DeleteObject(ClipRgn);
  454.         end;
  455.       { Menubar }
  456.       if ( Menu <> nil ) then
  457.         begin
  458.         ClipRect := Rect(Left + Frame.cx, Top + Frame.cy + CaptionH - Border.cy - 1,
  459.                          Right - Frame.cx, Bottom - Frame.cy - ClientH - ScrollH);
  460.         ClipRgn := CreateRectRgnIndirect(ClipRect);
  461.         TMessage(Msg).wParam := ClipRgn;
  462.         (self as TWinControl).DefaultHandler(Msg);
  463.         DeleteObject(ClipRgn);
  464.         end;
  465.       end;
  466.  
  467.     { Paint 3-D parts of nonclient area in 3-D style }
  468.     NCPaint3D(NCPaint_All);
  469.  
  470.     { Now let windows paint scroll bars. Need to wait until here because scroll }
  471.     {   bars take advantage of normal borders for their outer edges and they    }
  472.     {   our trounced in NCPaint3D. }
  473.     with  WndRect, SysMetrics  do
  474.       begin
  475.       if ( HScrollVis ) then
  476.         begin { Let Windows draw horz scroll bar }
  477.         ClipRect := Rect(Left  + (Frame.cx - 1), Bottom - (Frame.cy - 1) - HScrollBtn.cy,
  478.                          Right - (Frame.cx - 1), Bottom - (Frame.cy - 1));
  479.         if ( VScrollVis ) then Dec(ClipRect.Right, VScrollBtn.cx - 1);
  480.         ClipRgn := CreateRectRgnIndirect(ClipRect);
  481.         TMessage(Msg).wParam := ClipRgn;
  482.         (self as TWinControl).DefaultHandler(Msg);
  483.         DeleteObject(ClipRgn);
  484.         end;
  485.       if ( VScrollVis ) then
  486.         begin { Let Windows draw vert scroll bar }
  487.         ClipRect := Rect(Right - (Frame.cx - 1) - VScrollBtn.cx, Bottom - Frame.cy - ClientH - ScrollH,
  488.                          Right - (Frame.cx - 1),                 Bottom - (Frame.cy - 1));
  489.         if ( HScrollVis ) then Dec(ClipRect.Bottom, HScrollBtn.cy - 1);
  490.         ClipRgn := CreateRectRgnIndirect(ClipRect);
  491.         TMessage(Msg).wParam := ClipRgn;
  492.         (self as TWinControl).DefaultHandler(Msg);
  493.         DeleteObject(ClipRgn);
  494.         end;
  495.       if ( HScrollVis and VScrollVis ) then
  496.         begin { Let Windows draw little box in corner }
  497.         ClipRect := Rect(Right - (Frame.cx - 1) - (VScrollBtn.cx - 1),
  498.                          Bottom - (Frame.cy - 1) - (HScrollBtn.cy - 1),
  499.                          Right - (Frame.cx - 1) - 1, Bottom - (Frame.cy - 1) - 1);
  500.         ClipRgn := CreateRectRgnIndirect(ClipRect);
  501.         TMessage(Msg).wParam := ClipRgn;
  502.         (self as TWinControl).DefaultHandler(Msg);
  503.         DeleteObject(ClipRgn);
  504.         end;
  505.       end;
  506.     Msg.Result := 0;
  507.     end
  508.   else
  509.     begin
  510.     { Use whatever behavior is standard for this system }
  511.     DefaultHandler(Msg);
  512.     end;
  513. end;
  514.  
  515.  
  516. {
  517. WMNCActivate handles the WM_NCACTIVATE message.
  518. Calls NCPaint3D to repaint the caption.
  519. Can NOT let windows have this message or it will trash our 3D borders.
  520. }
  521. procedure TForm3D.WMNCActivate
  522.   (
  523.   var Msg: TWMNCActivate
  524.   );
  525. begin
  526.   if ( FEnable3D and (not IsIconic(Handle)) ) then
  527.     begin
  528.     if ( Msg.Active ) then
  529.       NCPaint3D(NCPaint_Activate)
  530.     else
  531.       NCPaint3D(NCPaint_Deactivate);
  532.  
  533.     Msg.Result := 1;
  534.     end
  535.   else
  536.     begin
  537.     DefaultHandler(Msg);
  538.     end;
  539. end;
  540.  
  541.  
  542. procedure TForm3D.WMSetText
  543.   (
  544.   var Msg : TWMSetText
  545.   );
  546. begin
  547.   { Got to let this one through so caption updated }
  548.   inherited;
  549.  
  550.   if ( Menu = nil ) then
  551.     begin
  552.     { Repaint the sucker }
  553.     NCPaint3D(NCPaint_All);
  554.     end;
  555. end;
  556.  
  557.  
  558. end.
  559.