Files
wxWidgets/contrib/src/fl/newbmpbtn.cpp
Julian Smart 45da7759b8 Applied [ 516382 ] FL toolbar patch
This patch addresses following issues on FL dynamic
toolbar:

* Removes duplicate code in
wxNewBitmapButton::RenderLabelImage(). (The code
between line 452-500 and 502-550 is the same.)

* Adds wxNewBitmapButton::Enable() own method to proper
drawing of enabled/disabled buttons.

* Changes "focused" state (of wxNewBitmapButton)
implementation from EVT_MOTION to
EVT_ENTER/LEAVE_WINDOW pair, because capturing mouse in
EVT_MOTION handler blocks some key events (control
characters, function keys), so underlynig app doesn't
receive all key events when mouse pointer is above
toolbar button.
Also "prev" state member variables are removed by this
patch (and IMHO they should be removed regardles on
accepting this patch - they are initalized and used
only in wxNewBitmapButton::OnMouseMove() method).
(Note: Blocking some keys can be only MSW problem, but
I can't verify it.)

* Adds EVT_LEFT_DCLICK handler to wxNewBitmapButton -
without this handler, toolbar button loses second
clicks if user presses left mouse button "fast enough".
Unfortunately, it doesn't handle drawing of
pressed/released button - it's only one event in
contrast to EVT_LEFT_UP/DOWN.

* Enables tooltips (in proper
wxDynamicToolBar::AddTool() method).


git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@14467 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
2002-03-06 19:05:25 +00:00

805 lines
21 KiB
C++

/////////////////////////////////////////////////////////////////////////////
// Name: newbmpbtn.cpp
// Purpose: wxNewBitmapButton enhanced bitmap button class.
// Author: Aleksandras Gluchovas
// Modified by:
// Created: ??/09/98
// RCS-ID: $Id$
// Copyright: (c) Aleksandras Gluchovas
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
#ifdef __GNUG__
#pragma implementation "newbmpbtn.h"
#endif
// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif
#include "wx/fl/newbmpbtn.h"
#include "wx/utils.h" // import wxMin,wxMax macros
///////////// button-label rendering helpers //////////////////
static int* create_array( int width, int height, int fill = 0 )
{
int* array = new int[width*height];
int len = width*height;
int i;
for ( i = 0; i != len; ++i )
array[i] = fill;
return array;
}
#define GET_ELEM(array,x,y) (array[width*(y)+(x)])
#define MIN_COLOR_DIFF 10
#define IS_IN_ARRAY(x,y) ( (x) < width && (y) < height && (x) >= 0 && (y) >= 0 )
#define GET_RED(col) col & 0xFF
#define GET_GREEN(col) (col >> 8) & 0xFF
#define GET_BLUE(col) (col >> 16) & 0xFF
#define MAKE_INT_COLOR(red,green,blue) ( (red) | \
( ( (green) << 8 ) & 0xFF00 ) | \
( ( (blue) << 16) & 0xFF0000) \
)
#define IS_GREATER(col1,col2) ( ( (GET_RED(col1) ) > (GET_RED(col2) ) + MIN_COLOR_DIFF ) && \
( (GET_GREEN(col1)) > (GET_GREEN(col2)) + MIN_COLOR_DIFF ) && \
( (GET_BLUE(col1) ) > (GET_BLUE(col2) ) + MIN_COLOR_DIFF ) \
)
#define MASK_BG 0
#define MASK_DARK 1
#define MASK_LIGHT 2
// helper function, used internally
static void gray_out_pixmap( int* src, int* dest, int width, int height )
{
// assuming the pixels along the edges are of the background color
int x = 0;
int y = 1;
do
{
int cur = GET_ELEM(src,x,y);
if ( IS_IN_ARRAY(x-1,y-1) )
{
int upperElem = GET_ELEM(src,x-1,y-1);
// if the upper element is lighter than current
if ( IS_GREATER(upperElem,cur) )
{
GET_ELEM(dest,x,y) = MASK_DARK;
}
else
// if the current element is ligher than the upper
if ( IS_GREATER(cur,upperElem) )
{
GET_ELEM(dest,x,y) = MASK_LIGHT;
}
else
{
if ( GET_ELEM(dest,x-1,y-1) == MASK_LIGHT )
GET_ELEM(dest,x,y) = MASK_BG;
if ( GET_ELEM(dest,x-1,y-1 ) == MASK_DARK )
GET_ELEM(dest,x,y) = MASK_DARK;
else
GET_ELEM(dest,x,y) = MASK_BG;
}
}
// go zig-zag
if ( IS_IN_ARRAY(x+1,y-1) )
{
++x;
--y;
}
else
{
while ( IS_IN_ARRAY(x-1,y+1) )
{
--x;
++y;
}
if ( IS_IN_ARRAY(x,y+1) )
{
++y;
continue;
}
else
{
if ( IS_IN_ARRAY(x+1,y) )
{
++x;
continue;
}
else break;
}
}
} while (1);
}
// algorithm for making the image look "grayed" (e.g. disabled button)
// NOTE:: used GetPixel(), which is Windows-Only!
void gray_out_image_on_dc( wxDC& dc, int width, int height )
{
// assuming the pixels along the edges are of the background color
wxColour bgCol;
dc.GetPixel( 0, 0, &bgCol );
wxPen darkPen ( wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW),1, wxSOLID );
wxPen lightPen( wxSystemSettings::GetColour(wxSYS_COLOUR_3DHIGHLIGHT),1, wxSOLID );
wxPen bgPen ( bgCol, 1, wxSOLID );
int* src = create_array( width, height, MASK_BG );
int* dest = create_array( width, height, MASK_BG );
int x, y;
for ( y = 0; y != height; ++y )
{
for ( x = 0; x != width; ++x )
{
wxColour col;
dc.GetPixel( x,y, &col );
GET_ELEM(src,x,y) = MAKE_INT_COLOR( col.Red(), col.Green(), col.Blue() );
}
}
gray_out_pixmap( src, dest, width, height );
for ( y = 0; y != height; ++y )
{
for ( x = 0; x != width; ++x )
{
int mask = GET_ELEM(dest,x,y);
switch (mask)
{
case MASK_BG : { dc.SetPen( bgPen );
dc.DrawPoint( x,y ); break;
}
case MASK_DARK : { dc.SetPen( darkPen );
dc.DrawPoint( x,y ); break;
}
case MASK_LIGHT : { dc.SetPen( lightPen );
dc.DrawPoint( x,y ); break;
}
default : break;
}
}
}
delete [] src;
delete [] dest;
}
///////////////////////////////
/***** Implementation for class wxNewBitmapButton *****/
IMPLEMENT_DYNAMIC_CLASS(wxNewBitmapButton, wxPanel)
BEGIN_EVENT_TABLE( wxNewBitmapButton, wxPanel )
EVT_LEFT_DOWN ( wxNewBitmapButton::OnLButtonDown )
EVT_LEFT_UP ( wxNewBitmapButton::OnLButtonUp )
EVT_LEFT_DCLICK ( wxNewBitmapButton::OnLButtonDClick )
EVT_ENTER_WINDOW( wxNewBitmapButton::OnMouseEnter )
EVT_LEAVE_WINDOW( wxNewBitmapButton::OnMouseLeave )
EVT_SIZE ( wxNewBitmapButton::OnSize )
EVT_PAINT( wxNewBitmapButton::OnPaint )
//EVT_KILL_FOCUS( wxNewBitmapButton::OnKillFocus )
EVT_ERASE_BACKGROUND( wxNewBitmapButton::OnEraseBackground )
END_EVENT_TABLE()
wxNewBitmapButton::wxNewBitmapButton( const wxBitmap& labelBitmap,
const wxString& labelText,
int alignText,
bool isFlat,
int firedEventType,
int marginX,
int marginY,
int textToLabelGap,
bool isSticky)
: mTextToLabelGap ( textToLabelGap ),
mMarginX( marginX ),
mMarginY( marginY ),
mTextAlignment( alignText ),
mIsSticky( isSticky ),
mIsFlat( isFlat ),
mLabelText( labelText ),
mImageFileType( wxBITMAP_TYPE_INVALID ),
mDepressedBmp( labelBitmap ),
mpDepressedImg( NULL ),
mpPressedImg ( NULL ),
mpDisabledImg ( NULL ),
mpFocusedImg ( NULL ),
mDragStarted ( FALSE ),
mIsPressed ( FALSE ),
mIsInFocus( FALSE ),
mHasFocusedBmp( FALSE ),
mFiredEventType( firedEventType ),
mBlackPen( wxColour( 0, 0, 0), 1, wxSOLID ),
mDarkPen ( wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW), 1, wxSOLID ),
mGrayPen ( wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE), 1, wxSOLID ),
mLightPen( wxSystemSettings::GetColour(wxSYS_COLOUR_3DHIGHLIGHT), 1, wxSOLID ),
mIsCreated( FALSE ),
mSizeIsSet( FALSE )
{
}
wxNewBitmapButton::wxNewBitmapButton( const wxString& bitmapFileName,
const wxBitmapType bitmapFileType,
const wxString& labelText,
int alignText,
bool isFlat,
int firedEventType,
int marginX,
int marginY,
int textToLabelGap,
bool isSticky)
: mTextToLabelGap ( 2 ),
mMarginX( 2 ),
mMarginY( 2 ),
mTextAlignment( alignText ),
mIsSticky( FALSE ),
mIsFlat( isFlat ),
mLabelText( labelText ),
mImageFileName( bitmapFileName ),
mImageFileType( bitmapFileType ),
mpDepressedImg( NULL ),
mpPressedImg ( NULL ),
mpDisabledImg ( NULL ),
mpFocusedImg ( NULL ),
mDragStarted ( FALSE ),
mIsPressed ( FALSE ),
mIsInFocus ( FALSE ),
mHasFocusedBmp( FALSE ),
mFiredEventType( wxEVT_COMMAND_MENU_SELECTED ),
mBlackPen( wxColour( 0, 0, 0), 1, wxSOLID ),
mDarkPen ( wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW), 1, wxSOLID ),
mGrayPen ( wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE), 1, wxSOLID ),
mLightPen( wxSystemSettings::GetColour(wxSYS_COLOUR_3DHIGHLIGHT), 1, wxSOLID ),
mIsCreated( FALSE ),
mSizeIsSet( FALSE )
{
}
wxNewBitmapButton::~wxNewBitmapButton(void)
{
DestroyLabels();
}
void wxNewBitmapButton::DrawShade( int outerLevel,
wxDC& dc,
wxPen& upperLeftSidePen,
wxPen& lowerRightSidePen )
{
wxBitmap* pBmp = GetStateLabel();
int x = mMarginX - (outerLevel + 1);
int y = mMarginY - (outerLevel + 1);
int height = pBmp->GetHeight() + (outerLevel + 1)*2 - 1;
int width = pBmp->GetWidth() + (outerLevel + 1)*2 - 1;
dc.SetPen( upperLeftSidePen );
dc.DrawLine( x,y, x + width, y );
dc.DrawLine( x,y, x, y + height );
dc.SetPen( lowerRightSidePen );
dc.DrawLine( x + width, y, x + width, y + height + 1 );
dc.DrawLine( x, y + height, x + width, y + height );
}
void wxNewBitmapButton::DestroyLabels()
{
if ( mpDepressedImg ) delete mpDepressedImg;
if ( mpPressedImg ) delete mpPressedImg;
if ( mpDisabledImg ) delete mpDisabledImg;
if ( mpFocusedImg ) delete mpFocusedImg;
mpDepressedImg = NULL;
mpPressedImg = NULL;
mpDisabledImg = NULL;
mpFocusedImg = NULL;
}
wxBitmap* wxNewBitmapButton::GetStateLabel()
{
if ( IsEnabled() )
{
if ( mIsPressed )
{
return mpPressedImg;
}
else
{
if ( mIsInFocus )
{
if ( mHasFocusedBmp )
return mpFocusedImg;
else
return mpDepressedImg;
}
else
return mpDepressedImg;
}
}
else
return mpDisabledImg;
}
static const unsigned char _gDisableImage[] = { 0x55,0xAA,0x55,0xAA,
0x55,0xAA,0x55,0xAA,
0x55,0xAA,0x55,0xAA,
0x55,0xAA,0x55,0xAA
};
void wxNewBitmapButton::RenderLabelImage( wxBitmap*& destBmp, wxBitmap* srcBmp,
bool isEnabled, bool isPressed )
{
if ( destBmp != 0 ) return;
// render labels on-demand
wxMemoryDC srcDc;
srcDc.SelectObject( *srcBmp );
bool hasText = ( mTextAlignment != NB_NO_TEXT ) &&
( mLabelText.length() != 0 );
bool hasImage = (mTextAlignment != NB_NO_IMAGE);
wxSize destDim;
wxPoint txtPos;
wxPoint imgPos;
if ( hasText )
{
long txtWidth, txtHeight;
srcDc.SetFont( wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT) );
srcDc.GetTextExtent( mLabelText, &txtWidth, &txtHeight );
if ( mTextAlignment == NB_ALIGN_TEXT_RIGHT )
{
destDim.x = srcBmp->GetWidth() + 2*mTextToLabelGap + txtWidth;
destDim.y =
wxMax( srcBmp->GetHeight(), txtHeight );
txtPos.x = srcBmp->GetWidth() + mTextToLabelGap;
txtPos.y = (destDim.y - txtHeight)/2;
imgPos.x = 0;
imgPos.y = (destDim.y - srcBmp->GetHeight())/2;
}
else
if ( mTextAlignment == NB_ALIGN_TEXT_BOTTOM )
{
destDim.x =
wxMax( srcBmp->GetWidth(), txtWidth );
destDim.y = srcBmp->GetHeight() + mTextToLabelGap + txtHeight;
txtPos.x = (destDim.x - txtWidth)/2;
txtPos.y = srcBmp->GetHeight() + mTextToLabelGap;
imgPos.x = (destDim.x - srcBmp->GetWidth())/2;
imgPos.y = 0;
}
else
{
wxFAIL_MSG("Unsupported FL alignment type detected in wxNewBitmapButton::RenderLabelImage()");
}
}
else
{
imgPos.x = 0;
imgPos.y = 0;
destDim.x = srcBmp->GetWidth();
destDim.y = srcBmp->GetHeight();
}
destBmp = new wxBitmap( int(destDim.x), int(destDim.y) );
wxMemoryDC destDc;
destDc.SelectObject( *destBmp );
wxBrush grayBrush( wxSystemSettings::GetColour( wxSYS_COLOUR_3DFACE), wxSOLID );
wxPen nullPen( wxColour(0,0,0), 1, wxTRANSPARENT );
destDc.SetBrush( grayBrush );
destDc.SetPen( nullPen );
destDc.DrawRectangle( 0,0, destDim.x+1, destDim.y+1 );
if ( isPressed )
{
++imgPos.x; ++imgPos.y;
++txtPos.x; ++txtPos.y;
}
if ( hasImage )
{
destDc.Blit( imgPos.x, imgPos.y,
srcBmp->GetWidth()+1,
srcBmp->GetHeight()+1,
&srcDc, 0,0, wxCOPY,TRUE );
}
if ( hasText )
{
wxWindow* pTopWnd = this;
do
{
wxWindow* pParent = pTopWnd->GetParent();
if ( pParent == 0 )
break;
pTopWnd = pParent;
} while (1);
destDc.SetFont( wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT) );
if ( isEnabled )
{
destDc.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_BTNTEXT) );
}
else
{
destDc.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW) );
}
destDc.SetTextBackground( wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE) );
destDc.DrawText( mLabelText, txtPos.x, txtPos.y );
}
if ( !isEnabled ){
#ifdef __WXMSW__ // This is currently MSW specific
gray_out_image_on_dc( destDc, destDim.x, destDim.y );
#else
wxBrush checkerBrush( wxBitmap( (const char*)_gDisableImage,8,8) );
checkerBrush.SetColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) );
destDc.SetBrush( checkerBrush );
destDc.DrawRectangle( imgPos.x, imgPos.y, srcBmp->GetWidth()+1, srcBmp->GetHeight()+1);
#endif
}
// adjust button size to fit the new dimensions of the label
if ( !mSizeIsSet && 0 )
{
mSizeIsSet = TRUE;
SetSize( -1,-1,
destBmp->GetWidth() + mMarginX*2,
destBmp->GetHeight() + mMarginY*2, 0
);
}
}
void wxNewBitmapButton::RenderAllLabelImages()
{
if ( !mIsCreated )
return;
RenderLabelImage( mpDisabledImg, &mDepressedBmp, FALSE );
RenderLabelImage( mpPressedImg, &mDepressedBmp, TRUE, TRUE );
RenderLabelImage( mpDepressedImg, &mDepressedBmp, TRUE, FALSE );
if ( mHasFocusedBmp )
{
RenderLabelImage( mpFocusedImg, &mFocusedBmp, TRUE, FALSE );
}
}
void wxNewBitmapButton::RenderLabelImages()
{
if ( !mIsCreated )
return;
if ( !IsEnabled() )
{
RenderLabelImage( mpDisabledImg, &mDepressedBmp, FALSE );
}
else
if ( mIsPressed )
RenderLabelImage( mpPressedImg, &mDepressedBmp, TRUE, TRUE );
else
{
if ( mIsInFocus )
{
if ( mHasFocusedBmp )
RenderLabelImage( mpFocusedImg, &mFocusedBmp, TRUE, FALSE );
else
RenderLabelImage( mpDepressedImg, &mDepressedBmp, TRUE, FALSE );
}
else
RenderLabelImage( mpDepressedImg, &mDepressedBmp, TRUE, FALSE );
}
}
bool wxNewBitmapButton::Enable(bool enable)
{
if ( enable != m_isEnabled )
{
if ( mIsInFocus )
{
mIsInFocus = FALSE;
}
if ( mIsPressed )
{
mIsPressed = FALSE;
}
Refresh();
}
return wxPanel::Enable( enable );
}
void wxNewBitmapButton::DrawDecorations( wxDC& dc )
{
if ( mIsFlat )
{
DrawShade( 1, dc, mGrayPen, mGrayPen );
if ( mIsInFocus )
{
if ( mIsPressed )
DrawShade( 0, dc, mDarkPen, mLightPen );
else
DrawShade( 0, dc, mLightPen, mDarkPen );
}
else
DrawShade( 0, dc, mGrayPen, mGrayPen );
}
else
{
if ( mIsPressed )
{
DrawShade( 0, dc, mDarkPen, mGrayPen );
DrawShade( 1, dc, mBlackPen, mLightPen );
}
else
{
DrawShade( 0, dc, mGrayPen, mDarkPen );
DrawShade( 1, dc, mLightPen, mBlackPen );
}
}
}
void wxNewBitmapButton::SetLabel(const wxBitmap& labelBitmap,
const wxString& labelText )
{
DestroyLabels();
mLabelText = labelText;
mDepressedBmp = labelBitmap;
//RenderLabelImages();
RenderAllLabelImages();
}
void wxNewBitmapButton::SetAlignments( int alignText,
int marginX,
int marginY,
int textToLabelGap)
{
DestroyLabels();
mMarginX = marginX;
mMarginY = marginY;
mTextAlignment = alignText;
mTextToLabelGap = textToLabelGap;
//RenderLabelImages();
RenderAllLabelImages();
}
// event handlers
void wxNewBitmapButton::OnLButtonDown( wxMouseEvent& event )
{
mDragStarted = TRUE;
mIsPressed = TRUE;
Refresh();
}
void wxNewBitmapButton::OnLButtonUp( wxMouseEvent& event )
{
if ( !mDragStarted )
return;
mDragStarted = FALSE;
mIsPressed = FALSE;
Refresh();
if ( IsInWindow( event.m_x, event.m_y ) )
{
// fire event, if mouse was released
// within the bounds of button
wxCommandEvent cmd( mFiredEventType, GetId() );
GetParent()->ProcessEvent( cmd );
}
}
void wxNewBitmapButton::OnLButtonDClick( wxMouseEvent& event )
{
if ( IsInWindow( event.m_x, event.m_y ) )
{
// fire event, if mouse was released
// within the bounds of button
wxCommandEvent cmd( mFiredEventType, GetId() );
GetParent()->ProcessEvent( cmd );
mDragStarted = FALSE;
mIsPressed = FALSE;
Refresh();
}
}
bool wxNewBitmapButton::IsInWindow( int x, int y )
{
int width, height;
GetSize( &width, &height );
return ( x >= 0 && y >= 0 &&
x < width &&
y < height );
}
void wxNewBitmapButton::OnMouseEnter( wxMouseEvent& event )
{
bool prevIsInFocus = mIsInFocus;
if ( !mIsInFocus )
{
mIsInFocus = TRUE;
}
if ( prevIsInFocus != mIsInFocus )
{
Refresh();
}
}
void wxNewBitmapButton::OnMouseLeave( wxMouseEvent& event )
{
bool prevIsInFocus = mIsInFocus;
bool prevIsPressed = mIsPressed;
if ( mIsInFocus )
{
mIsInFocus = FALSE;
mIsPressed = FALSE;
}
if ( prevIsInFocus != mIsInFocus || prevIsPressed != mIsPressed )
{
Refresh();
}
}
void wxNewBitmapButton::OnSize( wxSizeEvent& event )
{
//Reshape();
}
void wxNewBitmapButton::Reshape( )
{
bool wasCreated = mIsCreated;
mIsCreated = TRUE;
if ( !wasCreated )
{
// in the case of loading button from stream, check if we
// have non-empty image-file name, load if possible
if ( mImageFileName != "" )
{
mDepressedBmp.LoadFile( mImageFileName, mImageFileType );
//wxMessageBox("Image Loaded!!!");
}
//RenderLabelImages();
RenderAllLabelImages();
wxBitmap* pCurImg = GetStateLabel();
int w = pCurImg->GetWidth(),
h = pCurImg->GetHeight();
SetSize( 0,0, w + mMarginX*2, h + mMarginY*2 , 0 );
}
}
void wxNewBitmapButton::DrawLabel( wxDC& dc )
{
wxBitmap* pCurBmp = GetStateLabel();
if ( pCurBmp == NULL )
{
wxSizeEvent evt;
OnSize( evt ); // fake it up!
//RenderLabelImages();
pCurBmp = GetStateLabel();
}
wxMemoryDC mdc;
mdc.SelectObject( *pCurBmp );
dc.Blit( mMarginX, mMarginY,
pCurBmp->GetWidth(),
pCurBmp->GetHeight(),
&mdc, 0,0, wxCOPY
);
mdc.SelectObject( wxNullBitmap );
}
void wxNewBitmapButton::OnPaint( wxPaintEvent& event )
{
wxPaintDC dc(this);
// first, make sure images for current state are prepared
//RenderLabelImages();
DrawLabel( dc );
DrawDecorations( dc );
}
void wxNewBitmapButton::OnEraseBackground( wxEraseEvent& event )
{
// do nothing
}
void wxNewBitmapButton::OnKillFocus( wxFocusEvent& event )
{
// useless
wxMessageBox("kill-focus for button!");
}