home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Chip 2001 September
/
Chip_2001-09_cd1.bin
/
zkuste
/
delphi
/
kolekce
/
d123456
/
DFS.ZIP
/
GradForm.pas
< prev
next >
Wrap
Pascal/Delphi Source File
|
2001-06-27
|
72KB
|
1,961 lines
{$I DFS.INC} { Standard defines for all Delphi Free Stuff components }
{------------------------------------------------------------------------------}
{ TdfsGradientForm v2.03 }
{ A form to provide gradient filled caption bars ala Microsoft Office. }
{ You will notice that some of the initial comment characters are followed by }
{ a colon, and those sometimes contains some odd looking things that resemble }
{ HTML codes. These comments are used by the Time2Help application that I }
{ used to build the help file. }
{ }
{ Copyright 2000-2001, Brad Stowers. All Rights Reserved. }
{ }
{ Copyright: }
{ All Delphi Free Stuff (hereafter "DFS") source code is copyrighted by }
{ Bradley D. Stowers (hereafter "author"), and shall remain the exclusive }
{ property of the author. }
{ }
{ Distribution Rights: }
{ You are granted a non-exlusive, royalty-free right to produce and distribute }
{ compiled binary files (executables, DLLs, etc.) that are built with any of }
{ the DFS source code unless specifically stated otherwise. }
{ You are further granted permission to redistribute any of the DFS source }
{ code in source code form, provided that the original archive as found on the }
{ DFS web site (http://www.delphifreestuff.com) is distributed unmodified. For }
{ example, if you create a descendant of TDFSColorButton, you must include in }
{ the distribution package the colorbtn.zip file in the exact form that you }
{ downloaded it from http://www.delphifreestuff.com/mine/files/colorbtn.zip. }
{ }
{ Restrictions: }
{ Without the express written consent of the author, you may not: }
{ * Distribute modified versions of any DFS source code by itself. You must }
{ include the original archive as you found it at the DFS site. }
{ * Sell or lease any portion of DFS source code. You are, of course, free }
{ to sell any of your own original code that works with, enhances, etc. }
{ DFS source code. }
{ * Distribute DFS source code for profit. }
{ }
{ Warranty: }
{ There is absolutely no warranty of any kind whatsoever with any of the DFS }
{ source code (hereafter "software"). The software is provided to you "AS-IS", }
{ and all risks and losses associated with it's use are assumed by you. In no }
{ event shall the author of the softare, Bradley D. Stowers, be held }
{ accountable for any damages or losses that may occur from use or misuse of }
{ the software. }
{ }
{ Support: }
{ Support is provided via the DFS Support Forum, which is a web-based message }
{ system. You can find it at http://www.delphifreestuff.com/discus/ }
{ All DFS source code is provided free of charge. As such, I can not guarantee }
{ any support whatsoever. While I do try to answer all questions that I }
{ receive, and address all problems that are reported to me, you must }
{ understand that I simply can not guarantee that this will always be so. }
{ }
{ Clarifications: }
{ If you need any further information, please feel free to contact me directly.}
{ This agreement can be found online at my site in the "Miscellaneous" section.}
{------------------------------------------------------------------------------}
{ The lateset version of my components are always available on the web at: }
{ http://www.delphifreestuff.com/ }
{ See GradForm.txt for notes, known issues, and revision history. }
{------------------------------------------------------------------------------}
{ Date last modified: June 27, 2001 }
{------------------------------------------------------------------------------}
{: This unit provides the TdfsGradientForm class, and all supporting elements. }
unit GradForm;
{$IFNDEF DFS_WIN32}
Error! This unit is only available for Win32.
{$ENDIF}
interface
uses
{$IFDEF DFS_COMPILER_6_UP}
RTLConsts,
{$ELSE}
Consts,
{$ENDIF}
Windows, Messages, SysUtils, Forms, Classes, Graphics, Controls, Dialogs;
const
{ This shuts up C++Builder 3 about the redefiniton being different. There
seems to be no equivalent in C1. Sorry. }
{$IFDEF DFS_CPPB_3_UP}
{$EXTERNALSYM DFS_COMPONENT_VERSION}
{$ENDIF}
DFS_COMPONENT_VERSION = 'TdfsGradientForm v2.03';
{: The minimum number of colors that can be assigned to the
<See Property=TdfsGradientForm.GradientColors Text=GradientColors> property.
Any less than 8 colors doesn't look much like a gradient.
<Related A=MAX_GRADIENT_COLORS;DEF_GRADIENT_COLORS>
}
MIN_GRADIENT_COLORS = 8;
{: The maximum number of colors that can be assigned to the
<See Property=TdfsGradientForm.GradientColors Text=GradientColors> property.
Any more than 512 colors is not noticeable, and just slows the painting
down. <Related A=MIN_GRADIENT_COLORS;DEF_GRADIENT_COLORS> }
MAX_GRADIENT_COLORS = 512;
{: The default number of colors for the
<See Property=TdfsGradientForm.GradientColors Text=GradientColors> property.
This is a good compromise between speed and appearance.
<Related A=MAX_GRADIENT_COLORS;MIN_GRADIENT_COLORS>}
DEF_GRADIENT_COLORS = 64;
DEF_CAPTION_TEXT_COLOR = clWhite;
DEF_INACTIVE_CAPTION_TEXT_COLOR = clWhite;
DEF_GRADIENT_START_COLOR = clBlack;
DEF_GRADIENT_STOP_COLOR = clActiveCaption;
DEF_GRADIENT_INACTIVE_START_COLOR = clBlack;
DEF_GRADIENT_INACTIVE_STOP_COLOR = clInactiveCaption;
DEF_USE_WIN98_GRADIENT = FALSE;
DEF_USE_DITHERING = TRUE;
type
{: For some reason, you can not pass HWND and HDC type parameters from
C++Builder source code to a Delphi component. For some reason, C++B wants
to treat these parameters as "void *" (pointer) types, and you will get
unresolved external linker errors from Builder if you have methods that
take HWND and/or HDC parameters in other than the private section.
I have delcared this type so that it makes the code cleaner below. }
{$IFDEF DFS_CPPB}
DFS_HDC = pointer;
{$ELSE}
DFS_HDC = HDC;
{$ENDIF}
{: This enumerated type is used by the
<See Property=TdfsGradientForm.PaintGradient Text=PaintGradient> property to
indicate when the caption should be painted as a gradient.<BR>
<UL>
<LI>gfpAlways <TAB> The gradient should always be drawn.
<LI>gfpActive <TAB> Only draw the gradient when the form is active.
<LI>gfpNever <TAB> Never draw the gradient.
</UL>
<Related A=PaintGradient>}
TGFPaintWhen = (gfpAlways, gfpActive, gfpNever);
TGFLogoAlign = (laLeft, laRight);
const
DEF_PAINT_GRADIENT = gfpAlways;
type
{: Describes the parameters used by an
<See Event=TdfsGradientForm.OnCaptionPaint Text=OnCaptionPaint> event handler.
<BR><BR><B>Sender</B> is the TdfsGradientForm that is being painted.<BR><BR>
<B>Canvas</B> is the drawing surface that is being painted. Anything you
want to appear on the caption must be drawn on this canvas. This canvas is
not the actual caption canvas, it is a memory bitmap (non-visible). This
prevents flicker as many things are being drawn since the actual visible
drawing only happens when the entire drawing operation is complete.<BR><BR>
<B>R</B> is a rectangle that describes the area in which you can draw.
When the event is first fired, this rectangle will be the entire caption
less the system icon on the left (if any) and the caption buttons on the
right (if any). After performing your drawing operations, this value
should be modified so that the area you have painted is subtracted out.
This prevents the gradient from painting over what you have just done.
<Related A=OnCaptionPaint>}
TGFOnCaptionPaint = procedure(Sender: TObject; Canvas: TCanvas;
var R: TRect) of object;
//CE_Desc_Begin(TdfsGradientForm)
{TdfsGradientForm is a descendant of the TForm class that paints it's caption
bar in a gradient fill pattern, like the Microsoft Office applications.
By default, it starts with black and moves gradually to the system defined
caption color, although you can override these values. Also provided is
an event to allow you to add your own custom painting on the caption bar. }
//CE_Desc_End
TdfsGradientForm = class(TForm)
private
// Internal variables
Colors: array[0..1, 0..MAX_GRADIENT_COLORS-1] of TColorRef;
//** CaptionFontHandle: HFONT;
FGradDefClientProc: TFarProc;
FGradClientInstance: TFarProc;
// Property variables
FCaptionTextColor: TColor;
FInactiveCaptionTextColor: TColor;
FGradientStartColor: TColor;
FGradientStopColor: TColor;
FGradientInactiveStartColor: TColor;
FGradientInactiveStopColor: TColor;
FGradientColors: integer;
FPaintGradient: TGFPaintWhen;
FCaptionText: string;
FOnCaptionPaint: TGFOnCaptionPaint;
FUsingDefaultGradientStopColor: boolean;
FUsingDefaultGradientInactiveStopColor: boolean;
FUseWin98Gradient: boolean;
FRunningOnWin98: boolean;
FChangingActivationState: boolean;
FPaint16Color: boolean;
FSystemIs16Color: boolean;
FCaptionFont: TFont;
FUseSystemCaptionFont: boolean;
FCreating: boolean;
FUseDithering: boolean;
FLogo: TBitmap;
FLogoAlign: TGFLogoAlign;
FLogoLayered: Boolean;
FInactiveLogo: TBitmap;
// Internal methods
function IsActiveWindow: boolean;
//** procedure CreateCaptionFontHandle;
// Window message handlers
procedure WMNCActivate(var Msg: TWMNCActivate); message WM_NCACTIVATE;
procedure WMNCPaint(var Msg: TMessage); message WM_NCPAINT;
procedure WMSysColorChange(var Msg: TWMSysColorChange);
message WM_SYSCOLORCHANGE;
procedure WMSize(var Msg: TWMSize); message WM_SIZE;
procedure WMSetCursor(var Msg: TWMSetCursor); message WM_SETCURSOR;
procedure WMSetText(var Msg: TWMSetText); message WM_SETTEXT;
procedure WMGetText(var Msg: TWMGetText); message WM_GETTEXT;
procedure WMGetTextLength(var Msg: TWMGetTextLength);
message WM_GETTEXTLENGTH;
procedure WMSettingChange(var Msg: TMessage); message WM_SETTINGCHANGE;
procedure WMNCLButtonDown(var Msg: TWMNCLButtonDown);
message WM_NCLBUTTONDOWN;
procedure WMSysCommand(var Msg: TWMSysCommand); message WM_SYSCOMMAND;
procedure WMEnterIdle(var Msg: TWMEnterIdle); message WM_ENTERIDLE;
procedure WMWindowPosChanging(var Msg: TWMWindowPosChanging);
message WM_WINDOWPOSCHANGING;
// MDI Client Window Procedure
procedure GradClientWndProc(var Message: TMessage);
function GetInhibitGradient: boolean;
procedure SetCaptionFont(const Value: TFont);
// Misc
function GetSysCaptionLogFont: TLogFont;
procedure SetUseSystemCaptionFont(const Value: boolean);
protected
// Virtual methods useful for descandants
function GetCaptionRect: TRect; virtual;
procedure InvalidateCaption;
function DrawCaption(FormDC: DFS_HDC; Active: boolean): TRect; virtual;
procedure PaintMenuIcon(DC: DFS_HDC; var R: TRect; Active: boolean); virtual;
procedure FillRectSolid(DC: DFS_HDC; const R: TRect; Active: boolean;
ActiveColor, InactiveColor : TColor); virtual;
procedure FillRectGradient(DC: DFS_HDC; const R: TRect;
Dithered, Active: boolean); virtual;
procedure PaintCaptionText(DC: DFS_HDC; R: TRect; Active: boolean); virtual;
procedure PaintCaptionButtons(DC: DFS_HDC; var Rect: TRect); virtual;
procedure CalculateColors; virtual;
// Overriden methods
procedure Loaded; override;
procedure CreateWnd; override;
procedure DestroyWnd; override;
procedure Activate; override;
procedure Deactivate; override;
procedure DoShow; override;
// Property methods
procedure SetCaptionTextColor(Color: TColor);
procedure SetInactiveCaptionTextColor(Color: TColor);
procedure SetGradientStartColor(Color : TColor);
procedure SetGradientStopColor(Color : TColor);
procedure SetGradientInactiveStartColor(Color : TColor);
procedure SetGradientInactiveStopColor(Color : TColor);
procedure SetGradientColors(Val: integer);
procedure SetPaintGradient(Val: TGFPaintWhen);
procedure SetCaptionText(const Val: string);
procedure SetUseWin98Gradient(Val: boolean);
procedure SetUseDithering(Val: boolean);
procedure SetPaint16Color(const Value: boolean);
procedure SetLogo(const Value: TBitmap);
procedure SetLogoAlign(const Value: TGFLogoAlign);
procedure SetLogoLayered(const Value: Boolean);
procedure SetInactiveLogo(const Value: TBitmap);
function GetVersion: string;
procedure SetVersion(const Val: string);
// Property storage qualifing methods
function StoreGradientStopColor: boolean;
function StoreGradientInactiveStopColor: boolean;
// Utility methods
function Win98Check: boolean; virtual;
procedure UpdateCaptionFont; virtual;
public
function GetSystemColorBitDepth: integer;
{ This procedure is used to paint the caption gradient. }
procedure Draw(Active: boolean); virtual;
// Overridden methods
{ Create creates and initializes an instance of TdfsGradientForm. }
constructor Create(AOwner: TComponent); override;
{ Destroy destroys an instance of TdfsGradientForm. }
destructor Destroy; override;
property InhibitGradient: boolean
read GetInhibitGradient;
property Paint16Color: boolean
read FPaint16Color
write SetPaint16Color;
property SystemIs16Color: boolean
read FSystemIs16Color;
published
// Properties
property Version: string
read GetVersion
write SetVersion
stored FALSE;
{: Caption specifies a text string that appears in the caption bar. }
property Caption: string
read FCaptionText
write SetCaptionText
stored TRUE;
property CaptionFont: TFont
read FCaptionFont
write SetCaptionFont;
property UseSystemCaptionFont: boolean
read FUseSystemCaptionFont
write SetUseSystemCaptionFont;
{: Determines the number of colors used to paint the gradient pattern. The
individual colors are determined by fading the start color into the stop
color. The number of times this is done is controled by this property.
The higher the number of colors, the smoother the gradient will appear.
However, the more colors that are used, the more complex the painting
will be.
<Related A=MAX_GRADIENT_COLORS;MIN_GRADIENT_COLORS;DEF_GRADIENT_COLORS> }
property GradientColors: integer
read FGradientColors
write SetGradientColors
default DEF_GRADIENT_COLORS;
{: CaptionTextColor is the color that should be used for the text draw in
the caption bar. You may have to adjust this color if you change the
<See Property=TdfsGradientForm.GradientStartColor Text=GradientStartColor>
to something other than the default of clBlack.
<Related A=GradientStartColor;GradientStopColor;Caption>}
property CaptionTextColor: TColor
read FCaptionTextColor
write SetCaptionTextColor
default DEF_CAPTION_TEXT_COLOR;
property InactiveCaptionTextColor: TColor
read FInactiveCaptionTextColor
write SetInactiveCaptionTextColor
default DEF_INACTIVE_CAPTION_TEXT_COLOR;
{: The leftmost gradient color. This is the color that is used at the
beginning of the caption (the far left), and is gradually faded into the
<See Property=TdfsGradientForm.GradientStopColor Text=GradientStopColor>.
<Related A=GradientStopColor;GradientColors> }
property GradientStartColor: TColor
read FGradientStartColor
write SetGradientStartColor
default DEF_GRADIENT_START_COLOR;
{: The rightmost gradient color. This is the color that is used at the
end of the caption (the far right), and is gradually faded from the
<See Property=TdfsGradientForm.GradientStartColor Text=GradientStartColor>.
<Related A=GradientStartColor;GradientColors> }
property GradientStopColor: TColor
read FGradientStopColor
write SetGradientStopColor
stored StoreGradientStopColor
default DEF_GRADIENT_STOP_COLOR;
property GradientInactiveStartColor: TColor
read FGradientInactiveStartColor
write SetGradientInactiveStartColor
default DEF_GRADIENT_INACTIVE_START_COLOR;
property GradientInactiveStopColor: TColor
read FGradientInactiveStopColor
write SetGradientInactiveStopColor
stored StoreGradientInactiveStopColor
default DEF_GRADIENT_INACTIVE_STOP_COLOR;
{: Determines if and when the gradient caption should be painted.
<UL>
<LI>gfpAlways <TAB> The gradient should always be drawn.
<LI>gfpActive <TAB> Only draw the gradient when the form is active.
<LI>gfpNever <TAB> Never draw the gradient.
</UL> }
property PaintGradient: TGFPaintWhen
read FPaintGradient
write SetPaintGradient
default DEF_PAINT_GRADIENT;
property UseWin98Gradient: boolean
read FUseWin98Gradient
write SetUseWin98Gradient
default DEF_USE_WIN98_GRADIENT;
property UseDithering: boolean
read FUseDithering
write SetUseDithering
default DEF_USE_DITHERING;
property Logo: TBitmap
read FLogo
write SetLogo;
property InactiveLogo: TBitmap
read FInactiveLogo
write SetInactiveLogo;
property LogoAlign: TGFLogoAlign
read FLogoAlign
write SetLogoAlign
default laRight;
property LogoLayered: Boolean
read FLogoLayered
write SetLogoLayered
default FALSE;
{: This event is fired after the icon, buttons and gradient are painted,
but just before the text is painted. It is not fired if the caption is
painted but not as a gradient, that is if
<See TdfsGradientForm.PaintGradient Text=PaintGradient> is gfpNever or
gfpActive and the form is not active.<BR><BR> <B>Sender</B> is the
TdfsGradientForm that is being painted.<BR><BR><B>Canvas</B> is the drawing
surface that is being painted. Anything you want to appear on the
caption must be drawn on this canvas. This canvas is not the actual
caption canvas, it is a memory bitmap (non-visible). This prevents
flicker as many things are being drawn since the actual visible drawing
only happens when the entire drawing operation is complete.<BR><BR>
<B>R</B> is a rectangle that describes the area in which you can draw.
When the event is first fired, this rectangle will be the entire caption
less the system icon on the left (if any) and the caption buttons on the
right (if any). After performing your drawing operations, this value
should be modified so that the area you have painted is subtracted out.
This prevents the gradient from painting over what you have just done. }
property OnCaptionPaint: TGFOnCaptionPaint
read FOnCaptionPaint
write FOnCaptionPaint;
end;
implementation
{$R GRADFORM.RES}
// A variant record (known as a union in C) to allow easy access to the
// individual red, // green, and blue values of a TColorRef (RGB) value.
type
TRGBMap = packed record
case boolean of
TRUE: ( RGBVal: DWORD );
FALSE: ( Red,
Green,
Blue,
Unused: byte );
end;
var
EntrancyFlag: boolean;
{: Create creates and initializes an instance of TdfsGradientForm. Call Create
to instantiate a TdfsGradientForm at runtime. After calling the inherited
constructor, Create initializes the following properties:<BR>
<UL>
<LI>UsingDefaultGradientStopColor to TRUE.
<LI>CaptionTextColor to clWhite.
<LI>GradientStartColor to clBlack.
<LI>GradientStopColor to clActiveCaption.
<LI>GradientColors to <See DEF_GRADIENT_COLORS>.
<LI>PaintGradient to gpfAlways.
</UL> }
constructor TdfsGradientForm.Create(AOwner: TComponent);
var
VerInfo: TOSVersionInfo;
begin
// We set our new property values first so that they will be valid in the
// OnCreate event handler. The inherited Create is what calls that event, so
// we set up first.
FLogo := TBitmap.Create;
FInactiveLogo := TBitmap.Create;
FLogoLayered := FALSE;
FLogoAlign := laRight;
// Are we running under Win98, and should we let it do it for us?
VerInfo.dwOSVersionInfoSize := SizeOf(VerInfo);
if GetVersionEx(VerInfo) then
// this will also catch NT 5.
FRunningOnWin98 := (VerInfo.dwMajorVersion >= 5) or
((VerInfo.dwMajorVersion >= 4) and (VerInfo.dwMinorVersion > 0))
else
FRunningOnWin98 := FALSE;
// Are we running on a 16-color system?
FSystemIs16Color := GetSystemColorBitDepth = 4;
// Don't paint 16-color by default
FPaint16Color := FALSE;
FUseWin98Gradient := DEF_USE_WIN98_GRADIENT;
FUseDithering := DEF_USE_DITHERING;
FUsingDefaultGradientStopColor := TRUE;
FUsingDefaultGradientInactiveStopColor := TRUE;
FCaptionTextColor := DEF_CAPTION_TEXT_COLOR;
FInactiveCaptionTextColor := DEF_INACTIVE_CAPTION_TEXT_COLOR;
// Set gradient start and stop colors.
FGradientStartColor := DEF_GRADIENT_START_COLOR;
FGradientStopColor := DEF_GRADIENT_STOP_COLOR;
FGradientInactiveStartColor := DEF_GRADIENT_INACTIVE_START_COLOR;
FGradientInactiveStopColor := DEF_GRADIENT_INACTIVE_STOP_COLOR;
// Set the number of colors to use to create the gradient fill.
FGradientColors := DEF_GRADIENT_COLORS;
// Should we paint the gradient when window is inactive.
FPaintGradient := DEF_PAINT_GRADIENT;
FOnCaptionPaint := NIL;
FChangingActivationState := FALSE;
// Caption font stuff
FUseSystemCaptionFont := TRUE;
FCaptionFont := TFont.Create;
UpdateCaptionFont;
// Calculate the colors we need to paint the gradient.
CalculateColors;
//** CaptionFontHandle := 0;
// inherited Create(AOwner);
CreateNew(AOwner {$IFDEF DFS_CPPB_1}, 1 {$ENDIF});
if (ClassType <> TdfsGradientForm) and not (csDesigning in ComponentState) then
begin
FCreating := TRUE;
try
if not InitInheritedComponent(Self, TdfsGradientForm) then
{$IFDEF DFS_COMPILER_2}
raise EResNotFound.CreateResFmt(sResNotFound, [ClassName]);
{$ELSE}
raise EResNotFound.CreateFmt(sResNotFound, [ClassName]);
{$ENDIF}
finally
FCreating := FALSE;
end;
// All versions of Delphi, and C4 and up.
{$IFDEF DFS_DELPHI}
{$DEFINE DFS_DO_ONCREATE}
{$ENDIF}
{$IFDEF DFS_CPPB_4_UP}
{$DEFINE DFS_DO_ONCREATE}
{$ENDIF}
{$IFDEF DFS_DO_ONCREATE}
try
if {$IFDEF DFS_COMPILER_4_UP} OldCreateOrder and {$ENDIF}
assigned(OnCreate) then
OnCreate(Self);
except
Application.HandleException(Self);
end;
{$UNDEF DFS_DO_ONCREATE}
{$ENDIF}
end;
end;
{: Destroy destroys an instance of TdfsGradientForm. Do not call Destroy
directly in an application. Instead, call Free. Free verifies that the
instance is not already freed, and only then calls Destroy.<BR>
Destroy is used to free resources allocated in the
<See Method=TdfsGradientForm.Create Text=Create> constructor. }
destructor TdfsGradientForm.Destroy;
begin
FLogo.Free;
FInactiveLogo.Free;
FCaptionFont.Free;
// Clean up the font we created.
//** if CaptionFontHandle <> 0 then
//** DeleteObject(CaptionFontHandle);
inherited Destroy;
end;
procedure TdfsGradientForm.Loaded;
begin
inherited Loaded;
// Create a font for the caption bar.
//** CreateCaptionFontHandle;
end;
procedure TdfsGradientForm.CreateWnd;
begin
inherited CreateWnd;
if (not InhibitGradient) and (FormStyle = fsMDIForm) then
begin
FGradClientInstance := MakeObjectInstance(GradClientWndProc);
FGradDefClientProc := Pointer(GetWindowLong(ClientHandle, GWL_WNDPROC));
SetWindowLong(ClientHandle, GWL_WNDPROC, Longint(FGradClientInstance));
end else
FGradClientInstance := NIL;
//** if not (csLoading in ComponentState) then
// Create a font for the caption bar.
//** CreateCaptionFontHandle;
end;
procedure TdfsGradientForm.DestroyWnd;
begin
if (FormStyle = fsMDIForm) and assigned(FGradClientInstance) then
begin
SetWindowLong(ClientHandle, GWL_WNDPROC, Longint(FGradDefClientProc));
FreeObjectInstance(FGradClientInstance);
end;
inherited DestroyWnd;
end;
procedure TdfsGradientForm.SetGradientColors(Val: integer);
begin
if (Val = FGradientColors) or (Val < MIN_GRADIENT_COLORS) or
(Val > MAX_GRADIENT_COLORS) then
exit;
FGradientColors := Val;
// The number of colors have changed, we need to recalculate the colors we
// use to paint.
CalculateColors;
InvalidateCaption;
end;
procedure TdfsGradientForm.SetCaptionTextColor(Color: TColor);
begin
if FCaptionTextColor = Color then
exit;
FCaptionTextColor := Color;
InvalidateCaption;
end;
procedure TdfsGradientForm.SetInactiveCaptionTextColor(Color: TColor);
begin
if FInactiveCaptionTextColor = Color then
exit;
FInactiveCaptionTextColor := Color;
InvalidateCaption;
end;
procedure TdfsGradientForm.SetGradientStartColor(Color : TColor);
begin
FGradientStartColor := Color;
// The colors have changed, we need to recalculate the colors we use to paint.
CalculateColors;
InvalidateCaption;
end;
procedure TdfsGradientForm.SetGradientStopColor(Color : TColor);
begin
FGradientStopColor := Color;
FUsingDefaultGradientStopColor := FGradientStopColor = clActiveCaption;
// The colors have changed, we need to recalculate the colors we use to paint.
CalculateColors;
InvalidateCaption;
end;
procedure TdfsGradientForm.SetGradientInactiveStartColor(Color : TColor);
begin
FGradientInactiveStartColor := Color;
// The colors have changed, we need to recalculate the colors we use to paint.
CalculateColors;
InvalidateCaption;
end;
procedure TdfsGradientForm.SetGradientInactiveStopColor(Color : TColor);
begin
FGradientInactiveStopColor := Color;
FUsingDefaultGradientInactiveStopColor :=
(FGradientInactiveStopColor = clInactiveCaption);
// The colors have changed, we need to recalculate the colors we use to paint.
CalculateColors;
InvalidateCaption;
end;
procedure TdfsGradientForm.SetPaintGradient(Val: TGFPaintWhen);
begin
if FPaintGradient = Val then
exit;
FPaintGradient := Val;
InvalidateCaption;
end;
procedure TdfsGradientForm.SetCaptionText(const Val: string);
begin
if EntrancyFlag then
exit;
EntrancyFlag := TRUE;
try
// Have to do this so the MDI window menus get updated and application
// titles get updated (taskbar and Alt-Tab text)
inherited Caption := Val;
FCaptionText := Val;
if (not InhibitGradient) and HandleAllocated and IsWindowVisible(Handle) then
begin
if (FormStyle = fsMDIChild) {and FChangingActivationState} then
begin
if WindowState = wsMaximized then
// Need to cause main form's caption to be redrawn, not the MDI child.
SetWindowPos(Application.MainForm.Handle, 0, 0, 0, 0, 0,
SWP_DRAWFRAME or SWP_NOACTIVATE or SWP_NOMOVE or SWP_NOSIZE
or SWP_NOZORDER)
else
InvalidateCaption;
end else
Draw(IsActiveWindow);
end;
finally
EntrancyFlag := FALSE;
end;
end;
procedure TdfsGradientForm.SetUseWin98Gradient(Val: boolean);
begin
if Val <> FUseWin98Gradient then
begin
FUseWin98Gradient := Val;
if HandleAllocated then
begin
RecreateWnd;
// hmmm, how to get it to show again in the IDE?
end;
end;
end;
procedure TdfsGradientForm.SetUseDithering(Val: boolean);
begin
if Val <> FUseDithering then
begin
FUseDithering := Val;
InvalidateCaption;
end;
end;
function TdfsGradientForm.IsActiveWindow: boolean;
begin
if FormStyle = fsMDIChild then
if assigned(Application.MainForm) then
Result := (GetActiveWindow = Application.MainForm.Handle) and
(TForm(Application.MainForm).ActiveMDIChild = Self)
else
Result := FALSE
else
Result := GetActiveWindow=Handle;
end;
procedure TdfsGradientForm.CalculateColors;
var
LoColor, HiColor: TRGBMap;
RedPct,
GreenPct,
BluePct: real;
x,
Band: integer;
begin
// Get colors for both active and inactive captions.
for x := 0 to 1 do
begin
if x = 0 then // inactive captions
begin
LoColor.RGBVal := ColorToRGB(FGradientInactiveStartColor);
HiColor.RGBVal := ColorToRGB(FGradientInactiveStopColor);
end else begin // active caption
LoColor.RGBVal := ColorToRGB(FGradientStartColor);
HiColor.RGBVal := ColorToRGB(FGradientStopColor);
end;
// Figure out the percentage of each RGB value needed for banding
RedPct := (HiColor.Red - LoColor.Red)/ (FGradientColors-1);
GreenPct := (HiColor.Green - LoColor.Green) / (FGradientColors-1);
BluePct := (HiColor.Blue - LoColor.Blue) / (FGradientColors-1);
// Use the percentage of each color to create each band color.
for Band := 0 to (FGradientColors-1) do
Colors[x][Band] := RGB(LoColor.Red + round(RedPct * (Band)),
LoColor.Green + round(GreenPct * (Band)),
LoColor.Blue + round(BluePct * (Band)));
end;
end;
//**
{procedure TdfsGradientForm.CreateCaptionFontHandle;
var
NCM: TNonClientMetrics;
begin
if CaptionFontHandle <> 0 then
DeleteObject(CaptionFontHandle);
NCM.cbSize := SizeOf(NCM);
if SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, @NCM, 0) then
begin
if BorderStyle in [bsToolWindow, bsSizeToolWin] then
CaptionFontHandle := CreateFontIndirect(NCM.lfSmCaptionFont)
else
CaptionFontHandle := CreateFontIndirect(NCM.lfCaptionFont);
end else
CaptionFontHandle := 0;
end;
}
// The caption rect is the rectangle we are interested in painting. This will
// be the area that contains the caption icon, text and buttons.
function TdfsGradientForm.GetCaptionRect: TRect;
begin
// Designing mode always draws the form as bsSizeable
if csDesigning in ComponentState then
begin
GetWindowRect(Handle, Result);
// Convert rect from screen (absolute) to client (0 based) coordinates.
OffsetRect(Result, -Result.Left, -Result.Top);
// Shrink rectangle to allow for window border. We let Windows paint it.
InflateRect(Result, -GetSystemMetrics(SM_CXSIZEFRAME),
-GetSystemMetrics(SM_CYSIZEFRAME));
Result.Bottom := Result.Top + GetSystemMetrics(SM_CYCAPTION) - 1;
end else begin
// if we have no border style, then just set the rectange empty.
if BorderStyle = bsNone then
SetRectEmpty(Result)
else begin
GetWindowRect(Handle, Result);
// Convert rect from screen (absolute) to client (0 based) coordinates.
OffsetRect(Result, -Result.Left, -Result.Top);
// Shrink rectangle to allow for window border. We let Windows paint it.
if (WindowState = wsMinimized) or (BorderStyle in [bsToolWindow, bsSingle,
bsDialog]) then
InflateRect(Result, -GetSystemMetrics(SM_CXFIXEDFRAME),
-GetSystemMetrics(SM_CYFIXEDFRAME))
else if BorderStyle in [bsSizeable, bsSizeToolWin] then
InflateRect(Result, -GetSystemMetrics(SM_CXSIZEFRAME),
-GetSystemMetrics(SM_CYSIZEFRAME));
// Set the appropriate height of caption bar.
if BorderStyle in [bsToolWindow, bsSizeToolWin] then
Result.Bottom := Result.Top + GetSystemMetrics(SM_CYSMCAPTION) - 1
else
Result.Bottom := Result.Top + GetSystemMetrics(SM_CYCAPTION) - 1;
end;
end;
end;
// Paint the icon for the system menu.
procedure TdfsGradientForm.PaintMenuIcon(DC: DFS_HDC; var R: TRect; Active: boolean);
{$IFDEF DFS_COMPILER_2}
const
LR_COPYFROMRESOURCE = $4000; // Missing from WINDOWS.PAS in Delphi 2!
{$ENDIF}
var
SmallCopy,
IconHandle: HIcon;
Size: integer;
begin
// Does the form have an icon assigned to it?
if Icon.Handle <> 0 then
IconHandle := Icon.Handle
// If not, does the application have an icon?
else if Application.Icon.Handle <> 0 then
IconHandle := Application.Icon.Handle
// If not, then just use the system defined application icon.
else
IconHandle := LoadIcon(0, IDI_APPLICATION);
Size := GetSystemMetrics(SM_CXSMICON);
SmallCopy := CopyImage(IconHandle, IMAGE_ICON, Size, Size,
LR_COPYFROMRESOURCE);
with R do
// Let CopyImage() make get us a nice 16x16 version of the icon and we'll
// paint it.
DrawIconEx(HDC(DC), Left+1, Top+1, SmallCopy, 0, 0, 0, 0, DI_NORMAL);
DestroyIcon(SmallCopy);
Inc(R.Left, Size+1);
end;
// Paint the given rectangle with the system solid color.
procedure TdfsGradientForm.FillRectSolid(DC: DFS_HDC; const R: TRect;
Active: boolean; ActiveColor, InactiveColor : TColor);
var
OldBrush,
Brush: HBrush;
begin
// Create a brush with the appropriate color\
if Active then
Brush := CreateSolidBrush(ColorToRGB(ActiveColor))
else
Brush := CreateSolidBrush(ColorToRGB(InactiveColor));
// Select that brush into the temporary DC.
OldBrush := SelectObject(HDC(DC), Brush);
try
// Fill the rectangle using the selected brush -- PatBlt is faster than
// FillRect
with R do
PatBlt(HDC(DC), Left, Top, Right-Left, Bottom-Top, PATCOPY);
finally
// Clean up the brush
SelectObject(HDC(DC), OldBrush);
DeleteObject(Brush);
end;
end;
// Paint the given rectangle with the gradient pattern.
procedure TdfsGradientForm.FillRectGradient(DC: DFS_HDC; const R: TRect;
Dithered, Active: boolean);
function MaxInt(I1, I2: integer): integer;
begin
if I1 > I2 then
Result := I1
else
Result := I2;
end;
function MinInt(I1, I2: integer): integer;
begin
if I1 < I2 then
Result := I1
else
Result := I2;
end;
const
HorizTileWidth: array[0..1] of Integer = (69, 14);
HorizTileHeight: array[0..1] of Integer = (30, 28);
TileResName: array[0..1] of String = ('DFS_DITHGRADMASK1','DFS_DITHGRADMASK2');
var
OldBmp,
TmpBmp: HBitmap;
TmpDC: HDC;
OldBrush,
Brush: HBrush;
Step: real;
Band: integer;
Width, Height: Integer;
StartColor, StopColor: DWORD;
x, y, i: Integer;
RStart, GStart, BStart: Integer;
RDiff, GDiff, BDiff: Integer;
DitherColors, Index: Integer;
TileBitmap,
MaskBitmap,
OffScreenBitmap: TBitmap;
FromColor,
ToColor: TColor;
PixelsToInsert, PixelsNow: Integer;
ImageList: TImageList;
begin
Width := R.Right - R.Left;
if Width < 1 then exit;
Height := R.Bottom - R.Top;
StartColor := 0;
Index := 0;
RStart := 0;
GStart := 0;
BStart := 0;
RDiff := 0;
GDiff := 0;
BDiff := 0;
if Dithered then
begin
// Dithered style gradient
if Active then
begin
StartColor := ColorToRGB(FGradientStartColor);
StopColor := ColorToRGB(FGradientStopColor);
end else begin
StartColor := ColorToRGB(FGradientInactiveStartColor);
StopColor := ColorToRGB(FGradientInactiveStopColor);
end;
RStart := GetRValue(StartColor);
GStart := GetGValue(StartColor);
BStart := GetBValue(StartColor);
RDiff := GetRValue(StopColor) - RStart;
GDiff := GetGValue(StopColor) - GStart;
BDiff := GetBValue(StopColor) - BStart;
if (Abs(RDiff) + Abs(GDiff) + Abs(BDiff)) / Width < 200.0 / 280.0 then
Index := 0
else
Index := 1;
// Want dithering, but make sure it will look good.
if (Width < HorizTileWidth[Index] shl 1) or (GetDeviceCaps(HDC(DC),
SIZEPALETTE) > 0) then
Dithered := FALSE; // Low color mode, dithering will look horrible.
end;
if Dithered then
begin
OffScreenBitmap := TBitmap.Create;
try
OffScreenBitmap.Width := Width;
OffScreenBitmap.Height := Height;
// if dithering is on, we caluclate the number of colors from the width
DitherColors := (Width div HorizTileWidth[Index]) + 1;
if Width mod HorizTileWidth[Index] > 0 then
// if the width is not the multiple of HorizTileWidth, additional pixels
// must be inserted between the tiles
PixelsToInsert := Width - (DitherColors - 1) * HorizTileWidth[Index]
else
PixelsToInsert := 0;
// setting up the temp bitmap and loading the tile mask
ImageList := TImageList.CreateSize(HorizTileWidth[Index],
HorizTileHeight[Index]);
MaskBitmap := TBitmap.Create;
TileBitmap := TBitmap.Create;
try
MaskBitmap.LoadFromResourceName(HInstance, TileResName[Index]);
TileBitmap.Width := HorizTileWidth[Index];
TileBitmap.Height := HorizTileHeight[Index];
x := 0;
FromColor := StartColor;
for i := 1 to DitherColors do
begin
// calculating the next color
ToColor := RGB(
MinInt(MaxInt(RStart + MulDiv(i, RDiff, DitherColors - 1), 0), 255),
MinInt(MaxInt(GStart + MulDiv(i, GDiff, DitherColors - 1), 0), 255),
MinInt(MaxInt(BStart + MulDiv(i, BDiff, DitherColors - 1), 0), 255));
// colorizing the tile mask
TileBitmap.Canvas.Brush.Color := FromColor;
PatBlt(TileBitmap.Canvas.Handle, 0, 0, HorizTileWidth[Index],
HorizTileHeight[Index], PATCOPY);
if ImageList.Count = 0 then
ImageList.Add(TileBitmap, MaskBitmap)
else
ImageList.Replace(0, TileBitmap, MaskBitmap);
with OffScreenBitmap do
begin
// painting the tile mask
Canvas.Brush.Color := ToColor;
PatBlt(Canvas.Handle, x, 0, x + HorizTileWidth[Index], Height,
PATCOPY);
y := 0;
while y < Height do
begin
ImageList.Draw(Canvas, x, y, 0);
Inc(y, HorizTileHeight[Index]);
end;
Inc(x, HorizTileWidth[Index]);
// inserting the extra pixels if necessary
if PixelsToInsert <> 0 then
begin
PixelsNow := PixelsToInsert div (DitherColors - 2);
if i < PixelsToInsert mod (DitherColors - 2) then
Inc(PixelsNow);
PatBlt(Canvas.Handle, x, 0, x + PixelsNow, Height, PATCOPY);
Inc(x, PixelsNow);
end;
end;
FromColor := ToColor;
end;
finally
TileBitmap.Free;
MaskBitmap.Free;
ImageList.Free;
end;
BitBlt(HDC(DC), R.Left, R.Top, Width, Height,
OffScreenBitmap.Canvas.Handle, 0, 0, SRCCOPY);
finally
OffScreenBitmap.Free;
end;
end else begin
// "Banded" style gradient
// This may look backwards, but it's not. If the device capabilities
// indicate that there are palette entries (more than 0), then we are in
// a low color mode. This is because when in high color mode or above,
// Windows doesn't use palettes; each pixel knows it's RGB value.
if (GetDeviceCaps(HDC(DC), SIZEPALETTE) > 0) or
(Width < GradientColors) then
begin
// Low color gradient, slower
// Determine how large each band should be in order to cover the
// rectangle (one band for every color intensity level).
Step := Width / FGradientColors;
// Start filling bands
for Band := 0 to (FGradientColors-1) do
begin
// Create a brush with the appropriate color for this band
Brush := CreateSolidBrush(Colors[ord(Active)][Band]);
// Select that brush into the temporary DC.
OldBrush := SelectObject(HDC(DC), Brush);
try
// Fill the rectangle using the selected brush -- PatBlt is faster
// than FillRect
PatBlt(HDC(DC), round(Band*Step) + R.Left, 0,
round((Band+1)*Step) - round(Band*Step), Height, PATCOPY);
finally
// Clean up the brush
SelectObject(HDC(DC), OldBrush);
DeleteObject(Brush);
end;
end; // for
end else begin
// High color gradient, faster
TmpDC := CreateCompatibleDC(HDC(DC));
TmpBmp := CreateCompatibleBitmap(HDC(DC), FGradientColors, 1);
OldBmp := SelectObject(TmpDC, TmpBmp);
try
// Start filling bands
for Band := 0 to (FGradientColors-1) do
SetPixel(TmpDC, Band, 0, Colors[ord(Active)][Band]);
StretchBlt(HDC(DC), R.Left, 0, Width, Height, TmpDC, 0, 0,
FGradientColors-1, 1, SRCCOPY);
finally
SelectObject(TmpDC, OldBmp);
DeleteObject(TmpBmp);
DeleteDC(TmpDC);
end;
end;
end;
end;
procedure TdfsGradientForm.PaintCaptionText(DC: DFS_HDC; R: TRect; Active: boolean);
var
OldColor: TColorRef;
OldMode: integer;
OldFont: HFont;
CaptionText: string;
begin
CaptionText := Caption;
// Have to turn off complete boolean eval for this "if" statement. I never
// have it on anyway, but some do.
{$IFOPT B+} {$DEFINE DFS_RESET_BOOL_EVAL} {$B-} {$ENDIF}
if ((FormStyle = fsMDIForm) and (ActiveMDIChild <> NIL) and
(ActiveMDIChild.WindowState = wsMaximized)) then
CaptionText := CaptionText + ' - [' + ActiveMDIChild.Caption + ']';
{$IFDEF DFS_RESET_BOOL_EVAL} {$B+} {$UNDEF DFS_RESET_BOOL_EVAL} {$ENDIF}
Inc(R.Left, 2);
// Set the color to paint the text with.
if Active then
OldColor := SetTextColor(HDC(DC), ColorToRGB(FCaptionTextColor))
else
OldColor := SetTextColor(HDC(DC), ColorToRGB(FInactiveCaptionTextColor));
// Set the background text painting mode to transparent so that drawing text
// doesn't distrub the gradient we just painted. If you didn't do this, then
// drawing text would also fill the text rectangle with a solid background
// color, screwing up our gradient.
OldMode := SetBkMode(HDC(DC), TRANSPARENT);
// Select in the system defined caption font (see Create constructor).
if FCaptionFont.Handle <> 0 then
//** if CaptionFontHandle <> 0 then
OldFont := SelectObject(HDC(DC), FCaptionFont.Handle)
//** OldFont := SelectObject(HDC(DC), CaptionFontHandle)
else
OldFont := 0;
try
// Draw the text making it left aligned, centered vertically, allowing no
// line breaks.
DrawText(HDC(DC), PChar(CaptionText), -1, R, DT_LEFT or DT_VCENTER or
DT_SINGLELINE or DT_END_ELLIPSIS);
finally
// Clean up all the drawing objects.
if OldFont <> 0 then
SelectObject(HDC(DC), OldFont);
SetBkMode(HDC(DC), OldMode);
SetTextColor(HDC(DC), OldColor);
end;
end;
// Paint the min/max/help/close buttons.
procedure TdfsGradientForm.PaintCaptionButtons(DC: DFS_HDC; var Rect: TRect);
var
BtnWidth: integer;
Flag: UINT;
SrcRect: TRect;
ABorderStyle: TFormBorderStyle;
ABorderIcons: TBorderIcons;
begin
SrcRect := Rect;
InflateRect(SrcRect, -2, -2);
if csDesigning in ComponentState then
begin
// While designing, the min/max buttons are always shown in a sizeable frame
ABorderStyle := bsSizeable;
ABorderIcons := [biSystemMenu, biMinimize, biMaximize];
end else begin
ABorderStyle := BorderStyle;
ABorderIcons := BorderIcons;
end;
if ABorderStyle in [bsToolWindow, bsSizeToolWin] then
begin
// Tool windows only have the close button, nothing else.
with SrcRect do
Left := Right - (GetSystemMetrics(SM_CXSMSIZE)) + 2;
Flag := DFCS_CAPTIONCLOSE;
if (GetClassLong(Handle, GCL_STYLE) and CS_NOCLOSE) <> 0 then
Flag := Flag or DFCS_INACTIVE;
DrawFrameControl(HDC(DC), SrcRect, DFC_CAPTION, Flag);
Rect.Right := SrcRect.Left-5;
end else begin
BtnWidth := GetSystemMetrics(SM_CXSMICON)-2;
{ Windows is loopy. It always returns an even number, no matter what }
if (Odd(BtnWidth) XOR Odd(Rect.Bottom-Rect.Top)) then
inc(BtnWidth);
SrcRect.Left := SrcRect.Right - BtnWidth - 2;
// if it has system menu, it has a close button.
if biSystemMenu in ABorderIcons then
begin
Flag := DFCS_CAPTIONCLOSE;
if (GetClassLong(Handle, GCL_STYLE) and CS_NOCLOSE) <> 0 then
Flag := Flag or DFCS_INACTIVE;
DrawFrameControl(HDC(DC), SrcRect, DFC_CAPTION, Flag);
OffsetRect(SrcRect, -BtnWidth-4, 0);
Dec(Rect.Right,BtnWidth+4);
end;
// Minimize and Maximized don't show up at all if BorderStyle is bsDialog or
// if neither of them are enabled.
if (ABorderStyle in [bsSizeable, bsSingle]) and
(ABorderIcons * [biMinimize, biMaximize] <> []) then
begin
if WindowState = wsMaximized then
Flag := DFCS_CAPTIONRESTORE
else
Flag := DFCS_CAPTIONMAX;
// if it doesn't have max in style, then it shows up disabled.
if not (biMaximize in ABorderIcons) then
Flag := Flag or DFCS_INACTIVE;
DrawFrameControl(HDC(DC), SrcRect, DFC_CAPTION, Flag);
OffsetRect(SrcRect, -BtnWidth-2, 0);
Dec(Rect.Right,BtnWidth+2);
if WindowState = wsMinimized then
Flag := DFCS_CAPTIONRESTORE
else
Flag := DFCS_CAPTIONMIN;
// if it doesn't have min in style, then it shows up disabled.
if not (biMinimize in ABorderIcons) then
Flag := Flag or DFCS_INACTIVE;
DrawFrameControl(HDC(DC), SrcRect, DFC_CAPTION, Flag);
OffsetRect(SrcRect, -BtnWidth-2, 0);
Dec(Rect.Right,BtnWidth+2);
end;
// Help only shows up in bsDialog style, and bsSizeable, bsSingle when there
// is no min or max button.
if biHelp in ABorderIcons then
begin
if ((ABorderStyle in [bsSizeable, bsSingle]) and
(ABorderIcons * [biMinimize, biMaximize] = [])) or
(ABorderStyle = bsDialog) then
begin
DrawFrameControl(HDC(DC), SrcRect, DFC_CAPTION, DFCS_CAPTIONHELP);
Dec(Rect.Right,BtnWidth+2);
end;
end;
Dec(Rect.Right, 3);
end;
end;
function TdfsGradientForm.DrawCaption(FormDC: DFS_HDC; Active: boolean): TRect;
var
R: TRect;
OldBmp,
Bmp: HBitmap;
BmpDC: HDC;
BmpCanvas: TCanvas;
w,h:integer;
IsLogoGradient : Boolean;
GradientRect, LogoRect : TRect;
LogoWidth : Integer;
CurrentLogo : TBitmap;
begin
// Get only the portion we need to draw.
R := GetCaptionRect;
Result := R;
// Convert to logical (0-based) coordinates
OffsetRect(R, -R.Left, -R.Top);
W := R.Right - R.Left;
H := R.Bottom - R.Top;
// Create a temporary device context to draw on. Drawing on a temporary DC
// and copying it to the real DC accomplishes two things:
// 1) It is faster because Windows doesn't have to draw anything in the
// temporary DC on the screen, it only draws when you paint something on a
// real DC. Then it just draws everything at once when we copy it, instead
// of drawing a little, do some calculations, draw a little, etc.
// 2) It looks much better because it is drawn faster. It reduces the
// "flicker" that you would see from each individual part being drawn,
// especially the gradient bands.
BmpDC := CreateCompatibleDC(HDC(FormDC));
Bmp := CreateCompatibleBitmap(HDC(FormDC), W, H);
OldBmp := SelectObject(BmpDC, Bmp);
try
// If there's a logo bitmap, we need a solid background
// behind the menu icon, the caption buttons, and the
// logo; so we need to delay drawing of the gradient
// until after the menu and buttons are painted.
IsLogoGradient := FALSE;
if (FPaintGradient = gfpAlways) or
(Active and (FPaintGradient = gfpActive)) then
begin
if (Assigned (FLogo)) and (not FLogo.Empty) then
begin
IsLogoGradient := TRUE;
FillRectSolid(DFS_HDC (BmpDC), R, Active, GradientStartColor,
GradientInactiveStartColor);
end
else
// Draw the gradient background in the temporary DC
FillRectGradient(DFS_HDC(BmpDC), R, UseDithering, Active)
end
else
FillRectSolid(DFS_HDC(BmpDC), R, Active, GetSysColor(COLOR_ACTIVECAPTION),
GetSysColor(COLOR_INACTIVECAPTION));
Inc(R.Left, 1);
// Do we need to paint an icon for the system menu?
if not ((FormStyle = fsMDIChild) and (WindowState = wsMaximized)) then
begin
if IsLogoGradient then
begin
// Start by drawing the solid-color end of the bar.
// There's a solid color under the menu icon if the
// logo is left-aligned, or under the caption buttons
// if the logo is right-aligned.
if LogoAlign = laLeft then
begin
if ((biSystemMenu in BorderIcons) and
(BorderStyle in [bsSingle, bsSizeable])) or
(csDesigning in ComponentState) then
begin
FillRectSolid(DFS_HDC (BmpDC), R, Active, GradientStartColor,
GradientInactiveStartColor);
// PaintMenuIcon will adjust the rect so that future drawing operations
// happen in the right spot.
PaintMenuIcon(DFS_HDC(BmpDC), R, Active);
end;
end
else // LogoAlign = laRight
begin
FillRectSolid(DFS_HDC(BmpDC), R, Active, GradientStopColor,
GradientInactiveStopColor);
PaintCaptionButtons(DFS_HDC(BmpDC), R);
end;
if (not Active) and (not FInactiveLogo.Empty) then
CurrentLogo := FInactiveLogo
else
CurrentLogo := FLogo;
LogoWidth := CurrentLogo.Width;
if LogoAlign = laLeft then
LogoRect := Rect(R.Left, R.Top, R.Left + LogoWidth, R.Bottom)
else
LogoRect := Rect(R.Right - LogoWidth, R.Top, R.Right, R.Bottom);
// Make sure LogoRect doesn't fall off the edge
// of our drawable area (between icon and buttons)
IntersectRect (LogoRect, LogoRect, R);
if LogoAlign = laLeft then
GradientRect := Rect(LogoRect.Right, R.Top, R.Right, R.Bottom)
else
GradientRect := Rect(R.Left, R.Top, LogoRect.Left, R.Bottom);
if GradientRect.Right > GradientRect.Left then
FillRectGradient(DFS_HDC(BmpDC), GradientRect, UseDithering, Active);
BitBlt(BmpDC, LogoRect.Left, (LogoRect.Bottom - LogoRect.Top -
CurrentLogo.Height) div 2 + LogoRect.Top, LogoRect.Right -
LogoRect.Left, CurrentLogo.Height, CurrentLogo.Canvas.Handle, 0, 0,
SRCCOPY);
// Now draw the caption stuff that needs a gradient:
// caption buttons if logo is left-aligned, or icon
// if logo is right-aligned.
if LogoAlign <> laLeft then
begin
if ((biSystemMenu in BorderIcons) and
(BorderStyle in [bsSingle, bsSizeable])) or
(csDesigning in ComponentState) then
// PaintMenuIcon will adjust the rect so that future drawing operations
// happen in the right spot.
PaintMenuIcon(DFS_HDC(BmpDC), R, Active);
end
else // LogoAlign = laRight
PaintCaptionButtons(DFS_HDC(BmpDC), R);
if not LogoLayered then
IntersectRect(R, R, GradientRect);
// Done drawing the gradient, icon, caption buttons, and logo.
end
else
begin
if ((biSystemMenu in BorderIcons) and
(BorderStyle in [bsSingle, bsSizeable])) or
(csDesigning in ComponentState) then
// PaintMenuIcon will adjust the rect so that future drawing operations
// happen in the right spot.
PaintMenuIcon(DFS_HDC(BmpDC), R, Active);
PaintCaptionButtons(DFS_HDC(BmpDC), R); // Paint the min/max/help/close buttons.
end;
end;
if assigned(FOnCaptionPaint) then
begin
BmpCanvas := TCanvas.Create;
try
BmpCanvas.Handle := BmpDC;
// BmpCanvas.Font.handle := FCaptionFont.handle;
BmpCanvas.Font := FCaptionFont;
FOnCaptionPaint(Self, BmpCanvas, R);
finally
BmpCanvas.Free;
end;
end;
PaintCaptionText(DFS_HDC(BmpDC), R, Active); // Paint the caption text.
// Copy the gradient caption bar to the real DC.
BitBlt(HDC(FormDC), Result.Left, Result.Top, W, H, BmpDC, 0, 0, SRCCOPY);
finally
// Clean up all the temporary drawing objects.
SelectObject(BmpDC, OldBmp);
DeleteObject(Bmp);
DeleteDC(BmpDC);
end;
end;
// Windows sends this message when the window has been activated or deactivated.
procedure TdfsGradientForm.WMNCActivate(var Msg: TWMNCActivate);
begin
if not InhibitGradient then
begin
Msg.Result := 1;
// I can't remember what the "bad things" were, and I can't find any problems
// now if I don't call it.... If some new bug shows up, this is the first
// place to look.
{ if FormStyle in [fsMDIForm, fsMDIChild] then
inherited; { Call inherited or bad things will happen with MDI }
Draw(Msg.Active);
end else
inherited;
end;
// Windows sends this message whenever any part of the non-client area
// (caption, window border) needs repainting.
procedure TdfsGradientForm.WMNCPaint(var Msg: TMessage);
var
{$IFDEF DFS_COMPILER_4_UP}
SaveWR, CR,
{$ENDIF}
WR, R: TRect;
DC: HDC;
MyRgn: HRGN;
DeleteRgn: boolean;
begin
if not InhibitGradient then
begin
DeleteRgn := FALSE;
// The region that needs painting is passed in WParam. A region is a Windows
// object used to describe the non-rectangular area used by a combination of
// rectangles. We have to typecast it because in Delphi 4 wParam is signed
// and HRGN in unsigned. It worked prior to D4 because they were both
// signed.
MyRgn := HRGN(Msg.wParam);
DC := GetWindowDC(Handle);
try
GetWindowRect(Handle, WR);
// Select the update region as the clipping region. Clipping regions
// guarantee that any painting done outside of the selected region is not
// shown (thrown away).
if SelectClipRgn(DC, MyRgn) = ERROR then
begin
// We got passed an invalid region. Generally, this happens when the
// window is first created or a MDI is minimized. We'll create our own
// region (the rectangle that makes up the entire window) and use that
// instead.
with WR do
MyRgn := CreateRectRgn(Left, Top, Right, Bottom);
SelectClipRgn(DC, MyRgn);
DeleteRgn := TRUE;
end;
// Convert the clipping region coordinates from screen to client.
OffsetClipRgn(DC, -WR.Left, -WR.Top);
// Draw our gradient caption.
R := DrawCaption(DFS_HDC(DC), IsActiveWindow);
// Here's the trick. DrawCaption returns the rectangle that we painted.
// We now exclude that rectangle from the clipping region. This guarantees
// that any further painting that occurs will not happen in this rectangle.
// That means that when we let the default painting for WM_NCPAINT occur,
// it will not paint over our gradient. It only paints the stuff that we
// didn't, like the window borders.
ExcludeClipRect(DC, R.Left, R.Top, R.Right, R.Bottom);
{$IFDEF DFS_COMPILER_4_UP}
// Draw border if needed
if BorderWidth > 0 then
begin
Windows.GetClientRect(Handle, CR);
SaveWR := WR;
MapWindowPoints(0, Handle, WR, 2);
OffsetRect(CR, -WR.Left, -WR.Top);
{ Draw borders in non-client area }
InflateRect(CR, BorderWidth, BorderWidth);
WR := SaveWR;
OffsetRect(WR, -WR.Left, -WR.Top);
Windows.FillRect(DC, WR, Brush.Handle);
WR := SaveWR;
end;
{$ENDIF}
// Convert coordinates back into screen-based.
OffsetClipRgn(DC, WR.Left, WR.Top);
// Get the region that is now described by the clipping region.
GetClipRgn(DC, MyRgn);
// Pass that region on to the default WM_NCPAINT handler. Remember, we
// excluded the rectangle that we painted, so Windows will not be able to
// paint over what we did. Most gradient captions components just let
// windows draw its stuff first, and then paint the gradient. This results
// in an irritating "flicker", caused by the area being painted normally,
// and then painted over a second time by the gradient. We have to
// typecast the wParam parameter because in Delphi 4 wParam is signed and
// HRGN in unsigned. It worked prior to D4 because they were both signed.
Msg.Result := DefWindowProc(Handle, Msg.Msg, WPARAM(MyRgn), Msg.lParam);
finally
// If we had to create our own region, we have to clean it up.
if DeleteRgn then
DeleteObject(MyRgn);
ReleaseDC(Handle, DC); // NEVER leave this hanging.
end;
end else
inherited;
end;
// Windows sends this message if the user changes any of the system colors.
procedure TdfsGradientForm.WMSysColorChange(var Msg: TWMSysColorChange);
var
x: integer;
begin
// Did they change to 16-color mode?
FSystemIs16Color := GetSystemColorBitDepth = 4;
if not InhibitGradient then
begin
if FUsingDefaultGradientStopColor then
FGradientStopColor := clActiveCaption;
if FUsingDefaultGradientInactiveStopColor then
FGradientInactiveStopColor := clInactiveCaption;
CalculateColors;
// This only goes to top-level windows so we have to feed it to MDI children
if FormStyle = fsMDIForm then
begin
for x := 0 to MDIChildCount-1 do
if MDIChildren[x] is TdfsGradientForm then
TdfsGradientForm(MDIChildren[x]).WMSysColorChange(Msg);
end;
end;
inherited;
end;
// The window has been resized.
procedure TdfsGradientForm.WMSize(var Msg: TWMSize);
begin
inherited;
if not InhibitGradient then
begin
// If the window was maximized or restored, we need to redraw so the right
// caption button is painted.
if (Msg.SizeType = SIZE_MAXIMIZED) or (Msg.SizeType = SIZE_RESTORED) then
Draw(IsActiveWindow);
end;
end;
// Windows would like to have a cursor displayed. I know, you're wondering
// why the hell I care about this, aren't you? Well, in the inherited handling
// (default Windows processing) of this message, if the mouse is over a
// resizeable border section, Windows repaints the caption buttons. Why? I
// have absolutely no idea. However, that's not the important part. When it
// repaints those buttons, it also repaints the background around them in the
// last color it painted the caption in. Now, usually this would just result
// in losing a few bands of the caption gradient, which 99.44% of all users
// would never notice. However, because we don't always allow default
// processing of WM_NCACTIVATE, sometimes Windows doesn't have the right idea
// about which color is currently the background. This cause the background to
// get painted in the wrong color sometimes, which 99.44% of all users *will*
// notice. We fix it by setting the appropriate cursor and not allowing the
// default processing to occur.
procedure TdfsGradientForm.WMSetCursor(var Msg: TWMSetCursor);
begin
if not InhibitGradient then
begin
// Tell Windows we handled the message
Msg.Result := 1;
// Load and display the correct cursor for the border area being hit
case Msg.HitTest of
HTTOP,
HTBOTTOM: SetCursor(LoadCursor(0, MakeIntResource(IDC_SIZENS)));
HTLEFT,
HTRIGHT: SetCursor(LoadCursor(0, MakeIntResource(IDC_SIZEWE)));
HTTOPRIGHT,
HTBOTTOMLEFT: SetCursor(LoadCursor(0, MakeIntResource(IDC_SIZENESW)));
HTTOPLEFT,
HTBOTTOMRIGHT: SetCursor(LoadCursor(0, MakeIntResource(IDC_SIZENWSE)));
else
// Wasn't anything we cared about, so tell Windows we didn't handle it.
Msg.Result := 0;
inherited;
end;
end else
inherited;
end;
procedure TdfsGradientForm.WMSetText(var Msg: TWMSetText);
var
FlagSet: boolean;
Wnd: HWND;
begin
if (not InhibitGradient) then
begin
Wnd := 0;
if ((FormStyle = fsMDIChild) and (WindowState = wsMaximized)) then
begin
// Need to cause main form's caption to be redrawn, not the MDI child.
if Application.MainForm.HandleAllocated then
Wnd := Application.MainForm.Handle;
end else begin
if HandleAllocated then
Wnd := Handle;
end;
if (Wnd <> 0) and IsWindowVisible(Wnd) then
begin
FlagSet := TRUE;
// No update region for the window. changes won't be painted.
SetWindowRgn(Wnd, CreateRectRgn(0, 0, 0, 0), FALSE);
end else
FlagSet := FALSE;
// Normally, processing WM_SETTEXT would cause all sorts of flicker as it
// changed the caption text of the window. But, we've told it that the
// update region for the window (the portion it is allowed to paint in) is
// a NULL region (a rectangle equal to 0, 0, 0, 0). So, the changes don't
// have anywhere to paint now, so it is safe to call inherited at this
// point. After that, we'll restore the window region so that painting
// can happen again.
inherited;
if FlagSet then
// Reset region to normal.
SetWindowRgn(Wnd, 0, FALSE);
// Don't do it if it was called from .SetCaption
if not EntrancyFlag then
Caption := Msg.Text;
end else
inherited;
end;
procedure TdfsGradientForm.WMGetText(var Msg: TWMGetText);
begin
if not InhibitGradient then
begin
StrLCopy(Msg.Text, PChar(FCaptionText), Msg.TextMax-1);
Msg.Result := StrLen(Msg.Text)+1;
end else
inherited;
end;
procedure TdfsGradientForm.WMGetTextLength(var Msg: TWMGetTextLength);
begin
if not InhibitGradient then
begin
Msg.Result := Length(FCaptionText);
end else
inherited;
end;
procedure TdfsGradientForm.WMSettingChange(var Msg: TMessage);
begin
if not InhibitGradient then
begin
// User might have changed NC font.
if Msg.wParam = SPI_SETNONCLIENTMETRICS then
UpdateCaptionFont;
//** CreateCaptionFontHandle;
end;
inherited;
end;
{: This procedure is used to paint the caption gradient. It is normally
called internally, but it can be used any time a repaint of the caption
is needed. The <B>Active</B> parameter is used to indicate whether the
caption should be painted as the active window or an inactive window. }
procedure TdfsGradientForm.Draw(Active: boolean);
var
DC: HDC;
begin
if csDestroying in ComponentState then exit;
// Get the DC we need to paint in. GetDC would only get the DC for the
// client area, we need it for non-client area, too, so we use GetWindowDC.
DC := GetWindowDC(Handle);
try
DrawCaption(DFS_HDC(DC), Active);
finally
ReleaseDC(Handle, DC); // NEVER leave this hanging.
end;
end;
procedure TdfsGradientForm.GradClientWndProc(var Message: TMessage);
begin
with Message do begin
Result := CallWindowProc(FGradDefClientProc, ClientHandle, Msg, wParam,
lParam);
// if you don't want your MDI child forms to be TdfsGradientForm descendants,
// you will need to use uncomment the code in the following line.
// It will work, but it causes an annoying flicker.
// NOTE: as of v1.55, this is no longer necessary. Just leave it the way
// it is....I think.....
// $003F is a "Magic Number". I hate this, but it's the only way I could
// get it to work. :(
if {(Msg = WM_MDIREFRESHMENU) or} (Msg = $003F) then
Draw(IsActiveWindow);
end;
end;
procedure TdfsGradientForm.WMNCLButtonDown(var Msg: TWMNCLButtonDown);
begin
inherited;
if not InhibitGradient then
Draw(IsActiveWindow);
end;
procedure TdfsGradientForm.WMSysCommand (var Msg : TWMSysCommand);
begin
if not InhibitGradient then
begin
if Msg.CmdType = SC_CONTEXTHELP then
// Help button pressed, do't call Draw() because it will draw it in the up state.
inherited
else
begin
Draw(IsActiveWindow);
inherited;
Draw(IsActiveWindow);
end;
end else
inherited;
end;
procedure TdfsGradientForm.WMEnterIdle(var Msg: TWMEnterIdle);
begin
if not InhibitGradient then
Draw(IsActiveWindow);
inherited;
end;
procedure TdfsGradientForm.WMWindowPosChanging(var Msg: TWMWindowPosChanging);
begin
with Msg.WindowPos^ do
if FCreating and ((Flags and SWP_HIDEWINDOW) <> 0) then
Flags := Flags or SWP_NOREDRAW;
inherited;
end;
function TdfsGradientForm.StoreGradientStopColor: boolean;
begin
Result := not FUsingDefaultGradientStopColor;
end;
function TdfsGradientForm.StoreGradientInactiveStopColor: boolean;
begin
Result := not FUsingDefaultGradientInactiveStopColor;
end;
function TdfsGradientForm.Win98Check: boolean;
begin
// Are we running under Win98, and should we let it do it for us?
Result := FUseWin98Gradient and FRunningOnWin98;
end;
function TdfsGradientForm.GetVersion: string;
begin
Result := DFS_COMPONENT_VERSION;
end;
procedure TdfsGradientForm.SetVersion(const Val: string);
begin
{ empty write method, just needed to get it to show up in Object Inspector }
end;
procedure TdfsGradientForm.Activate;
begin
FChangingActivationState := TRUE;
try
inherited Activate;
finally
FChangingActivationState := FALSE;
end;
end;
procedure TdfsGradientForm.Deactivate;
begin
FChangingActivationState := TRUE;
try
inherited Deactivate;
finally
FChangingActivationState := FALSE;
end;
end;
procedure TdfsGradientForm.DoShow;
begin
FChangingActivationState := TRUE;
try
inherited DoShow;
finally
FChangingActivationState := FALSE;
end;
end;
function TdfsGradientForm.GetSystemColorBitDepth: integer;
var
DC: HDC;
begin
DC := GetDC(0);
try
Result := (GetDeviceCaps(DC, PLANES) * GetDeviceCaps(DC, BITSPIXEL));
finally
ReleaseDC(0, DC);
end;
end;
function TdfsGradientForm.GetInhibitGradient: boolean;
begin
Result := Win98Check or (SystemIs16Color and (not Paint16Color));
end;
procedure TdfsGradientForm.SetPaint16Color(const Value: boolean);
begin
if FPaint16Color <> Value then
begin
FPaint16Color := Value;
InvalidateCaption;
end;
end;
procedure TdfsGradientForm.SetCaptionFont(const Value: TFont);
begin
FCaptionFont.Assign(Value);
UseSystemCaptionFont := FALSE;
UpdateCaptionFont;
end;
function TdfsGradientForm.GetSysCaptionLogFont: TLogFont;
var
NCM: TNonClientMetrics;
begin
FillChar(Result, SizeOf(Result), #0);
NCM.cbSize := SizeOf(NCM);
if SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, @NCM, 0) then
begin
if BorderStyle in [bsToolWindow, bsSizeToolWin] then
Result := NCM.lfSmCaptionFont
else
Result := NCM.lfCaptionFont;
end;
end;
procedure TdfsGradientForm.SetUseSystemCaptionFont(const Value: boolean);
begin
if FUseSystemCaptionFont <> Value then
begin
FUseSystemCaptionFont := Value;
UpdateCaptionFont;
end;
end;
procedure TdfsGradientForm.UpdateCaptionFont;
var
CF: TLogFont;
NCM: TNonClientMetrics;
FS: TFontStyles;
begin
CF := GetSysCaptionLogFont;
if FUseSystemCaptionFont then
begin
NCM.cbSize := SizeOf(NCM);
SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, @NCM, 0);
{$IFDEF DFS_COMPILER_3_UP}
FCaptionFont.Charset := TFontCharset(CF.lfCharSet);
{$ENDIF}
FCaptionFont.Name := CF.lfFaceName;
FCaptionFont.Height := CF.lfHeight;
case CF.lfPitchAndFamily and $F of
VARIABLE_PITCH: FCaptionFont.Pitch := fpVariable;
FIXED_PITCH: FCaptionFont.Pitch := fpFixed;
else
FCaptionFont.Pitch := fpDefault;
end;
FS := [];
if CF.lfWeight >= FW_BOLD then
Include(FS, fsBold);
if CF.lfItalic = 1 then
Include(FS, fsItalic);
if CF.lfUnderline = 1 then
Include(FS, fsUnderline);
if CF.lfStrikeOut = 1 then
Include(FS, fsStrikeOut);
FCaptionFont.Style := FS;
end else
FCaptionFont.Height := CF.lfHeight;
InvalidateCaption;
end;
procedure TdfsGradientForm.SetInactiveLogo(const Value: TBitmap);
begin
FInactiveLogo.Assign(Value);
InvalidateCaption;
end;
procedure TdfsGradientForm.SetLogo(const Value: TBitmap);
begin
FLogo.Assign(Value);
InvalidateCaption;
end;
procedure TdfsGradientForm.SetLogoAlign(const Value: TGFLogoAlign);
begin
if FLogoAlign <> Value then
begin
FLogoAlign := Value;
InvalidateCaption;
end;
end;
procedure TdfsGradientForm.SetLogoLayered(const Value: Boolean);
begin
if FLogoLayered <> Value then
begin
FLogoLayered := Value;
InvalidateCaption;
end;
end;
procedure TdfsGradientForm.InvalidateCaption;
begin
if HandleAllocated and not InhibitGradient then
begin
// Make the non client area repaint.
SetWindowPos(Handle, 0, 0, 0, 0, 0, SWP_DRAWFRAME or SWP_NOACTIVATE or
SWP_NOMOVE or SWP_NOSIZE or SWP_NOZORDER);
end;
end;
initialization
EntrancyFlag := FALSE;
end.