1
/////////////////////////////////////////////////////////////////////////////
2
// Name: msw/tbar95.cpp
4
// Author: Julian Smart
7
// RCS-ID: $Id: tbar95.cpp,v 1.157.2.8 2006/04/01 18:16:17 JS Exp $
8
// Copyright: (c) Julian Smart
9
// Licence: wxWindows licence
10
/////////////////////////////////////////////////////////////////////////////
12
// ============================================================================
14
// ============================================================================
16
// ----------------------------------------------------------------------------
18
// ----------------------------------------------------------------------------
20
#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
21
#pragma implementation "tbar95.h"
24
// For compilers that support precompilation, includes "wx.h".
25
#include "wx/wxprec.h"
35
#include "wx/dynarray.h"
36
#include "wx/settings.h"
37
#include "wx/bitmap.h"
38
#include "wx/dcmemory.h"
39
#include "wx/control.h"
42
#if wxUSE_TOOLBAR && wxUSE_TOOLBAR_NATIVE && !defined(__SMARTPHONE__)
44
#include "wx/toolbar.h"
45
#include "wx/sysopt.h"
48
#include "wx/msw/private.h"
51
#include "wx/msw/uxtheme.h"
54
// include <commctrl.h> "properly"
55
#include "wx/msw/wrapcctl.h"
57
#include "wx/app.h" // for GetComCtl32Version
59
// ----------------------------------------------------------------------------
61
// ----------------------------------------------------------------------------
63
// these standard constants are not always defined in compilers headers
67
#define TBSTYLE_LIST 0x1000
68
#define TBSTYLE_FLAT 0x0800
71
#ifndef TBSTYLE_TRANSPARENT
72
#define TBSTYLE_TRANSPARENT 0x8000
75
#ifndef TBSTYLE_TOOLTIPS
76
#define TBSTYLE_TOOLTIPS 0x0100
81
#define TB_SETSTYLE (WM_USER + 56)
82
#define TB_GETSTYLE (WM_USER + 57)
86
#define TB_HITTEST (WM_USER + 69)
90
#define TB_GETMAXSIZE (WM_USER + 83)
93
// these values correspond to those used by comctl32.dll
94
#define DEFAULTBITMAPX 16
95
#define DEFAULTBITMAPY 15
97
// ----------------------------------------------------------------------------
99
// ----------------------------------------------------------------------------
101
IMPLEMENT_DYNAMIC_CLASS(wxToolBar, wxControl)
113
style ( wxNO_BORDER | wxTB_HORIZONTAL)
122
BEGIN_EVENT_TABLE(wxToolBar, wxToolBarBase)
123
EVT_MOUSE_EVENTS(wxToolBar::OnMouseEvent)
124
EVT_SYS_COLOUR_CHANGED(wxToolBar::OnSysColourChanged)
125
EVT_ERASE_BACKGROUND(wxToolBar::OnEraseBackground)
128
// ----------------------------------------------------------------------------
130
// ----------------------------------------------------------------------------
132
class wxToolBarTool : public wxToolBarToolBase
135
wxToolBarTool(wxToolBar *tbar,
137
const wxString& label,
138
const wxBitmap& bmpNormal,
139
const wxBitmap& bmpDisabled,
141
wxObject *clientData,
142
const wxString& shortHelp,
143
const wxString& longHelp)
144
: wxToolBarToolBase(tbar, id, label, bmpNormal, bmpDisabled, kind,
145
clientData, shortHelp, longHelp)
150
wxToolBarTool(wxToolBar *tbar, wxControl *control)
151
: wxToolBarToolBase(tbar, control)
156
virtual void SetLabel(const wxString& label)
158
if ( label == m_label )
161
wxToolBarToolBase::SetLabel(label);
163
// we need to update the label shown in the toolbar because it has a
164
// pointer to the internal buffer of the old label
166
// TODO: use TB_SETBUTTONINFO
169
// set/get the number of separators which we use to cover the space used by
170
// a control in the toolbar
171
void SetSeparatorsCount(size_t count) { m_nSepCount = count; }
172
size_t GetSeparatorsCount() const { return m_nSepCount; }
177
DECLARE_NO_COPY_CLASS(wxToolBarTool)
181
// ============================================================================
183
// ============================================================================
185
// ----------------------------------------------------------------------------
187
// ----------------------------------------------------------------------------
189
wxToolBarToolBase *wxToolBar::CreateTool(int id,
190
const wxString& label,
191
const wxBitmap& bmpNormal,
192
const wxBitmap& bmpDisabled,
194
wxObject *clientData,
195
const wxString& shortHelp,
196
const wxString& longHelp)
198
return new wxToolBarTool(this, id, label, bmpNormal, bmpDisabled, kind,
199
clientData, shortHelp, longHelp);
202
wxToolBarToolBase *wxToolBar::CreateTool(wxControl *control)
204
return new wxToolBarTool(this, control);
207
// ----------------------------------------------------------------------------
208
// wxToolBar construction
209
// ----------------------------------------------------------------------------
211
void wxToolBar::Init()
214
m_disabledImgList = NULL;
218
m_defaultWidth = DEFAULTBITMAPX;
219
m_defaultHeight = DEFAULTBITMAPY;
224
bool wxToolBar::Create(wxWindow *parent,
229
const wxString& name)
231
// common initialisation
232
if ( !CreateControl(parent, id, pos, size, style, wxDefaultValidator, name) )
235
// MSW-specific initialisation
236
if ( !MSWCreateToolbar(pos, size) )
239
wxSetCCUnicodeFormat(GetHwnd());
241
// set up the colors and fonts
242
SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE));
243
SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
245
// workaround for flat toolbar on Windows XP classic style: we have to set
246
// the style after creating the control, doing it at creation time doesn't
249
if ( style & wxTB_FLAT )
251
LRESULT style = ::SendMessage(GetHwnd(), TB_GETSTYLE, 0, 0L);
253
if ( !(style & TBSTYLE_FLAT) )
255
::SendMessage(GetHwnd(), TB_SETSTYLE, 0, style | TBSTYLE_FLAT);
258
#endif // wxUSE_UXTHEME
263
bool wxToolBar::MSWCreateToolbar(const wxPoint& pos, const wxSize& size)
265
if ( !MSWCreateControl(TOOLBARCLASSNAME, wxEmptyString, pos, size) )
268
// toolbar-specific post initialisation
269
::SendMessage(GetHwnd(), TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0);
274
void wxToolBar::Recreate()
276
const HWND hwndOld = GetHwnd();
279
// we haven't been created yet, no need to recreate
283
// get the position and size before unsubclassing the old toolbar
284
const wxPoint pos = GetPosition();
285
const wxSize size = GetSize();
289
if ( !MSWCreateToolbar(pos, size) )
292
wxFAIL_MSG( _T("recreating the toolbar failed") );
297
// reparent all our children under the new toolbar
298
for ( wxWindowList::compatibility_iterator node = m_children.GetFirst();
300
node = node->GetNext() )
302
wxWindow *win = node->GetData();
303
if ( !win->IsTopLevel() )
304
::SetParent(GetHwndOf(win), GetHwnd());
307
// only destroy the old toolbar now -- after all the children had been
309
::DestroyWindow(hwndOld);
311
// it is for the old bitmap control and can't be used with the new one
314
::DeleteObject((HBITMAP) m_hBitmap);
318
if ( m_disabledImgList )
320
delete m_disabledImgList;
321
m_disabledImgList = NULL;
327
wxToolBar::~wxToolBar()
329
// we must refresh the frame size when the toolbar is deleted but the frame
330
// is not - otherwise toolbar leaves a hole in the place it used to occupy
331
wxFrame *frame = wxDynamicCast(GetParent(), wxFrame);
332
if ( frame && !frame->IsBeingDeleted() )
334
frame->SendSizeEvent();
339
::DeleteObject((HBITMAP) m_hBitmap);
342
delete m_disabledImgList;
345
wxSize wxToolBar::DoGetBestSize() const
350
if ( !::SendMessage(GetHwnd(), TB_GETMAXSIZE, 0, (LPARAM)&size) )
352
// maybe an old (< 0x400) Windows version? try to approximate the
353
// toolbar size ourselves
354
sizeBest = GetToolSize();
355
sizeBest.y += 2 * ::GetSystemMetrics(SM_CYBORDER); // Add borders
356
sizeBest.x *= GetToolsCount();
358
// reverse horz and vertical components if necessary
359
if ( HasFlag(wxTB_VERTICAL) )
362
sizeBest.x = sizeBest.y;
368
sizeBest.x = size.cx;
369
sizeBest.y = size.cy;
372
CacheBestSize(sizeBest);
376
WXDWORD wxToolBar::MSWGetStyle(long style, WXDWORD *exstyle) const
378
// toolbars never have border, giving one to them results in broken
380
WXDWORD msStyle = wxControl::MSWGetStyle
382
(style & ~wxBORDER_MASK) | wxBORDER_NONE, exstyle
385
// always include this one, it never hurts and setting it later only if we
386
// do have tooltips wouldn't work
387
msStyle |= TBSTYLE_TOOLTIPS;
389
if ( style & (wxTB_FLAT | wxTB_HORZ_LAYOUT) )
391
// static as it doesn't change during the program lifetime
392
static int s_verComCtl = wxApp::GetComCtl32Version();
394
// comctl32.dll 4.00 doesn't support the flat toolbars and using this
395
// style with 6.00 (part of Windows XP) leads to the toolbar with
396
// incorrect background colour - and not using it still results in the
397
// correct (flat) toolbar, so don't use it there
398
if ( s_verComCtl > 400 && s_verComCtl < 600 )
400
msStyle |= TBSTYLE_FLAT | TBSTYLE_TRANSPARENT;
403
if ( s_verComCtl >= 470 && style & wxTB_HORZ_LAYOUT )
405
msStyle |= TBSTYLE_LIST;
409
if ( style & wxTB_NODIVIDER )
410
msStyle |= CCS_NODIVIDER;
412
if ( style & wxTB_NOALIGN )
413
msStyle |= CCS_NOPARENTALIGN;
415
if ( style & wxTB_VERTICAL )
421
// ----------------------------------------------------------------------------
422
// adding/removing tools
423
// ----------------------------------------------------------------------------
425
bool wxToolBar::DoInsertTool(size_t WXUNUSED(pos), wxToolBarToolBase *tool)
427
// nothing special to do here - we really create the toolbar buttons in
431
InvalidateBestSize();
435
bool wxToolBar::DoDeleteTool(size_t pos, wxToolBarToolBase *tool)
437
// the main difficulty we have here is with the controls in the toolbars:
438
// as we (sometimes) use several separators to cover up the space used by
439
// them, the indices are not the same for us and the toolbar
441
// first determine the position of the first button to delete: it may be
442
// different from pos if we use several separators to cover the space used
444
wxToolBarToolsList::compatibility_iterator node;
445
for ( node = m_tools.GetFirst(); node; node = node->GetNext() )
447
wxToolBarToolBase *tool2 = node->GetData();
450
// let node point to the next node in the list
451
node = node->GetNext();
456
if ( tool2->IsControl() )
458
pos += ((wxToolBarTool *)tool2)->GetSeparatorsCount() - 1;
462
// now determine the number of buttons to delete and the area taken by them
463
size_t nButtonsToDelete = 1;
465
// get the size of the button we're going to delete
467
if ( !::SendMessage(GetHwnd(), TB_GETITEMRECT, pos, (LPARAM)&r) )
469
wxLogLastError(_T("TB_GETITEMRECT"));
472
int width = r.right - r.left;
474
if ( tool->IsControl() )
476
nButtonsToDelete = ((wxToolBarTool *)tool)->GetSeparatorsCount();
477
width *= nButtonsToDelete;
478
tool->GetControl()->Destroy();
481
// do delete all buttons
482
m_nButtons -= nButtonsToDelete;
483
while ( nButtonsToDelete-- > 0 )
485
if ( !::SendMessage(GetHwnd(), TB_DELETEBUTTON, pos, 0) )
487
wxLogLastError(wxT("TB_DELETEBUTTON"));
495
// and finally reposition all the controls after this button (the toolbar
496
// takes care of all normal items)
497
for ( /* node -> first after deleted */ ; node; node = node->GetNext() )
499
wxToolBarToolBase *tool2 = node->GetData();
500
if ( tool2->IsControl() )
503
wxControl *control = tool2->GetControl();
504
control->GetPosition(&x, NULL);
505
control->Move(x - width, wxDefaultCoord);
509
InvalidateBestSize();
513
void wxToolBar::CreateDisabledImageList()
515
// as we can't use disabled image list with older versions of comctl32.dll,
516
// don't even bother creating it
517
if ( wxTheApp->GetComCtl32Version() >= 470 )
519
// search for the first disabled button img in the toolbar, if any
520
for ( wxToolBarToolsList::compatibility_iterator
521
node = m_tools.GetFirst(); node; node = node->GetNext() )
523
wxToolBarToolBase *tool = node->GetData();
524
wxBitmap bmpDisabled = tool->GetDisabledBitmap();
525
if ( bmpDisabled.Ok() )
527
m_disabledImgList = new wxImageList
531
bmpDisabled.GetMask() != NULL,
538
// we don't have any disabled bitmaps
541
m_disabledImgList = NULL;
544
bool wxToolBar::Realize()
546
const size_t nTools = GetToolsCount();
553
const bool isVertical = HasFlag(wxTB_VERTICAL);
555
bool doRemap, doRemapBg, doTransparent;
559
doTransparent = false;
561
if (wxSystemOptions::GetOptionInt(wxT("msw.remap")) == 2)
563
doRemapBg = doRemap = false;
564
doTransparent = true;
567
{ doRemap = !wxSystemOptions::HasOption(wxT("msw.remap"))
568
|| wxSystemOptions::GetOptionInt(wxT("msw.remap")) == 1;
569
doRemapBg = !doRemap;
570
doTransparent = false;
574
// delete all old buttons, if any
575
for ( size_t pos = 0; pos < m_nButtons; pos++ )
577
if ( !::SendMessage(GetHwnd(), TB_DELETEBUTTON, 0, 0) )
579
wxLogDebug(wxT("TB_DELETEBUTTON failed"));
583
// First, add the bitmap: we use one bitmap for all toolbar buttons
584
// ----------------------------------------------------------------
586
wxToolBarToolsList::compatibility_iterator node;
590
if ( HasFlag(wxTB_NOICONS) )
592
// no icons, don't leave space for them
596
else // do show icons
598
// if we already have a bitmap, we'll replace the existing one --
599
// otherwise we'll install a new one
600
HBITMAP oldToolBarBitmap = (HBITMAP)m_hBitmap;
602
sizeBmp.x = m_defaultWidth;
603
sizeBmp.y = m_defaultHeight;
605
const wxCoord totalBitmapWidth = m_defaultWidth *
606
wx_truncate_cast(wxCoord, nTools),
607
totalBitmapHeight = m_defaultHeight;
609
// Create a bitmap and copy all the tool bitmaps to it
610
wxMemoryDC dcAllButtons;
611
wxBitmap bitmap(totalBitmapWidth, totalBitmapHeight);
612
dcAllButtons.SelectObject(bitmap);
614
dcAllButtons.SetBackground(wxBrush(wxColour(192,192,192)));
617
dcAllButtons.SetBackground(*wxTRANSPARENT_BRUSH);
619
dcAllButtons.SetBackground(wxBrush(GetBackgroundColour()));
621
dcAllButtons.Clear();
623
m_hBitmap = bitmap.GetHBITMAP();
624
HBITMAP hBitmap = (HBITMAP)m_hBitmap;
629
dcAllButtons.SelectObject(wxNullBitmap);
631
// Even if we're not remapping the bitmap
632
// content, we still have to remap the background.
633
hBitmap = (HBITMAP)MapBitmap((WXHBITMAP) hBitmap,
634
totalBitmapWidth, totalBitmapHeight);
636
dcAllButtons.SelectObject(bitmap);
640
#endif // !__WXWINCE__
642
// the button position
645
// the number of buttons (not separators)
648
CreateDisabledImageList();
649
for ( node = m_tools.GetFirst(); node; node = node->GetNext() )
651
wxToolBarToolBase *tool = node->GetData();
652
if ( tool->IsButton() )
654
const wxBitmap& bmp = tool->GetNormalBitmap();
656
const int w = bmp.GetWidth();
657
const int h = bmp.GetHeight();
661
int xOffset = wxMax(0, (m_defaultWidth - w)/2);
662
int yOffset = wxMax(0, (m_defaultHeight - h)/2);
664
// notice the last parameter: do use mask
665
dcAllButtons.DrawBitmap(bmp, x + xOffset, yOffset, true);
669
wxFAIL_MSG( _T("invalid tool button bitmap") );
672
// also deal with disabled bitmap if we want to use them
673
if ( m_disabledImgList )
675
wxBitmap bmpDisabled = tool->GetDisabledBitmap();
676
#if wxUSE_IMAGE && wxUSE_WXDIB
677
if ( !bmpDisabled.Ok() )
679
// no disabled bitmap specified but we still need to
680
// fill the space in the image list with something, so
681
// we grey out the normal bitmap
683
wxCreateGreyedImage(bmp.ConvertToImage(), imgGreyed);
685
// we need to have light grey background colour for
686
// MapBitmap() to work correctly
687
for ( int y = 0; y < h; y++ )
689
for ( int x = 0; x < w; x++ )
691
if ( imgGreyed.IsTransparent(x, y) )
692
imgGreyed.SetRGB(x, y,
694
wxLIGHT_GREY->Green(),
695
wxLIGHT_GREY->Blue());
699
bmpDisabled = wxBitmap(imgGreyed);
701
#endif // wxUSE_IMAGE
703
MapBitmap(bmpDisabled.GetHBITMAP(), w, h);
705
m_disabledImgList->Add(bmpDisabled);
708
// still inc width and number of buttons because otherwise the
709
// subsequent buttons will all be shifted which is rather confusing
710
// (and like this you'd see immediately which bitmap was bad)
716
dcAllButtons.SelectObject(wxNullBitmap);
718
// don't delete this HBITMAP!
719
bitmap.SetHBITMAP(0);
723
// Map to system colours
724
hBitmap = (HBITMAP)MapBitmap((WXHBITMAP) hBitmap,
725
totalBitmapWidth, totalBitmapHeight);
730
bool addBitmap = true;
732
if ( oldToolBarBitmap )
734
#ifdef TB_REPLACEBITMAP
735
if ( wxApp::GetComCtl32Version() >= 400 )
737
TBREPLACEBITMAP replaceBitmap;
738
replaceBitmap.hInstOld = NULL;
739
replaceBitmap.hInstNew = NULL;
740
replaceBitmap.nIDOld = (UINT) oldToolBarBitmap;
741
replaceBitmap.nIDNew = (UINT) hBitmap;
742
replaceBitmap.nButtons = nButtons;
743
if ( !::SendMessage(GetHwnd(), TB_REPLACEBITMAP,
744
0, (LPARAM) &replaceBitmap) )
746
wxFAIL_MSG(wxT("Could not replace the old bitmap"));
749
::DeleteObject(oldToolBarBitmap);
755
#endif // TB_REPLACEBITMAP
757
// we can't replace the old bitmap, so we will add another one
758
// (awfully inefficient, but what else to do?) and shift the bitmap
759
// indices accordingly
762
bitmapId = m_nButtons;
766
if ( addBitmap ) // no old bitmap or we can't replace it
768
TBADDBITMAP addBitmap;
770
addBitmap.nID = (UINT) hBitmap;
771
if ( ::SendMessage(GetHwnd(), TB_ADDBITMAP,
772
(WPARAM) nButtons, (LPARAM)&addBitmap) == -1 )
774
wxFAIL_MSG(wxT("Could not add bitmap to toolbar"));
778
if ( m_disabledImgList )
780
HIMAGELIST oldImageList = (HIMAGELIST)
781
::SendMessage(GetHwnd(),
782
TB_SETDISABLEDIMAGELIST,
784
(LPARAM)GetHimagelistOf(m_disabledImgList));
786
// delete previous image list if any
788
::DeleteObject( oldImageList );
792
// don't call SetToolBitmapSize() as we don't want to change the values of
793
// m_defaultWidth/Height
794
if ( !::SendMessage(GetHwnd(), TB_SETBITMAPSIZE, 0,
795
MAKELONG(sizeBmp.x, sizeBmp.y)) )
797
wxLogLastError(_T("TB_SETBITMAPSIZE"));
800
// Next add the buttons and separators
801
// -----------------------------------
803
TBBUTTON *buttons = new TBBUTTON[nTools];
805
// this array will hold the indices of all controls in the toolbar
806
wxArrayInt controlIds;
808
bool lastWasRadio = false;
810
for ( node = m_tools.GetFirst(); node; node = node->GetNext() )
812
wxToolBarToolBase *tool = node->GetData();
814
// don't add separators to the vertical toolbar with old comctl32.dll
815
// versions as they didn't handle this properly
816
if ( isVertical && tool->IsSeparator() &&
817
wxApp::GetComCtl32Version() <= 472 )
823
TBBUTTON& button = buttons[i];
825
wxZeroMemory(button);
827
bool isRadio = false;
828
switch ( tool->GetStyle() )
830
case wxTOOL_STYLE_CONTROL:
831
button.idCommand = tool->GetId();
832
// fall through: create just a separator too
834
case wxTOOL_STYLE_SEPARATOR:
835
button.fsState = TBSTATE_ENABLED;
836
button.fsStyle = TBSTYLE_SEP;
839
case wxTOOL_STYLE_BUTTON:
840
if ( !HasFlag(wxTB_NOICONS) )
841
button.iBitmap = bitmapId;
843
if ( HasFlag(wxTB_TEXT) )
845
const wxString& label = tool->GetLabel();
846
if ( !label.empty() )
848
button.iString = (int)label.c_str();
852
button.idCommand = tool->GetId();
854
if ( tool->IsEnabled() )
855
button.fsState |= TBSTATE_ENABLED;
856
if ( tool->IsToggled() )
857
button.fsState |= TBSTATE_CHECKED;
859
switch ( tool->GetKind() )
862
button.fsStyle = TBSTYLE_CHECKGROUP;
866
// the first item in the radio group is checked by
867
// default to be consistent with wxGTK and the menu
869
button.fsState |= TBSTATE_CHECKED;
871
if (tool->Toggle(true))
873
DoToggleTool(tool, true);
876
else if (tool->IsToggled())
878
wxToolBarToolsList::compatibility_iterator nodePrev = node->GetPrevious();
879
int prevIndex = i - 1;
882
TBBUTTON& prevButton = buttons[prevIndex];
883
wxToolBarToolBase *tool = nodePrev->GetData();
884
if ( !tool->IsButton() || tool->GetKind() != wxITEM_RADIO )
887
if ( tool->Toggle(false) )
889
DoToggleTool(tool, false);
891
prevButton.fsState = TBSTATE_ENABLED;
892
nodePrev = nodePrev->GetPrevious();
901
button.fsStyle = TBSTYLE_CHECK;
905
wxFAIL_MSG( _T("unexpected toolbar button kind") );
909
button.fsStyle = TBSTYLE_BUTTON;
916
lastWasRadio = isRadio;
921
if ( !::SendMessage(GetHwnd(), TB_ADDBUTTONS, (WPARAM)i, (LPARAM)buttons) )
923
wxLogLastError(wxT("TB_ADDBUTTONS"));
928
// Deal with the controls finally
929
// ------------------------------
931
// adjust the controls size to fit nicely in the toolbar
934
for ( node = m_tools.GetFirst(); node; node = node->GetNext(), index++ )
936
wxToolBarToolBase *tool = node->GetData();
938
// we calculate the running y coord for vertical toolbars so we need to
939
// get the items size for all items but for the horizontal ones we
940
// don't need to deal with the non controls
941
bool isControl = tool->IsControl();
942
if ( !isControl && !isVertical )
945
// note that we use TB_GETITEMRECT and not TB_GETRECT because the
946
// latter only appeared in v4.70 of comctl32.dll
948
if ( !::SendMessage(GetHwnd(), TB_GETITEMRECT,
949
index, (LPARAM)(LPRECT)&r) )
951
wxLogLastError(wxT("TB_GETITEMRECT"));
956
// can only be control if isVertical
957
y += r.bottom - r.top;
962
wxControl *control = tool->GetControl();
964
wxSize size = control->GetSize();
966
// the position of the leftmost controls corner
967
int left = wxDefaultCoord;
969
// TB_SETBUTTONINFO message is only supported by comctl32.dll 4.71+
970
#ifdef TB_SETBUTTONINFO
971
// available in headers, now check whether it is available now
973
if ( wxApp::GetComCtl32Version() >= 471 )
975
// set the (underlying) separators width to be that of the
978
tbbi.cbSize = sizeof(tbbi);
979
tbbi.dwMask = TBIF_SIZE;
980
tbbi.cx = (WORD)size.x;
981
if ( !::SendMessage(GetHwnd(), TB_SETBUTTONINFO,
982
tool->GetId(), (LPARAM)&tbbi) )
984
// the id is probably invalid?
985
wxLogLastError(wxT("TB_SETBUTTONINFO"));
989
#endif // comctl32.dll 4.71
990
// TB_SETBUTTONINFO unavailable
992
// try adding several separators to fit the controls width
993
int widthSep = r.right - r.left;
999
tbb.fsState = TBSTATE_ENABLED;
1000
tbb.fsStyle = TBSTYLE_SEP;
1002
size_t nSeparators = size.x / widthSep;
1003
for ( size_t nSep = 0; nSep < nSeparators; nSep++ )
1005
if ( !::SendMessage(GetHwnd(), TB_INSERTBUTTON,
1006
index, (LPARAM)&tbb) )
1008
wxLogLastError(wxT("TB_INSERTBUTTON"));
1014
// remember the number of separators we used - we'd have to
1015
// delete all of them later
1016
((wxToolBarTool *)tool)->SetSeparatorsCount(nSeparators);
1018
// adjust the controls width to exactly cover the separators
1019
control->SetSize((nSeparators + 1)*widthSep, wxDefaultCoord);
1022
// position the control itself correctly vertically
1023
int height = r.bottom - r.top;
1024
int diff = height - size.y;
1027
// the control is too high, resize to fit
1028
control->SetSize(wxDefaultCoord, height - 2);
1039
y += height + 2*GetMargins().y;
1041
else // horizontal toolbar
1043
if ( left == wxDefaultCoord )
1049
control->Move(left, top + (diff + 1) / 2);
1052
// the max index is the "real" number of buttons - i.e. counting even the
1053
// separators which we added just for aligning the controls
1058
if ( m_maxRows == 0 )
1060
// if not set yet, only one row
1064
else if ( m_nButtons > 0 ) // vertical non empty toolbar
1066
if ( m_maxRows == 0 )
1068
// if not set yet, have one column
1069
SetRows(m_nButtons);
1073
InvalidateBestSize();
1079
// ----------------------------------------------------------------------------
1081
// ----------------------------------------------------------------------------
1083
bool wxToolBar::MSWCommand(WXUINT WXUNUSED(cmd), WXWORD id)
1085
wxToolBarToolBase *tool = FindById((int)id);
1089
bool toggled = false; // just to suppress warnings
1091
if ( tool->CanBeToggled() )
1093
LRESULT state = ::SendMessage(GetHwnd(), TB_GETSTATE, id, 0);
1094
toggled = (state & TBSTATE_CHECKED) != 0;
1096
// ignore the event when a radio button is released, as this doesn't
1097
// seem to happen at all, and is handled otherwise
1098
if ( tool->GetKind() == wxITEM_RADIO && !toggled )
1101
tool->Toggle(toggled);
1102
UnToggleRadioGroup(tool);
1105
// OnLeftClick() can veto the button state change - for buttons which
1106
// may be toggled only, of couse
1107
if ( !OnLeftClick((int)id, toggled) && tool->CanBeToggled() )
1110
tool->Toggle(!toggled);
1112
::SendMessage(GetHwnd(), TB_CHECKBUTTON, id, MAKELONG(!toggled, 0));
1118
bool wxToolBar::MSWOnNotify(int WXUNUSED(idCtrl),
1120
WXLPARAM *WXUNUSED(result))
1123
// First check if this applies to us
1124
NMHDR *hdr = (NMHDR *)lParam;
1126
// the tooltips control created by the toolbar is sometimes Unicode, even
1127
// in an ANSI application - this seems to be a bug in comctl32.dll v5
1128
UINT code = hdr->code;
1129
if ( (code != (UINT) TTN_NEEDTEXTA) && (code != (UINT) TTN_NEEDTEXTW) )
1132
HWND toolTipWnd = (HWND)::SendMessage((HWND)GetHWND(), TB_GETTOOLTIPS, 0, 0);
1133
if ( toolTipWnd != hdr->hwndFrom )
1136
LPTOOLTIPTEXT ttText = (LPTOOLTIPTEXT)lParam;
1137
int id = (int)ttText->hdr.idFrom;
1139
wxToolBarToolBase *tool = FindById(id);
1143
return HandleTooltipNotify(code, lParam, tool->GetShortHelp());
1145
wxUnusedVar(lParam);
1151
// ----------------------------------------------------------------------------
1153
// ----------------------------------------------------------------------------
1155
void wxToolBar::SetToolBitmapSize(const wxSize& size)
1157
wxToolBarBase::SetToolBitmapSize(size);
1159
::SendMessage(GetHwnd(), TB_SETBITMAPSIZE, 0, MAKELONG(size.x, size.y));
1162
void wxToolBar::SetRows(int nRows)
1164
if ( nRows == m_maxRows )
1166
// avoid resizing the frame uselessly
1170
// TRUE in wParam means to create at least as many rows, FALSE -
1173
::SendMessage(GetHwnd(), TB_SETROWS,
1174
MAKEWPARAM(nRows, !(GetWindowStyle() & wxTB_VERTICAL)),
1182
// The button size is bigger than the bitmap size
1183
wxSize wxToolBar::GetToolSize() const
1185
// TB_GETBUTTONSIZE is supported from version 4.70
1186
#if defined(_WIN32_IE) && (_WIN32_IE >= 0x300 ) \
1187
&& !( defined(__GNUWIN32__) && !wxCHECK_W32API_VERSION( 1, 0 ) ) \
1188
&& !defined (__DIGITALMARS__)
1189
if ( wxApp::GetComCtl32Version() >= 470 )
1191
DWORD dw = ::SendMessage(GetHwnd(), TB_GETBUTTONSIZE, 0, 0);
1193
return wxSize(LOWORD(dw), HIWORD(dw));
1196
#endif // comctl32.dll 4.70+
1199
return wxSize(m_defaultWidth + 8, m_defaultHeight + 7);
1204
wxToolBarToolBase *GetItemSkippingDummySpacers(const wxToolBarToolsList& tools,
1207
wxToolBarToolsList::compatibility_iterator current = tools.GetFirst();
1209
for ( ; current ; current = current->GetNext() )
1212
return current->GetData();
1214
wxToolBarTool *tool = (wxToolBarTool *)current->GetData();
1215
size_t separators = tool->GetSeparatorsCount();
1217
// if it is a normal button, sepcount == 0, so skip 1 item (the button)
1218
// otherwise, skip as many items as the separator count, plus the
1220
index -= separators ? separators + 1 : 1;
1226
wxToolBarToolBase *wxToolBar::FindToolForPosition(wxCoord x, wxCoord y) const
1231
int index = (int)::SendMessage(GetHwnd(), TB_HITTEST, 0, (LPARAM)&pt);
1232
// MBN: when the point ( x, y ) is close to the toolbar border
1233
// TB_HITTEST returns m_nButtons ( not -1 )
1234
if ( index < 0 || (size_t)index >= m_nButtons )
1236
// it's a separator or there is no tool at all there
1237
return (wxToolBarToolBase *)NULL;
1240
// when TB_SETBUTTONINFO is available (both during compile- and run-time),
1241
// we don't use the dummy separators hack
1242
#ifdef TB_SETBUTTONINFO
1243
if ( wxApp::GetComCtl32Version() >= 471 )
1245
return m_tools.Item((size_t)index)->GetData();
1248
#endif // TB_SETBUTTONINFO
1250
return GetItemSkippingDummySpacers( m_tools, (size_t) index );
1254
void wxToolBar::UpdateSize()
1256
wxPoint pos = GetPosition();
1257
::SendMessage(GetHwnd(), TB_AUTOSIZE, 0, 0);
1258
if (pos != GetPosition())
1261
// In case Realize is called after the initial display (IOW the programmer
1262
// may have rebuilt the toolbar) give the frame the option of resizing the
1263
// toolbar to full width again, but only if the parent is a frame and the
1264
// toolbar is managed by the frame. Otherwise assume that some other
1265
// layout mechanism is controlling the toolbar size and leave it alone.
1266
wxFrame *frame = wxDynamicCast(GetParent(), wxFrame);
1267
if ( frame && frame->GetToolBar() == this )
1269
frame->SendSizeEvent();
1273
// ----------------------------------------------------------------------------
1275
// ---------------------------------------------------------------------------
1277
void wxToolBar::SetWindowStyleFlag(long style)
1279
// the style bits whose changes force us to recreate the toolbar
1280
static const long MASK_NEEDS_RECREATE = wxTB_TEXT | wxTB_NOICONS;
1282
const long styleOld = GetWindowStyle();
1284
wxToolBarBase::SetWindowStyleFlag(style);
1286
// don't recreate an empty toolbar: not only this is unnecessary, but it is
1287
// also fatal as we'd then try to recreate the toolbar when it's just being
1289
if ( GetToolsCount() &&
1290
(style & MASK_NEEDS_RECREATE) != (styleOld & MASK_NEEDS_RECREATE) )
1292
// to remove the text labels, simply re-realizing the toolbar is enough
1293
// but I don't know of any way to add the text to an existing toolbar
1294
// other than by recreating it entirely
1299
// ----------------------------------------------------------------------------
1301
// ----------------------------------------------------------------------------
1303
void wxToolBar::DoEnableTool(wxToolBarToolBase *tool, bool enable)
1305
::SendMessage(GetHwnd(), TB_ENABLEBUTTON,
1306
(WPARAM)tool->GetId(), (LPARAM)MAKELONG(enable, 0));
1309
void wxToolBar::DoToggleTool(wxToolBarToolBase *tool, bool toggle)
1311
::SendMessage(GetHwnd(), TB_CHECKBUTTON,
1312
(WPARAM)tool->GetId(), (LPARAM)MAKELONG(toggle, 0));
1315
void wxToolBar::DoSetToggle(wxToolBarToolBase *WXUNUSED(tool), bool WXUNUSED(toggle))
1317
// VZ: AFAIK, the button has to be created either with TBSTYLE_CHECK or
1318
// without, so we really need to delete the button and recreate it here
1319
wxFAIL_MSG( _T("not implemented") );
1322
// ----------------------------------------------------------------------------
1324
// ----------------------------------------------------------------------------
1326
// Responds to colour changes, and passes event on to children.
1327
void wxToolBar::OnSysColourChanged(wxSysColourChangedEvent& event)
1329
wxRGBToColour(m_backgroundColour, ::GetSysColor(COLOR_BTNFACE));
1331
// Remap the buttons
1334
// Relayout the toolbar
1335
int nrows = m_maxRows;
1336
m_maxRows = 0; // otherwise SetRows() wouldn't do anything
1341
// let the event propagate further
1345
void wxToolBar::OnMouseEvent(wxMouseEvent& event)
1347
if (event.Leaving() && m_pInTool)
1354
if ( event.RightDown() )
1356
// find the tool under the mouse
1358
event.GetPosition(&x, &y);
1360
wxToolBarToolBase *tool = FindToolForPosition(x, y);
1361
OnRightClick(tool ? tool->GetId() : -1, x, y);
1369
// This handler is required to allow the toolbar to be set to a non-default
1370
// colour: for example, when it must blend in with a notebook page.
1371
void wxToolBar::OnEraseBackground(wxEraseEvent& event)
1373
wxColour bgCol = GetBackgroundColour();
1380
// notice that this 'dumb' implementation may cause flicker for some of the
1381
// controls in which case they should intercept wxEraseEvent and process it
1382
// themselves somehow
1385
::GetClientRect(GetHwnd(), &rect);
1387
HBRUSH hBrush = ::CreateSolidBrush(wxColourToRGB(bgCol));
1389
HDC hdc = GetHdcOf((*event.GetDC()));
1392
int mode = ::SetMapMode(hdc, MM_TEXT);
1395
::FillRect(hdc, &rect, hBrush);
1396
::DeleteObject(hBrush);
1399
::SetMapMode(hdc, mode);
1403
bool wxToolBar::HandleSize(WXWPARAM WXUNUSED(wParam), WXLPARAM lParam)
1405
// calculate our minor dimension ourselves - we're confusing the standard
1406
// logic (TB_AUTOSIZE) with our horizontal toolbars and other hacks
1408
if ( ::SendMessage(GetHwnd(), TB_GETITEMRECT, 0, (LPARAM)&r) )
1412
if ( GetWindowStyle() & wxTB_VERTICAL )
1414
w = r.right - r.left;
1417
w *= (m_nButtons + m_maxRows - 1)/m_maxRows;
1424
if (HasFlag( wxTB_FLAT ))
1425
h = r.bottom - r.top - 3;
1427
h = r.bottom - r.top;
1430
// FIXME: hardcoded separator line height...
1431
h += HasFlag(wxTB_NODIVIDER) ? 4 : 6;
1436
if ( MAKELPARAM(w, h) != lParam )
1438
// size really changed
1442
// message processed
1449
bool wxToolBar::HandlePaint(WXWPARAM wParam, WXLPARAM lParam)
1451
// erase any dummy separators which we used for aligning the controls if
1454
// first of all, do we have any controls at all?
1455
wxToolBarToolsList::compatibility_iterator node;
1456
for ( node = m_tools.GetFirst(); node; node = node->GetNext() )
1458
if ( node->GetData()->IsControl() )
1464
// no controls, nothing to erase
1468
// prepare the DC on which we'll be drawing
1469
wxClientDC dc(this);
1470
dc.SetBrush(wxBrush(GetBackgroundColour(), wxSOLID));
1471
dc.SetPen(*wxTRANSPARENT_PEN);
1474
if ( !::GetUpdateRect(GetHwnd(), &r, FALSE) )
1476
// nothing to redraw anyhow
1481
wxCopyRECTToRect(r, rectUpdate);
1483
dc.SetClippingRegion(rectUpdate);
1485
// draw the toolbar tools, separators &c normally
1486
wxControl::MSWWindowProc(WM_PAINT, wParam, lParam);
1488
// for each control in the toolbar find all the separators intersecting it
1491
// NB: this is really the only way to do it as we don't know if a separator
1492
// corresponds to a control (i.e. is a dummy one) or a real one
1494
for ( node = m_tools.GetFirst(); node; node = node->GetNext() )
1496
wxToolBarToolBase *tool = node->GetData();
1497
if ( tool->IsControl() )
1499
// get the control rect in our client coords
1500
wxControl *control = tool->GetControl();
1501
wxRect rectCtrl = control->GetRect();
1503
// iterate over all buttons
1505
int count = ::SendMessage(GetHwnd(), TB_BUTTONCOUNT, 0, 0);
1506
for ( int n = 0; n < count; n++ )
1508
// is it a separator?
1509
if ( !::SendMessage(GetHwnd(), TB_GETBUTTON,
1512
wxLogDebug(_T("TB_GETBUTTON failed?"));
1517
if ( tbb.fsStyle != TBSTYLE_SEP )
1520
// get the bounding rect of the separator
1522
if ( !::SendMessage(GetHwnd(), TB_GETITEMRECT,
1525
wxLogDebug(_T("TB_GETITEMRECT failed?"));
1530
// does it intersect the control?
1532
wxCopyRECTToRect(r, rectItem);
1533
if ( rectCtrl.Intersects(rectItem) )
1535
// yes, do erase it!
1536
dc.DrawRectangle(rectItem);
1538
// Necessary in case we use a no-paint-on-size
1539
// style in the parent: the controls can disappear
1540
control->Refresh(false);
1549
void wxToolBar::HandleMouseMove(WXWPARAM WXUNUSED(wParam), WXLPARAM lParam)
1551
wxCoord x = GET_X_LPARAM(lParam),
1552
y = GET_Y_LPARAM(lParam);
1553
wxToolBarToolBase* tool = FindToolForPosition( x, y );
1555
// cursor left current tool
1556
if( tool != m_pInTool && !tool )
1562
// cursor entered a tool
1563
if( tool != m_pInTool && tool )
1566
OnMouseEnter( tool->GetId() );
1570
WXLRESULT wxToolBar::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
1575
// we don't handle mouse moves, so always pass the message to
1576
// wxControl::MSWWindowProc (HandleMouseMove just calls OnMouseEnter)
1577
HandleMouseMove(wParam, lParam);
1581
if ( HandleSize(wParam, lParam) )
1587
if ( HandlePaint(wParam, lParam) )
1592
return wxControl::MSWWindowProc(nMsg, wParam, lParam);
1595
// ----------------------------------------------------------------------------
1596
// private functions
1597
// ----------------------------------------------------------------------------
1599
WXHBITMAP wxToolBar::MapBitmap(WXHBITMAP bitmap, int width, int height)
1605
wxLogLastError(_T("CreateCompatibleDC"));
1610
SelectInHDC bmpInHDC(hdcMem, (HBITMAP)bitmap);
1614
wxLogLastError(_T("SelectObject"));
1619
wxCOLORMAP *cmap = wxGetStdColourMap();
1621
for ( int i = 0; i < width; i++ )
1623
for ( int j = 0; j < height; j++ )
1625
COLORREF pixel = ::GetPixel(hdcMem, i, j);
1627
for ( size_t k = 0; k < wxSTD_COL_MAX; k++ )
1629
COLORREF col = cmap[k].from;
1630
if ( abs(GetRValue(pixel) - GetRValue(col)) < 10 &&
1631
abs(GetGValue(pixel) - GetGValue(col)) < 10 &&
1632
abs(GetBValue(pixel) - GetBValue(col)) < 10 )
1634
::SetPixel(hdcMem, i, j, cmap[k].to);
1643
// VZ: I leave here my attempts to map the bitmap to the system colours
1644
// faster by using BitBlt() even though it's broken currently - but
1645
// maybe someone else can finish it? It should be faster than iterating
1646
// over all pixels...
1648
MemoryHDC hdcMask, hdcDst;
1649
if ( !hdcMask || !hdcDst )
1651
wxLogLastError(_T("CreateCompatibleDC"));
1656
// create the target bitmap
1657
HBITMAP hbmpDst = ::CreateCompatibleBitmap(hdcDst, width, height);
1660
wxLogLastError(_T("CreateCompatibleBitmap"));
1665
// create the monochrome mask bitmap
1666
HBITMAP hbmpMask = ::CreateBitmap(width, height, 1, 1, 0);
1669
wxLogLastError(_T("CreateBitmap(mono)"));
1671
::DeleteObject(hbmpDst);
1676
SelectInHDC bmpInDst(hdcDst, hbmpDst),
1677
bmpInMask(hdcMask, hbmpMask);
1680
for ( n = 0; n < NUM_OF_MAPPED_COLOURS; n++ )
1682
// create the mask for this colour
1683
::SetBkColor(hdcMem, ColorMap[n].from);
1684
::BitBlt(hdcMask, 0, 0, width, height, hdcMem, 0, 0, SRCCOPY);
1686
// replace this colour with the target one in the dst bitmap
1687
HBRUSH hbr = ::CreateSolidBrush(ColorMap[n].to);
1688
HGDIOBJ hbrOld = ::SelectObject(hdcDst, hbr);
1690
::MaskBlt(hdcDst, 0, 0, width, height,
1693
MAKEROP4(PATCOPY, SRCCOPY));
1695
(void)::SelectObject(hdcDst, hbrOld);
1696
::DeleteObject(hbr);
1699
::DeleteObject((HBITMAP)bitmap);
1701
return (WXHBITMAP)hbmpDst;
1705
#endif // wxUSE_TOOLBAR && Win95