// - TGlyphDib by Barmak Shemirani Feb,17th,1999
// - This class is almost the same thing
as Borland's TGlyphButton
// - I have marked most of the differences
to TGlyphButton by
// three slashes "///"
// - TButtonEx is drived from this class.
// - A major difference to TGlyphButton
is in using TDib instead
// of TBtnBitmap because it
is easier to map colors in TDib.
// - It is also easier to drive from this
class.
// compared to TGlyphButton
// the following modifications has been
made:
// changed UpBmp, DownBmp,... to dibUp, dibDown,... (no reason)
// Changed paint functions to virtual
// added:
// TGlyphType::gtNegative and
dibNegative
// when mouse moves over the button above
values will be used
// added:
// TDib* dibOf(TGlyphType);
->to access private TDib from drived classes
// TFont* BtnFontOf();
->to access private TFont from drived classes
// added:
// SendNotificationEx();
// this function sends a BN_CLICKED message
to the parent.
// Drive classes can over-ride it to do
pre-processing
// added:
// MapColors(TDib* dib, TGlyphType);
// This will map the colors to the proper
system colors
// Drived classes can over-ride it
DEFINE_RESPONSE_TABLE1(TGlyphDib, TButton)
EV_WM_PAINT,
EV_WM_ERASEBKGND,
EV_WM_SETFOCUS,
EV_WM_KILLFOCUS,
EV_WM_GETFONT,
EV_WM_SETFONT,
EV_WM_GETDLGCODE,
EV_WM_LBUTTONDOWN,
EV_WM_LBUTTONDBLCLK,
EV_WM_LBUTTONUP,
EV_WM_MOUSEMOVE,
EV_WM_KEYDOWN,
EV_WM_KEYUP,
EV_WM_ENABLE,
EV_WM_CANCELMODE,
EV_MESSAGE(BM_SETSTATE, BmSetState),
EV_MESSAGE(BM_GETSTATE, BmGetState),
EV_MESSAGE(BM_SETSTYLE, BmSetStyle),
END_RESPONSE_TABLE;
const int LayoutMargin
= 4;
const int FaceToFocusRectDelta = -1;
const int BUTTONSTATE_PUSHED =
0x0004;
const int BUTTONSTATE_FOCUS
= 0x0008;
const int BUTTONSTYLE_MASK
= 0x00FF;
const long RopDSPDxax
= 0x00E20746L;
OWL_DIAGINFO;
DIAG_DECLARE_GROUP(OwlControl);
// General Controls diagnostic group
// Constructor of TGlyphDib - Use this
constructor to create a GlyphBtn from scratch.
TGlyphDib::TGlyphDib(TWindow* parent, int id, const
char far* text,
int X, int Y, int W, int H, bool isDefault,
TModule* module)
:
TButton(parent, id, text, X, Y, W, H, isDefault, module)
{
InitVars();
}
// Constructor of TGlyphDib - Use this
constructor to alias a glyph button control specified in a dialog template.
TGlyphDib::TGlyphDib(TWindow* parent, int resourceId, TModule*
module)
:
TButton(parent, resourceId, module)
{
InitVars();
}
// Method used to initialized variables
used by GlyphButton's implementation
void
TGlyphDib::InitVars()
{
///
dibUp = 0;
dibDown = 0;
dibFocus = 0;
dibNegative = 0;
BtnFont = new TDefaultGUIFont;
xText = yText = -1;
xGlyph = yGlyph = -1;
LayStyle = lsH_GST;
Set(biShowText);
}
// Destructor - Cleanup resources used
by Glyph Button object
TGlyphDib::~TGlyphDib()
{
///
delete dibUp;
delete dibDown;
delete dibFocus;
delete dibNegative;
delete BtnFont;
}
// Window proc. of control to handle messages
sent before OWL thunks
LRESULT CALLBACK OWL_EXPORT16
BButtonProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg) {
case WM_GETDLGCODE: {
uint32 style = GetWindowLong(hwnd,
GWL_STYLE);
uint16 btnStyle = uint16(LoUint16(style)
& BUTTONSTYLE_MASK);
if (btnStyle == BS_DEFPUSHBUTTON)
return DLGC_BUTTON|DLGC_DEFPUSHBUTTON;
else
return DLGC_BUTTON|DLGC_UNDEFPUSHBUTTON;
}
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
//
// Overriden virtual of TWindow - Fills
out information about the Window
// class associated with a glyph button.
// NOTE: The class information is based
on the system's "BUTTON" class.
//
void
TGlyphDib::GetWindowClass(WNDCLASS& wndClass)
{
// Grab a the attributes of the
native "BUTTON" control
//
if (::GetClassInfo(0,
"BUTTON", &wndClass)) {
wndClass.hInstance = *GetModule();
wndClass.lpszClassName = GetClassName();
wndClass.lpfnWndProc = BButtonProc;
}
else {
TControl::GetWindowClass(wndClass);
wndClass.style = CS_HREDRAW|CS_VREDRAW|CS_PARENTDC;
wndClass.hbrBackground = HBRUSH(COLOR_BTNFACE+1);
}
}
// Return name of window class associated
with a glyph button control.
char far*
TGlyphDib::GetClassName()
{
return OWL_GLYPHBTN_EX;
}
//
// Overriden virtual of TWindow - Updates
internal flags based on style of
// underlying window.
//
void
TGlyphDib::SetupWindow()
{
TButton::SetupWindow();
// Update state flags based on current
style
//
uint32 style = GetStyle();
if (style & BS_DEFPUSHBUTTON)
Set(biDefault);
if (style & WS_DISABLED)
Set(biDisabled);
}
// Specify the resource identifier of a
dib to be used as glyph
void
TGlyphDib::SetGlyph(TResId resId, TGlyphType type, TModule* module)
{
PRECONDITION(module != 0 ||
GetModule());
SetGlyphDib(new TDib(module ? *module : *GetModule(),
resId), type);
}
///you might need to over-ride this in
the drived class
void TGlyphDib::MapColors(TDib* dib, TGlyphType)
{
dib->MapUIColors(TDib::MapFace|TDib::MapText|TDib::MapShadow|TDib::MapHighlight);
}
// Specify a dib object to be used as glyph
// NOTE: The 'dib' parameter can be 0
to reset the glyph stored by the glyph button object.
void TGlyphDib::SetGlyphDib(TDib* dib, TGlyphType type)
{
PRECONDITION(!dib || dib->IsGDIObject());
///
MapColors(dib,type);
switch (type) {
case gtUp:
delete dibUp;
dibUp = dib;
break;
case gtDown:
delete dibDown;
dibDown = dib;
break;
case gtDisabled:
delete dibDisabled;
dibDisabled = dib;
break;
case gtFocus:
delete dibFocus;
dibFocus = dib;
break;
case gtNegative:
delete dibNegative;
dibNegative = dib;
break;
default:
break;
}
// Update status flag
//
if (dibUp)
Set(biShowGlyph);
else
Clear(biShowGlyph);
}
void TGlyphDib::EvPaint()
{
TPaintDC dc(*this);
TRect& rect = *(TRect*)&dc.Ps.rcPaint;
Paint(dc, dc.Ps.fErase, rect);
}
void TGlyphDib::Paint(TDC& dc, bool /*erase*/,
TRect& /*rect*/)
{
PaintButton(dc);
}
bool TGlyphDib::EvEraseBkgnd(HDC /*dc*/)
{
return true;
}
void TGlyphDib::EvSetFocus(THandle /*hWndLostFocus*/)
{
Set(biFocus);
Invalidate(true);
}
void TGlyphDib::EvKillFocus(THandle /*hWndGetFocus*/)
{
Clear(biFocus);
if (IsSet(biPushed))
ClearCapture();
else
Invalidate(true);
}
HFONT TGlyphDib::EvGetFont()
{
PRECONDITION(!BtnFont || BtnFont->IsGDIObject());
return BtnFont ? HFONT(*BtnFont) : HFONT(0);
}
void TGlyphDib::EvSetFont(HFONT hFont, bool redraw)
{
delete BtnFont;
BtnFont = new TFont(hFont);
if (redraw)
Invalidate();
}
uint TGlyphDib::EvGetDlgCode(MSG* /*msg*/)
{
if (IsSet(biDefault))
return DLGC_BUTTON|DLGC_DEFPUSHBUTTON;
else
return DLGC_BUTTON|DLGC_UNDEFPUSHBUTTON;
}
void TGlyphDib::EvLButtonDown(uint /*modKeys*/,
TPoint& /*point*/)
{
SetCapture();
SendMessage(BM_SETSTATE, TRUE);
if (!IsSet(biFocus))
SetFocus();
}
void TGlyphDib::EvLButtonDblClk(uint modKeys, TPoint&
point)
{
EvLButtonDown(modKeys, point);
}
void TGlyphDib::EvLButtonUp(uint /*modKeys*/,
TPoint& point)
{
if (GetCapture() == *this) {
ReleaseCapture();
SendMessage(BM_SETSTATE, FALSE);
TRect rect;
GetClientRect(rect);
if (rect.Contains(point))
SendNotificationEx();
}
}
void TGlyphDib::EvMouseMove(uint modKeys, TPoint& point)
{
if (modKeys & MK_LBUTTON && GetCapture()
== *this) {
TRect rect;
GetClientRect(rect);
if (rect.Contains(point))
SendMessage(BM_SETSTATE, TRUE);
else
SendMessage(BM_SETSTATE, FALSE);
}
}
void TGlyphDib::EvKeyDown(uint key, uint /*repeatCount*/,
uint /*flags*/)
{
if (key == VK_SPACE)
SendMessage(BM_SETSTATE, TRUE);
}
//I might want to catch this, I use this
instead of
//EV_NOTIFY_AT_CHILD(BN_CLICKED, BNClicked),
void TGlyphDib::SendNotificationEx()
{
SendNotification(::GetParent(*this), GetDlgCtrlID(),BN_CLICKED,
*this);
}
void TGlyphDib::EvKeyUp(uint key, uint /*repeatCount*/,
uint /*flags*/)
{
if (IsSet(biPushed) && key == VK_SPACE) {
SendMessage(BM_SETSTATE, FALSE);
SendNotificationEx();
}
}
// WM_ENABLE handler. Update internal flags
and invalidate control if necessary.
void TGlyphDib::EvEnable(bool enabled)
{
if (enabled) {
Clear(biDisabled);
}
else {
ClearCapture();
Set(biDisabled);
}
Invalidate(true);
}
void TGlyphDib::EvCancelMode()
{
ClearCapture();
}
TResult TGlyphDib::BmGetState(TParam1 /*param1*/,
TParam2 /*param2*/)
{
TResult result = 0;
if (IsSet(biPushed))
result |= BUTTONSTATE_PUSHED;
if (IsSet(biFocus))
result |= BUTTONSTATE_FOCUS;
return result;
}
TResult TGlyphDib::BmSetState(TParam1 param1, TParam2 /*param2*/)
{
if (param1) {
// Needs hilight look
//
if (!IsSet(biPushed)) {
Set(biPushed);
PaintNow();
}
}
else {
// Needs normal look
//
if (IsSet(biPushed)) {
Clear(biPushed);
PaintNow();
}
}
return 0;
}
// BM_SETSTYLE handler. Update internal
flags to match specified parameters and invalidate the window if necessary.
TResult TGlyphDib::BmSetStyle(TParam1 param1, TParam2 /*param2*/)
{
// Grab and splice the styles
//
uint32 style = GetStyle();
uint16 winStyle = HiUint16(style);
uint16 btnStyle = uint16(LoUint16(style) & BUTTONSTYLE_MASK);
// Check against passed in parameter
// NOTE: We only cater to PUSHBUTTON
and DEFPUSHBUTTON
//
The current definition so BS_PUSHBUTTON is 0L
//
if (LOWORD(param1) == BS_PUSHBUTTON &&
btnStyle != BS_PUSHBUTTON) {
// Make 'simple' push
button
//
TWindow::SetStyle(MAKELONG(param1, winStyle));
Clear(biDefault);
Invalidate(true);
}
else if (LOWORD(param1)==BS_DEFPUSHBUTTON
&& btnStyle != BS_DEFPUSHBUTTON){
// Make 'default' push
button
//
TWindow::SetStyle(MAKELONG(param1, winStyle));
Set(biDefault);
Invalidate(true);
}
#if defined(__DEBUG) || defined(__TRACE) ||
defined(__WARN)
if (LOWORD(param1) != BS_PUSHBUTTON &&
LOWORD(param1) != BS_DEFPUSHBUTTON)
TRACEX(OwlControl, 0,
"BmSetStyle: Invalid style specified");
#endif
return 0;
}
// Release caption if we are in 'capture'
mode. Reset internal flags
// appropriately.
void TGlyphDib::ClearCapture()
{
if (GetCapture() == *this)
ReleaseCapture();
Clear(biPushed);
Invalidate(true);
}
// Paint the button into a memory DC and
bitblt the final rendering to the
// specified 'dc'.
void TGlyphDib::PaintButton(TDC& dc)
{
TRect rect;
GetClientRect(rect);
// Create compatible bitmap
TBitmap memBmp(dc, rect.Width(), rect.Height());
// Create compatible memory DC
TMemoryDC memDC(dc);
// Select and init memory bitmap
memDC.SelectObject(memBmp);
// Save the rectangle for bitblt'ing
- 'rect' will be adjusted
// as we move from defaultFrame,
border and face...
TRect bltRect = rect;
// Paint the button into the memory
DC
PaintDefaultRect(memDC, rect);
PaintFrame(memDC, rect);
PaintFace(memDC, rect);
// Bitblt the button to the output
device
dc.BitBlt(bltRect, memDC, TPoint(bltRect.left, bltRect.top));
// Cleanup
memDC.RestoreBitmap();
}
// Draw the border of the button
void TGlyphDib::PaintFrame(TDC& dc, TRect& rect)
{
// Paint the border
//
TUIBorder uiBorder(rect, IsSet(biPushed) ? TUIBorder::ButtonDn
:
TUIBorder::ButtonUp);
uiBorder.Paint(dc);
// Shrink the rectangle to leave
the face
//
rect = uiBorder.GetClientRect();
}
// Draw a frame around the button if it's
a default push button.
void TGlyphDib::PaintDefaultRect(TDC& dc, TRect&
rect)
{
if (IsSet(biDefault)) {
if (rect.Width() > 2
&& rect.Height() > 2) {
TPen framePen(TColor::SysWindowFrame);
dc.SelectObject(framePen);
TBrush nullBrush(HBRUSH(::GetStockObject(NULL_BRUSH)));
dc.SelectObject(nullBrush);
dc.Rectangle(rect);
rect.Inflate(-1,
-1);
dc.RestoreBrush();
dc.RestorePen();
}
}
}
// Draw the face of the button [i.e. text
and glyph portions]
void TGlyphDib::PaintFace(TDC& dc, TRect& rect)
{
// Fill the background with the
face color
//
TBrush brush(TColor::Sys3dFace);
dc.FillRect(rect, brush);
// Grab the glyph and it's size
//
TDib* glyph = 0;
// Pointer to glyph
TRect glyphRect(0, 0,
0, 0);
// Size of glyph
if (IsSet(biShowGlyph)) {
// Start with the up
dib
//
glyph = dibUp;
// Switch to more appropriate
dib if applicable
//
if (IsSet(biPushed) && dibDown)
glyph = dibDown;
CHECK(glyph && glyph->IsGDIObject());
glyphRect.Set(0,
0, glyph->Width(), glyph->Height());
}
// Grab some information about the
text/caption
//
int len = 0;
// Length of Caption
TRect textRect(0, 0,
0, 0);
// Size of text
TAPointer<char> text;
// Pointer to caption dynamic buffer
TPointer<TFont> tmpFnt;
// Object wrapping font handle
TColor textColor;
// Color used for button's text
if (IsSet(biShowText)) {
len = GetWindowTextLength();
if (len) {
// Select
the font
//
if (!BtnFont) {
HFONT hFont = HFONT(::SendMessage(::GetParent(*this),
WM_GETFONT,
0, 0));
if (!hFont)
hFont =
HFONT(GetStockObject(TSystem::Has3dUI() ? ANSI_VAR_FONT :
SYSTEM_FONT));
if (hFont)
tmpFnt =
new TFont(hFont);
}
if (BtnFont) {
CHECK(BtnFont->IsGDIObject());
dc.SelectObject(*BtnFont);
}
else if (tmpFnt) {
CHECK(((TFont&)tmpFnt).IsGDIObject());
dc.SelectObject((TFont&)tmpFnt);
}
text = new char[len+1];
GetWindowText(text, len+1);
textRect.Set(0,
0, rect.Width() - glyphRect.Width(), SHRT_MAX);
// Display
text in proper color
//
// Under
NT 3.51 and Window 3.1, the system inexplicably returns values
// for
gray text that are the same as either button text (so text never
// looks
grayed) or as button face (so grayed text is invisible). The
// extra
if statement guards against those problems.
if (IsSet(biDisabled)) {
textColor = TColor::SysGrayText;
if ((textColor
== TColor::Sys3dFace.GetValue()) ||
(textColor == TColor::SysBtnText.GetValue())) {
textColor
= TColor::Sys3dShadow;
}
} else {
textColor = TColor::SysBtnText;
}
TColor oldColor = dc.SetTextColor(textColor);
dc.DrawText(text, len, textRect,
DT_WORDBREAK|DT_CALCRECT);
dc.SetTextColor(oldColor);
}
}
// If we have text and/or glyph,
lay them out and paint them
if (!textRect.IsNull() || !glyphRect.IsNull()) {
LayoutTextGlyph(rect, textRect, glyphRect);
// Offset things to
the lower right if we're in down
//
if (IsSet(biPushed)) {
if (!glyphRect.IsNull() &&
glyph == dibUp)
glyphRect.Offset(1,
1);
if (!textRect.IsNull())
textRect.Offset(1,
1);
}
// Paint the components
of the button
//
if (!glyphRect.IsNull()) {
PRECONDITION(glyph && glyph->IsGDIObject());
if(IsSet(biDisabled))
{
TBitmap bitmap(*glyph,
&TPalette((HPALETTE)::GetStockObject(DEFAULT_PALETTE)));
TUIFace uiFace(glyphRect,
bitmap);
uiFace.Paint(dc, TPoint(0,
0), IsSet(biDisabled)?TUIFace::Disabled:TUIFace::Normal,
IsSet(biPushed));
}
else
{
dc.SetDIBitsToDevice(glyphRect,TPoint(0,0),*glyph);
}
}
if (!textRect.IsNull()) {
int mode = dc.SetBkMode(TRANSPARENT);
TColor oldColor = dc.SetTextColor(textColor);
dc.DrawText(text, len, textRect,
DT_WORDBREAK);
dc.SetTextColor(oldColor);
dc.SetBkMode(mode);
}
}
// Paint the focus rect [if necessary]
//
if (IsSet(biFocus))
PaintFocusRect(dc, rect);
// Restore font
//
if (len && (BtnFont || tmpFnt))
dc.RestoreFont();
}
//
// Virtual routine invoked to retrieve
the placement of text and glyph
// when drawing the button.
// Override this routine to customize
the layout logic and support
// custom layout styles.
//
void
TGlyphDib::LayoutTextGlyph(const TRect& faceRect, TRect&
textRect,
TRect& glyphRect)
{
// Must have either text or a glyph
//
PRECONDITION(!textRect.IsNull() || !glyphRect.IsNull());
// First check for the case where
we've got either
// text or glyph - but not both
//
if (textRect.IsNull() || glyphRect.IsNull()) {
TRect& centerRect = textRect.IsNull() ?
glyphRect : textRect;
centerRect.Offset(faceRect.left, faceRect.top);
if (centerRect.Width() < faceRect.Width())
centerRect.Offset((faceRect.Width()
- centerRect.Width())/2, 0);
else
centerRect.right = faceRect.right;
if (centerRect.Height() < faceRect.Height())
centerRect.Offset(0,
(faceRect.Height() - centerRect.Height())/2);
else
centerRect.bottom = faceRect.bottom;
}
// Here we attempt to layout both
the glyph and text
//
else {
// Align upper left
corners of face, text and glyph rectangles
//
glyphRect.Offset(faceRect.left, faceRect.top);
textRect.Offset(faceRect.left, faceRect.top);
// Compute amount of
'extra' space, if any, and how to partition it
// between the two
items
//
int space = faceRect.Width() -
glyphRect.Width()- textRect.Width() -
LayoutMargin*2;
int gDelta;
int tDelta;
switch (LayStyle) {
case lsH_SGST: {
gDelta = space >= 0 ? LayoutMargin + space/3
:
LayoutMargin + space/2;
tDelta = space >= 0 ? gDelta + glyphRect.Width()
+ space/3 :
gDelta + glyphRect.Width();
}
break;
case lsH_GST: {
gDelta = space >= 0 ? LayoutMargin : LayoutMargin
+ space/2;
tDelta = space >= 0 ? gDelta + glyphRect.Width()
+ space/2 :
gDelta + glyphRect.Width();
}
break;
default:
break;
}
if (LayStyle == lsH_SGST || LayStyle ==
lsH_GST)
{
// Center
vertically
//
if (textRect.Height() <
faceRect.Height())
textRect.Offset(0,
(faceRect.Height() - textRect.Height())/2);
if (glyphRect.Height() <
faceRect.Height())
glyphRect.Offset(0,
(faceRect.Height() - glyphRect.Height())/2);
// Layout
horizontally
//
glyphRect.Offset(gDelta, 0);
textRect.Offset(tDelta, 0);
}
}
}
//
// Specify a 'style' describing how text
and glyph should be laid out.
// Invalidate the window if necessary.
//
void
TGlyphDib::SetLayoutStyle(TLayoutStyle style)
{
if (style != LayStyle) {
LayStyle = style;
if (GetHandle())
Invalidate();
}
}
//
// Sets text coordinates and invalidates
window if necessary
//
void
TGlyphDib::SetTextOrigin(int x, int y)
{
if (x != xText || y != yText) {
xText = x;
yText = y;
if (GetHandle())
Invalidate();
}
}
//
// Set the upper left corner of glyphs
and invalidates window if necessary
//
void
TGlyphDib::SetGlyphOrigin(int x, int y)
{
if (x != xGlyph || y != yGlyph) {
xGlyph = x;
yGlyph = y;
if (GetHandle())
Invalidate();
}
}
//
// Display a focus rectangle
//
void
TGlyphDib::PaintFocusRect(TDC& dc, const TRect&
faceRect)
{
PRECONDITION(IsSet(biFocus));
TRect focusRect = faceRect;
focusRect.Inflate(FaceToFocusRectDelta, FaceToFocusRectDelta);
dc.DrawFocusRect(focusRect);
}
//
// Repaints window right away by retrieving
a client DC and invoking the
// 'Paint' method.
//
void
TGlyphDib::PaintNow()
{
TRect rect;
GetClientRect(rect);
TClientDC dc(*this);
Paint(dc, false, rect);
}
///added
TDib* TGlyphDib::dibOf(TGlyphType gt)
{
switch(gt){
case gtUp:
return dibUp;
case gtDisabled: return
dibDisabled;
case gtDown:
return dibDown;
case gtFocus:
return dibFocus;
case gtNegative: return
dibNegative;
default: break;
}
return NULL;
}
TFont* TGlyphDib::BtnFontOf()
{
return BtnFont;
}