1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        msw/tbar95.cpp 
   4 // Author:      Julian Smart 
   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(_WIN32_WCE) || (_WIN32_WCE >= 400 && !wxUSE_POCKETPC_UI)) 
  44 #include "wx/toolbar.h" 
  45 #include "wx/sysopt.h" 
  47 #include "wx/msw/private.h" 
  49 // include <commctrl.h> "properly" 
  50 #include "wx/msw/wrapcctl.h" 
  52 #include "wx/app.h"         // for GetComCtl32Version 
  54 // ---------------------------------------------------------------------------- 
  55 // conditional compilation 
  56 // ---------------------------------------------------------------------------- 
  58 // wxWindows previously always considered that toolbar buttons have light grey 
  59 // (0xc0c0c0) background and so ignored any bitmap masks - however, this 
  60 // doesn't work with XPMs which then appear to have black background. To make 
  61 // this work, we must respect the bitmap masks - which we do now. This should 
  62 // be ok in any case, but to restore 100% compatible with the old version 
  63 // behaviour, you can set this to 0. 
  64 #define USE_BITMAP_MASKS 1 
  66 // ---------------------------------------------------------------------------- 
  68 // ---------------------------------------------------------------------------- 
  70 // these standard constants are not always defined in compilers headers 
  74     #define TBSTYLE_LIST            0x1000 
  75     #define TBSTYLE_FLAT            0x0800 
  78 #ifndef TBSTYLE_TRANSPARENT 
  79     #define TBSTYLE_TRANSPARENT     0x8000 
  82 #ifndef TBSTYLE_TOOLTIPS 
  83     #define TBSTYLE_TOOLTIPS        0x0100 
  88     #define TB_SETSTYLE             (WM_USER + 56) 
  89     #define TB_GETSTYLE             (WM_USER + 57) 
  93     #define TB_HITTEST              (WM_USER + 69) 
  96 // these values correspond to those used by comctl32.dll 
  97 #define DEFAULTBITMAPX   16 
  98 #define DEFAULTBITMAPY   15 
  99 #define DEFAULTBUTTONX   24 
 100 #define DEFAULTBUTTONY   24 
 101 #define DEFAULTBARHEIGHT 27 
 103 // ---------------------------------------------------------------------------- 
 105 // ---------------------------------------------------------------------------- 
 107 IMPLEMENT_DYNAMIC_CLASS(wxToolBar
, wxControl
) 
 119                 style ( wxNO_BORDER | wxTB_HORIZONTAL) 
 128 BEGIN_EVENT_TABLE(wxToolBar
, wxToolBarBase
) 
 129     EVT_MOUSE_EVENTS(wxToolBar::OnMouseEvent
) 
 130     EVT_SYS_COLOUR_CHANGED(wxToolBar::OnSysColourChanged
) 
 133 // ---------------------------------------------------------------------------- 
 135 // ---------------------------------------------------------------------------- 
 137 class wxToolBarTool 
: public wxToolBarToolBase
 
 140     wxToolBarTool(wxToolBar 
*tbar
, 
 142                   const wxString
& label
, 
 143                   const wxBitmap
& bmpNormal
, 
 144                   const wxBitmap
& bmpDisabled
, 
 146                   wxObject 
*clientData
, 
 147                   const wxString
& shortHelp
, 
 148                   const wxString
& longHelp
) 
 149         : wxToolBarToolBase(tbar
, id
, label
, bmpNormal
, bmpDisabled
, kind
, 
 150                             clientData
, shortHelp
, longHelp
) 
 155     wxToolBarTool(wxToolBar 
*tbar
, wxControl 
*control
) 
 156         : wxToolBarToolBase(tbar
, control
) 
 161     virtual void SetLabel(const wxString
& label
) 
 163         if ( label 
== m_label 
) 
 166         wxToolBarToolBase::SetLabel(label
); 
 168         // we need to update the label shown in the toolbar because it has a 
 169         // pointer to the internal buffer of the old label 
 171         // TODO: use TB_SETBUTTONINFO 
 174     // set/get the number of separators which we use to cover the space used by 
 175     // a control in the toolbar 
 176     void SetSeparatorsCount(size_t count
) { m_nSepCount 
= count
; } 
 177     size_t GetSeparatorsCount() const { return m_nSepCount
; } 
 182     DECLARE_NO_COPY_CLASS(wxToolBarTool
) 
 186 // ============================================================================ 
 188 // ============================================================================ 
 190 // ---------------------------------------------------------------------------- 
 192 // ---------------------------------------------------------------------------- 
 194 wxToolBarToolBase 
*wxToolBar::CreateTool(int id
, 
 195                                          const wxString
& label
, 
 196                                          const wxBitmap
& bmpNormal
, 
 197                                          const wxBitmap
& bmpDisabled
, 
 199                                          wxObject 
*clientData
, 
 200                                          const wxString
& shortHelp
, 
 201                                          const wxString
& longHelp
) 
 203     return new wxToolBarTool(this, id
, label
, bmpNormal
, bmpDisabled
, kind
, 
 204                              clientData
, shortHelp
, longHelp
); 
 207 wxToolBarToolBase 
*wxToolBar::CreateTool(wxControl 
*control
) 
 209     return new wxToolBarTool(this, control
); 
 212 // ---------------------------------------------------------------------------- 
 213 // wxToolBar construction 
 214 // ---------------------------------------------------------------------------- 
 216 void wxToolBar::Init() 
 222     m_defaultWidth 
= DEFAULTBITMAPX
; 
 223     m_defaultHeight 
= DEFAULTBITMAPY
; 
 228 bool wxToolBar::Create(wxWindow 
*parent
, 
 233                        const wxString
& name
) 
 235     // common initialisation 
 236     if ( !CreateControl(parent
, id
, pos
, size
, style
, wxDefaultValidator
, name
) ) 
 239     // MSW-specific initialisation 
 240     if ( !MSWCreateToolbar(pos
, size
) ) 
 243     wxSetCCUnicodeFormat(GetHwnd()); 
 245     // set up the colors and fonts 
 246     SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_MENUBAR
)); 
 247     SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT
)); 
 252 bool wxToolBar::MSWCreateToolbar(const wxPoint
& pos
, const wxSize
& size
) 
 254     if ( !MSWCreateControl(TOOLBARCLASSNAME
, wxEmptyString
, pos
, size
) ) 
 257     // toolbar-specific post initialisation 
 258     ::SendMessage(GetHwnd(), TB_BUTTONSTRUCTSIZE
, sizeof(TBBUTTON
), 0); 
 263 void wxToolBar::Recreate() 
 265     const HWND hwndOld 
= GetHwnd(); 
 268         // we haven't been created yet, no need to recreate 
 272     // get the position and size before unsubclassing the old toolbar 
 273     const wxPoint pos 
= GetPosition(); 
 274     const wxSize size 
= GetSize(); 
 278     if ( !MSWCreateToolbar(pos
, size
) ) 
 281         wxFAIL_MSG( _T("recreating the toolbar failed") ); 
 286     // reparent all our children under the new toolbar 
 287     for ( wxWindowList::compatibility_iterator node 
= m_children
.GetFirst(); 
 289           node 
= node
->GetNext() ) 
 291         wxWindow 
*win 
= node
->GetData(); 
 292         if ( !win
->IsTopLevel() ) 
 293             ::SetParent(GetHwndOf(win
), GetHwnd()); 
 296     // only destroy the old toolbar now -- after all the children had been 
 298     ::DestroyWindow(hwndOld
); 
 300     // it is for the old bitmap control and can't be used with the new one 
 303         ::DeleteObject((HBITMAP
) m_hBitmap
); 
 311 wxToolBar::~wxToolBar() 
 313     // we must refresh the frame size when the toolbar is deleted but the frame 
 314     // is not - otherwise toolbar leaves a hole in the place it used to occupy 
 315     wxFrame 
*frame 
= wxDynamicCast(GetParent(), wxFrame
); 
 316     if ( frame 
&& !frame
->IsBeingDeleted() ) 
 318         frame
->SendSizeEvent(); 
 323         ::DeleteObject((HBITMAP
) m_hBitmap
); 
 327 wxSize 
wxToolBar::DoGetBestSize() const 
 329     wxSize sizeBest 
= GetToolSize(); 
 330     sizeBest
.x 
*= GetToolsCount(); 
 332     // reverse horz and vertical components if necessary 
 333     return HasFlag(wxTB_VERTICAL
) ? wxSize(sizeBest
.y
, sizeBest
.x
) : sizeBest
; 
 336 WXDWORD 
wxToolBar::MSWGetStyle(long style
, WXDWORD 
*exstyle
) const 
 338     // toolbars never have border, giving one to them results in broken 
 340     WXDWORD msStyle 
= wxControl::MSWGetStyle
 
 342                         (style 
& ~wxBORDER_MASK
) | wxBORDER_NONE
, exstyle
 
 345     // always include this one, it never hurts and setting it later only if we 
 346     // do have tooltips wouldn't work 
 347     msStyle 
|= TBSTYLE_TOOLTIPS
; 
 349     if ( style 
& (wxTB_FLAT 
| wxTB_HORZ_LAYOUT
) ) 
 351         // static as it doesn't change during the program lifetime 
 352         static int s_verComCtl 
= wxTheApp
->GetComCtl32Version(); 
 354         // comctl32.dll 4.00 doesn't support the flat toolbars and using this 
 355         // style with 6.00 (part of Windows XP) leads to the toolbar with 
 356         // incorrect background colour - and not using it still results in the 
 357         // correct (flat) toolbar, so don't use it there 
 358         if ( s_verComCtl 
> 400 && s_verComCtl 
< 600 ) 
 360             msStyle 
|= TBSTYLE_FLAT 
| TBSTYLE_TRANSPARENT
; 
 363         if ( s_verComCtl 
>= 470 && style 
& wxTB_HORZ_LAYOUT 
) 
 365             msStyle 
|= TBSTYLE_LIST
; 
 369     if ( style 
& wxTB_NODIVIDER 
) 
 370         msStyle 
|= CCS_NODIVIDER
; 
 372     if ( style 
& wxTB_NOALIGN 
) 
 373         msStyle 
|= CCS_NOPARENTALIGN
; 
 375     if ( style 
& wxTB_VERTICAL 
) 
 381 // ---------------------------------------------------------------------------- 
 382 // adding/removing tools 
 383 // ---------------------------------------------------------------------------- 
 385 bool wxToolBar::DoInsertTool(size_t WXUNUSED(pos
), wxToolBarToolBase 
*tool
) 
 387     // nothing special to do here - we really create the toolbar buttons in 
 394 bool wxToolBar::DoDeleteTool(size_t pos
, wxToolBarToolBase 
*tool
) 
 396     // the main difficulty we have here is with the controls in the toolbars: 
 397     // as we (sometimes) use several separators to cover up the space used by 
 398     // them, the indices are not the same for us and the toolbar 
 400     // first determine the position of the first button to delete: it may be 
 401     // different from pos if we use several separators to cover the space used 
 403     wxToolBarToolsList::compatibility_iterator node
; 
 404     for ( node 
= m_tools
.GetFirst(); node
; node 
= node
->GetNext() ) 
 406         wxToolBarToolBase 
*tool2 
= node
->GetData(); 
 409             // let node point to the next node in the list 
 410             node 
= node
->GetNext(); 
 415         if ( tool2
->IsControl() ) 
 417             pos 
+= ((wxToolBarTool 
*)tool2
)->GetSeparatorsCount() - 1; 
 421     // now determine the number of buttons to delete and the area taken by them 
 422     size_t nButtonsToDelete 
= 1; 
 424     // get the size of the button we're going to delete 
 426     if ( !::SendMessage(GetHwnd(), TB_GETITEMRECT
, pos
, (LPARAM
)&r
) ) 
 428         wxLogLastError(_T("TB_GETITEMRECT")); 
 431     int width 
= r
.right 
- r
.left
; 
 433     if ( tool
->IsControl() ) 
 435         nButtonsToDelete 
= ((wxToolBarTool 
*)tool
)->GetSeparatorsCount(); 
 437         width 
*= nButtonsToDelete
; 
 440     // do delete all buttons 
 441     m_nButtons 
-= nButtonsToDelete
; 
 442     while ( nButtonsToDelete
-- > 0 ) 
 444         if ( !::SendMessage(GetHwnd(), TB_DELETEBUTTON
, pos
, 0) ) 
 446             wxLogLastError(wxT("TB_DELETEBUTTON")); 
 454     // and finally reposition all the controls after this button (the toolbar 
 455     // takes care of all normal items) 
 456     for ( /* node -> first after deleted */ ; node
; node 
= node
->GetNext() ) 
 458         wxToolBarToolBase 
*tool2 
= node
->GetData(); 
 459         if ( tool2
->IsControl() ) 
 462             wxControl 
*control 
= tool2
->GetControl(); 
 463             control
->GetPosition(&x
, NULL
); 
 464             control
->Move(x 
- width
, -1); 
 471 bool wxToolBar::Realize() 
 473     const size_t nTools 
= GetToolsCount(); 
 480     const bool isVertical 
= HasFlag(wxTB_VERTICAL
); 
 482     // delete all old buttons, if any 
 483     for ( size_t pos 
= 0; pos 
< m_nButtons
; pos
++ ) 
 485         if ( !::SendMessage(GetHwnd(), TB_DELETEBUTTON
, 0, 0) ) 
 487             wxLogDebug(wxT("TB_DELETEBUTTON failed")); 
 491     // First, add the bitmap: we use one bitmap for all toolbar buttons 
 492     // ---------------------------------------------------------------- 
 494     wxToolBarToolsList::compatibility_iterator node
; 
 498     if ( HasFlag(wxTB_NOICONS
) ) 
 500         // no icons, don't leave space for them 
 504     else // do show icons 
 506         // if we already have a bitmap, we'll replace the existing one -- 
 507         // otherwise we'll install a new one 
 508         HBITMAP oldToolBarBitmap 
= (HBITMAP
)m_hBitmap
; 
 510         sizeBmp
.x 
= m_defaultWidth
; 
 511         sizeBmp
.y 
= m_defaultHeight
; 
 513         const wxCoord totalBitmapWidth 
= m_defaultWidth 
* nTools
, 
 514                       totalBitmapHeight 
= m_defaultHeight
; 
 516         // Create a bitmap and copy all the tool bitmaps to it 
 518         wxMemoryDC dcAllButtons
; 
 519         wxBitmap 
bitmap(totalBitmapWidth
, totalBitmapHeight
); 
 520         dcAllButtons
.SelectObject(bitmap
); 
 521         dcAllButtons
.SetBackground(*wxLIGHT_GREY_BRUSH
); 
 522         dcAllButtons
.Clear(); 
 524         m_hBitmap 
= bitmap
.GetHBITMAP(); 
 525         HBITMAP hBitmap 
= (HBITMAP
)m_hBitmap
; 
 526 #else // !USE_BITMAP_MASKS 
 527         HBITMAP hBitmap 
= ::CreateCompatibleBitmap(ScreenHDC(), 
 532             wxLogLastError(_T("CreateCompatibleBitmap")); 
 537         m_hBitmap 
= (WXHBITMAP
)hBitmap
; 
 540         SelectInHDC 
hdcSelector(memoryDC
, hBitmap
); 
 543 #endif // USE_BITMAP_MASKS/!USE_BITMAP_MASKS 
 545         if (wxSystemOptions::HasOption(wxT("msw.remap")) && wxSystemOptions::GetOptionInt(wxT("msw.remap")) == 0) 
 548             dcAllButtons
.SelectObject(wxNullBitmap
); 
 551             // Even if we're not remapping the bitmap 
 552             // content, we still have to remap the background. 
 553             hBitmap 
= (HBITMAP
)MapBitmap((WXHBITMAP
) hBitmap
, 
 554                 totalBitmapWidth
, totalBitmapHeight
); 
 557             dcAllButtons
.SelectObject(bitmap
); 
 561         // the button position 
 564         // the number of buttons (not separators) 
 567         for ( node 
= m_tools
.GetFirst(); node
; node 
= node
->GetNext() ) 
 569             wxToolBarToolBase 
*tool 
= node
->GetData(); 
 570             if ( tool
->IsButton() ) 
 572                 const wxBitmap
& bmp 
= tool
->GetNormalBitmap(); 
 575                     int xOffset 
= wxMax(0, (m_defaultWidth 
- bmp
.GetWidth())/2); 
 576                     int yOffset 
= wxMax(0, (m_defaultHeight 
- bmp
.GetHeight())/2); 
 578                     // notice the last parameter: do use mask 
 579                     dcAllButtons
.DrawBitmap(bmp
, x
+xOffset
, yOffset
, TRUE
); 
 580 #else // !USE_BITMAP_MASKS 
 581                     SelectInHDC 
hdcSelector2(memoryDC2
, GetHbitmapOf(bmp
)); 
 582                     if ( !BitBlt(memoryDC
, 
 583                                  x
+xOffset
, yOffset
,  m_defaultWidth
, m_defaultHeight
, 
 587                         wxLogLastError(wxT("BitBlt")); 
 589 #endif // USE_BITMAP_MASKS/!USE_BITMAP_MASKS 
 593                     wxFAIL_MSG( _T("invalid tool button bitmap") ); 
 596                 // still inc width and number of buttons because otherwise the 
 597                 // subsequent buttons will all be shifted which is rather confusing 
 598                 // (and like this you'd see immediately which bitmap was bad) 
 605         dcAllButtons
.SelectObject(wxNullBitmap
); 
 607         // don't delete this HBITMAP! 
 608         bitmap
.SetHBITMAP(0); 
 609 #endif // USE_BITMAP_MASKS/!USE_BITMAP_MASKS 
 611         if (!wxSystemOptions::HasOption(wxT("msw.remap")) || wxSystemOptions::GetOptionInt(wxT("msw.remap")) == 1) 
 613             // Map to system colours 
 614             hBitmap 
= (HBITMAP
)MapBitmap((WXHBITMAP
) hBitmap
, 
 615                 totalBitmapWidth
, totalBitmapHeight
); 
 618         bool addBitmap 
= TRUE
; 
 620         if ( oldToolBarBitmap 
) 
 622 #ifdef TB_REPLACEBITMAP 
 623             if ( wxTheApp
->GetComCtl32Version() >= 400 ) 
 625                 TBREPLACEBITMAP replaceBitmap
; 
 626                 replaceBitmap
.hInstOld 
= NULL
; 
 627                 replaceBitmap
.hInstNew 
= NULL
; 
 628                 replaceBitmap
.nIDOld 
= (UINT
) oldToolBarBitmap
; 
 629                 replaceBitmap
.nIDNew 
= (UINT
) hBitmap
; 
 630                 replaceBitmap
.nButtons 
= nButtons
; 
 631                 if ( !::SendMessage(GetHwnd(), TB_REPLACEBITMAP
, 
 632                                     0, (LPARAM
) &replaceBitmap
) ) 
 634                     wxFAIL_MSG(wxT("Could not replace the old bitmap")); 
 637                 ::DeleteObject(oldToolBarBitmap
); 
 643 #endif // TB_REPLACEBITMAP 
 645                 // we can't replace the old bitmap, so we will add another one 
 646                 // (awfully inefficient, but what else to do?) and shift the bitmap 
 647                 // indices accordingly 
 650                 bitmapId 
= m_nButtons
; 
 654         if ( addBitmap 
) // no old bitmap or we can't replace it 
 656             TBADDBITMAP addBitmap
; 
 658             addBitmap
.nID 
= (UINT
) hBitmap
; 
 659             if ( ::SendMessage(GetHwnd(), TB_ADDBITMAP
, 
 660                                (WPARAM
) nButtons
, (LPARAM
)&addBitmap
) == -1 ) 
 662                 wxFAIL_MSG(wxT("Could not add bitmap to toolbar")); 
 667     // don't call SetToolBitmapSize() as we don't want to change the values of 
 668     // m_defaultWidth/Height 
 669     if ( !::SendMessage(GetHwnd(), TB_SETBITMAPSIZE
, 0, 
 670                         MAKELONG(sizeBmp
.x
, sizeBmp
.y
)) ) 
 672         wxLogLastError(_T("TB_SETBITMAPSIZE")); 
 675     // Next add the buttons and separators 
 676     // ----------------------------------- 
 678     TBBUTTON 
*buttons 
= new TBBUTTON
[nTools
]; 
 680     // this array will hold the indices of all controls in the toolbar 
 681     wxArrayInt controlIds
; 
 683     bool lastWasRadio 
= FALSE
; 
 685     for ( node 
= m_tools
.GetFirst(); node
; node 
= node
->GetNext() ) 
 687         wxToolBarToolBase 
*tool 
= node
->GetData(); 
 689         // don't add separators to the vertical toolbar with old comctl32.dll 
 690         // versions as they didn't handle this properly 
 691         if ( isVertical 
&& tool
->IsSeparator() && 
 692                 wxTheApp
->GetComCtl32Version() <= 472 ) 
 698         TBBUTTON
& button 
= buttons
[i
]; 
 700         wxZeroMemory(button
); 
 702         bool isRadio 
= FALSE
; 
 703         switch ( tool
->GetStyle() ) 
 705             case wxTOOL_STYLE_CONTROL
: 
 706                 button
.idCommand 
= tool
->GetId(); 
 707                 // fall through: create just a separator too 
 709             case wxTOOL_STYLE_SEPARATOR
: 
 710                 button
.fsState 
= TBSTATE_ENABLED
; 
 711                 button
.fsStyle 
= TBSTYLE_SEP
; 
 714             case wxTOOL_STYLE_BUTTON
: 
 715                 if ( !HasFlag(wxTB_NOICONS
) ) 
 716                     button
.iBitmap 
= bitmapId
; 
 718                 if ( HasFlag(wxTB_TEXT
) ) 
 720                     const wxString
& label 
= tool
->GetLabel(); 
 721                     if ( !label
.empty() ) 
 723                         button
.iString 
= (int)label
.c_str(); 
 727                 button
.idCommand 
= tool
->GetId(); 
 729                 if ( tool
->IsEnabled() ) 
 730                     button
.fsState 
|= TBSTATE_ENABLED
; 
 731                 if ( tool
->IsToggled() ) 
 732                     button
.fsState 
|= TBSTATE_CHECKED
; 
 734                 switch ( tool
->GetKind() ) 
 737                         button
.fsStyle 
= TBSTYLE_CHECKGROUP
; 
 741                             // the first item in the radio group is checked by 
 742                             // default to be consistent with wxGTK and the menu 
 744                             button
.fsState 
|= TBSTATE_CHECKED
; 
 753                         button
.fsStyle 
= TBSTYLE_CHECK
; 
 757                         wxFAIL_MSG( _T("unexpected toolbar button kind") ); 
 761                         button
.fsStyle 
= TBSTYLE_BUTTON
; 
 768         lastWasRadio 
= isRadio
; 
 773     if ( !::SendMessage(GetHwnd(), TB_ADDBUTTONS
, (WPARAM
)i
, (LPARAM
)buttons
) ) 
 775         wxLogLastError(wxT("TB_ADDBUTTONS")); 
 780     // Deal with the controls finally 
 781     // ------------------------------ 
 783     // adjust the controls size to fit nicely in the toolbar 
 786     for ( node 
= m_tools
.GetFirst(); node
; node 
= node
->GetNext(), index
++ ) 
 788         wxToolBarToolBase 
*tool 
= node
->GetData(); 
 790         // we calculate the running y coord for vertical toolbars so we need to 
 791         // get the items size for all items but for the horizontal ones we 
 792         // don't need to deal with the non controls 
 793         bool isControl 
= tool
->IsControl(); 
 794         if ( !isControl 
&& !isVertical 
) 
 797         // note that we use TB_GETITEMRECT and not TB_GETRECT because the 
 798         // latter only appeared in v4.70 of comctl32.dll 
 800         if ( !SendMessage(GetHwnd(), TB_GETITEMRECT
, 
 801                           index
, (LPARAM
)(LPRECT
)&r
) ) 
 803             wxLogLastError(wxT("TB_GETITEMRECT")); 
 808             // can only be control if isVertical 
 809             y 
+= r
.bottom 
- r
.top
; 
 814         wxControl 
*control 
= tool
->GetControl(); 
 816         wxSize size 
= control
->GetSize(); 
 818         // the position of the leftmost controls corner 
 821         // TB_SETBUTTONINFO message is only supported by comctl32.dll 4.71+ 
 822 #ifdef TB_SETBUTTONINFO 
 823         // available in headers, now check whether it is available now 
 825         if ( wxTheApp
->GetComCtl32Version() >= 471 ) 
 827             // set the (underlying) separators width to be that of the 
 830             tbbi
.cbSize 
= sizeof(tbbi
); 
 831             tbbi
.dwMask 
= TBIF_SIZE
; 
 833             if ( !SendMessage(GetHwnd(), TB_SETBUTTONINFO
, 
 834                               tool
->GetId(), (LPARAM
)&tbbi
) ) 
 836                 // the id is probably invalid? 
 837                 wxLogLastError(wxT("TB_SETBUTTONINFO")); 
 841 #endif // comctl32.dll 4.71 
 842         // TB_SETBUTTONINFO unavailable 
 844             // try adding several separators to fit the controls width 
 845             int widthSep 
= r
.right 
- r
.left
; 
 851             tbb
.fsState 
= TBSTATE_ENABLED
; 
 852             tbb
.fsStyle 
= TBSTYLE_SEP
; 
 854             size_t nSeparators 
= size
.x 
/ widthSep
; 
 855             for ( size_t nSep 
= 0; nSep 
< nSeparators
; nSep
++ ) 
 857                 if ( !SendMessage(GetHwnd(), TB_INSERTBUTTON
, 
 858                                   index
, (LPARAM
)&tbb
) ) 
 860                     wxLogLastError(wxT("TB_INSERTBUTTON")); 
 866             // remember the number of separators we used - we'd have to 
 867             // delete all of them later 
 868             ((wxToolBarTool 
*)tool
)->SetSeparatorsCount(nSeparators
); 
 870             // adjust the controls width to exactly cover the separators 
 871             control
->SetSize((nSeparators 
+ 1)*widthSep
, -1); 
 874         // position the control itself correctly vertically 
 875         int height 
= r
.bottom 
- r
.top
; 
 876         int diff 
= height 
- size
.y
; 
 879             // the control is too high, resize to fit 
 880             control
->SetSize(-1, height 
- 2); 
 891             y 
+= height 
+ 2*GetMargins().y
; 
 893         else // horizontal toolbar 
 901         control
->Move(left
, top 
+ (diff 
+ 1) / 2); 
 904     // the max index is the "real" number of buttons - i.e. counting even the 
 905     // separators which we added just for aligning the controls 
 910         if ( m_maxRows 
== 0 ) 
 912             // if not set yet, only one row 
 916     else if ( m_nButtons 
> 0 ) // vertical non empty toolbar 
 918         if ( m_maxRows 
== 0 ) 
 920             // if not set yet, have one column 
 928 // ---------------------------------------------------------------------------- 
 930 // ---------------------------------------------------------------------------- 
 932 bool wxToolBar::MSWCommand(WXUINT 
WXUNUSED(cmd
), WXWORD id
) 
 934     wxToolBarToolBase 
*tool 
= FindById((int)id
); 
 938     bool toggled 
= false; // just to suppress warnings 
 940     if ( tool
->CanBeToggled() ) 
 942         LRESULT state 
= ::SendMessage(GetHwnd(), TB_GETSTATE
, id
, 0); 
 943         toggled 
= (state 
& TBSTATE_CHECKED
) != 0; 
 945         // ignore the event when a radio button is released, as this doesn't seem to 
 946         // happen at all, and is handled otherwise 
 947         if ( tool
->GetKind() == wxITEM_RADIO 
&& !toggled 
) 
 950         tool
->Toggle(toggled
); 
 951         UnToggleRadioGroup(tool
); 
 954     // OnLeftClick() can veto the button state change - for buttons which 
 955     // may be toggled only, of couse 
 956     if ( !OnLeftClick((int)id
, toggled
) && tool
->CanBeToggled() ) 
 959         tool
->Toggle(!toggled
); 
 961         ::SendMessage(GetHwnd(), TB_CHECKBUTTON
, id
, MAKELONG(toggled
, 0)); 
 967 bool wxToolBar::MSWOnNotify(int WXUNUSED(idCtrl
), 
 969                             WXLPARAM 
*WXUNUSED(result
)) 
 972     // First check if this applies to us 
 973     NMHDR 
*hdr 
= (NMHDR 
*)lParam
; 
 975     // the tooltips control created by the toolbar is sometimes Unicode, even 
 976     // in an ANSI application - this seems to be a bug in comctl32.dll v5 
 977     UINT code 
= hdr
->code
; 
 978     if ( (code 
!= (UINT
) TTN_NEEDTEXTA
) && (code 
!= (UINT
) TTN_NEEDTEXTW
) ) 
 981     HWND toolTipWnd 
= (HWND
)::SendMessage((HWND
)GetHWND(), TB_GETTOOLTIPS
, 0, 0); 
 982     if ( toolTipWnd 
!= hdr
->hwndFrom 
) 
 985     LPTOOLTIPTEXT ttText 
= (LPTOOLTIPTEXT
)lParam
; 
 986     int id 
= (int)ttText
->hdr
.idFrom
; 
 988     wxToolBarToolBase 
*tool 
= FindById(id
); 
 992     return HandleTooltipNotify(code
, lParam
, tool
->GetShortHelp()); 
 998 // ---------------------------------------------------------------------------- 
1000 // ---------------------------------------------------------------------------- 
1002 void wxToolBar::SetToolBitmapSize(const wxSize
& size
) 
1004     wxToolBarBase::SetToolBitmapSize(size
); 
1006     ::SendMessage(GetHwnd(), TB_SETBITMAPSIZE
, 0, MAKELONG(size
.x
, size
.y
)); 
1009 void wxToolBar::SetRows(int nRows
) 
1011     if ( nRows 
== m_maxRows 
) 
1013         // avoid resizing the frame uselessly 
1017     // TRUE in wParam means to create at least as many rows, FALSE - 
1020     ::SendMessage(GetHwnd(), TB_SETROWS
, 
1021                   MAKEWPARAM(nRows
, !(GetWindowStyle() & wxTB_VERTICAL
)), 
1029 // The button size is bigger than the bitmap size 
1030 wxSize 
wxToolBar::GetToolSize() const 
1032     // TB_GETBUTTONSIZE is supported from version 4.70 
1033 #if defined(_WIN32_IE) && (_WIN32_IE >= 0x300 ) \ 
1034     && !( defined(__GNUWIN32__) && !wxCHECK_W32API_VERSION( 1, 0 ) ) \ 
1035     && !defined (__DIGITALMARS__) 
1036     if ( wxTheApp
->GetComCtl32Version() >= 470 ) 
1038         DWORD dw 
= ::SendMessage(GetHwnd(), TB_GETBUTTONSIZE
, 0, 0); 
1040         return wxSize(LOWORD(dw
), HIWORD(dw
)); 
1043 #endif // comctl32.dll 4.70+ 
1046         return wxSize(m_defaultWidth 
+ 8, m_defaultHeight 
+ 7); 
1051 wxToolBarToolBase 
*GetItemSkippingDummySpacers(const wxToolBarToolsList
& tools
, 
1054     wxToolBarToolsList::compatibility_iterator current 
= tools
.GetFirst(); 
1056     for ( ; current 
!= 0; current 
= current
->GetNext() ) 
1059             return current
->GetData(); 
1061         wxToolBarTool 
*tool 
= (wxToolBarTool 
*)current
->GetData(); 
1062         size_t separators 
= tool
->GetSeparatorsCount(); 
1064         // if it is a normal button, sepcount == 0, so skip 1 item (the button) 
1065         // otherwise, skip as many items as the separator count, plus the 
1067         index 
-= separators 
? separators 
+ 1 : 1; 
1073 wxToolBarToolBase 
*wxToolBar::FindToolForPosition(wxCoord x
, wxCoord y
) const 
1078     int index 
= (int)::SendMessage(GetHwnd(), TB_HITTEST
, 0, (LPARAM
)&pt
); 
1079     // MBN: when the point ( x, y ) is close to the toolbar border 
1080     //      TB_HITTEST returns m_nButtons ( not -1 ) 
1081     if ( index 
< 0 || (size_t)index 
>= m_nButtons 
) 
1083         // it's a separator or there is no tool at all there 
1084         return (wxToolBarToolBase 
*)NULL
; 
1087     // if comctl32 version < 4.71 wxToolBar95 adds dummy spacers 
1088 #if defined(_WIN32_IE) && (_WIN32_IE >= 0x400 ) 
1089     if ( wxTheApp
->GetComCtl32Version() >= 471 ) 
1091         return m_tools
.Item((size_t)index
)->GetData(); 
1096         return GetItemSkippingDummySpacers( m_tools
, (size_t) index 
); 
1100 void wxToolBar::UpdateSize() 
1102     // the toolbar size changed 
1103     SendMessage(GetHwnd(), TB_AUTOSIZE
, 0, 0); 
1105     // we must also refresh the frame after the toolbar size (possibly) changed 
1106     wxFrame 
*frame 
= wxDynamicCast(GetParent(), wxFrame
); 
1109         frame
->SendSizeEvent(); 
1113 // ---------------------------------------------------------------------------- 
1115 // --------------------------------------------------------------------------- 
1117 void wxToolBar::SetWindowStyleFlag(long style
) 
1119     // the style bits whose changes force us to recreate the toolbar 
1120     static const long MASK_NEEDS_RECREATE 
= wxTB_TEXT 
| wxTB_NOICONS
; 
1122     const long styleOld 
= GetWindowStyle(); 
1124     wxToolBarBase::SetWindowStyleFlag(style
); 
1126     // don't recreate an empty toolbar: not only this is unnecessary, but it is 
1127     // also fatal as we'd then try to recreate the toolbar when it's just being 
1129     if ( GetToolsCount() && 
1130             (style 
& MASK_NEEDS_RECREATE
) != (styleOld 
& MASK_NEEDS_RECREATE
) ) 
1132         // to remove the text labels, simply re-realizing the toolbar is enough 
1133         // but I don't know of any way to add the text to an existing toolbar 
1134         // other than by recreating it entirely 
1139 // ---------------------------------------------------------------------------- 
1141 // ---------------------------------------------------------------------------- 
1143 void wxToolBar::DoEnableTool(wxToolBarToolBase 
*tool
, bool enable
) 
1145     ::SendMessage(GetHwnd(), TB_ENABLEBUTTON
, 
1146                   (WPARAM
)tool
->GetId(), (LPARAM
)MAKELONG(enable
, 0)); 
1149 void wxToolBar::DoToggleTool(wxToolBarToolBase 
*tool
, bool toggle
) 
1151     ::SendMessage(GetHwnd(), TB_CHECKBUTTON
, 
1152                   (WPARAM
)tool
->GetId(), (LPARAM
)MAKELONG(toggle
, 0)); 
1155 void wxToolBar::DoSetToggle(wxToolBarToolBase 
*WXUNUSED(tool
), bool WXUNUSED(toggle
)) 
1157     // VZ: AFAIK, the button has to be created either with TBSTYLE_CHECK or 
1158     //     without, so we really need to delete the button and recreate it here 
1159     wxFAIL_MSG( _T("not implemented") ); 
1162 // ---------------------------------------------------------------------------- 
1164 // ---------------------------------------------------------------------------- 
1166 // Responds to colour changes, and passes event on to children. 
1167 void wxToolBar::OnSysColourChanged(wxSysColourChangedEvent
& event
) 
1169     wxRGBToColour(m_backgroundColour
, ::GetSysColor(COLOR_BTNFACE
)); 
1171     // Remap the buttons 
1174     // Relayout the toolbar 
1175     int nrows 
= m_maxRows
; 
1176     m_maxRows 
= 0;      // otherwise SetRows() wouldn't do anything 
1181     // let the event propagate further 
1185 void wxToolBar::OnMouseEvent(wxMouseEvent
& event
) 
1187     if (event
.Leaving() && m_pInTool
) 
1194     if (event
.RightDown()) 
1196         // For now, we don't have an id. Later we could 
1197         // try finding the tool. 
1198         OnRightClick((int)-1, event
.GetX(), event
.GetY()); 
1206 bool wxToolBar::HandleSize(WXWPARAM 
WXUNUSED(wParam
), WXLPARAM lParam
) 
1208     // calculate our minor dimension ourselves - we're confusing the standard 
1209     // logic (TB_AUTOSIZE) with our horizontal toolbars and other hacks 
1211     if ( ::SendMessage(GetHwnd(), TB_GETITEMRECT
, 0, (LPARAM
)&r
) ) 
1215         if ( GetWindowStyle() & wxTB_VERTICAL 
) 
1217             w 
= r
.right 
- r
.left
; 
1220                 w 
*= (m_nButtons 
+ m_maxRows 
- 1)/m_maxRows
; 
1227             if (HasFlag( wxTB_FLAT 
)) 
1228                 h 
= r
.bottom 
- r
.top 
- 3; 
1230                 h 
= r
.bottom 
- r
.top
; 
1233                 // FIXME: hardcoded separator line height... 
1234                 h 
+= HasFlag(wxTB_NODIVIDER
) ? 4 : 6; 
1239         if ( MAKELPARAM(w
, h
) != lParam 
) 
1241             // size really changed 
1245         // message processed 
1252 bool wxToolBar::HandlePaint(WXWPARAM wParam
, WXLPARAM lParam
) 
1254     // erase any dummy separators which we used for aligning the controls if 
1257     // first of all, do we have any controls at all? 
1258     wxToolBarToolsList::compatibility_iterator node
; 
1259     for ( node 
= m_tools
.GetFirst(); node
; node 
= node
->GetNext() ) 
1261         if ( node
->GetData()->IsControl() ) 
1267         // no controls, nothing to erase 
1271     // prepare the DC on which we'll be drawing 
1272     wxClientDC 
dc(this); 
1273     dc
.SetBrush(wxBrush(GetBackgroundColour(), wxSOLID
)); 
1274     dc
.SetPen(*wxTRANSPARENT_PEN
); 
1277     if ( !GetUpdateRect(GetHwnd(), &r
, FALSE
) ) 
1279         // nothing to redraw anyhow 
1284     wxCopyRECTToRect(r
, rectUpdate
); 
1286     dc
.SetClippingRegion(rectUpdate
); 
1288     // draw the toolbar tools, separators &c normally 
1289     wxControl::MSWWindowProc(WM_PAINT
, wParam
, lParam
); 
1291     // for each control in the toolbar find all the separators intersecting it 
1294     // NB: this is really the only way to do it as we don't know if a separator 
1295     //     corresponds to a control (i.e. is a dummy one) or a real one 
1297     for ( node 
= m_tools
.GetFirst(); node
; node 
= node
->GetNext() ) 
1299         wxToolBarToolBase 
*tool 
= node
->GetData(); 
1300         if ( tool
->IsControl() ) 
1302             // get the control rect in our client coords 
1303             wxControl 
*control 
= tool
->GetControl(); 
1304             wxRect rectCtrl 
= control
->GetRect(); 
1306             // iterate over all buttons 
1308             int count 
= ::SendMessage(GetHwnd(), TB_BUTTONCOUNT
, 0, 0); 
1309             for ( int n 
= 0; n 
< count
; n
++ ) 
1311                 // is it a separator? 
1312                 if ( !::SendMessage(GetHwnd(), TB_GETBUTTON
, 
1315                     wxLogDebug(_T("TB_GETBUTTON failed?")); 
1320                 if ( tbb
.fsStyle 
!= TBSTYLE_SEP 
) 
1323                 // get the bounding rect of the separator 
1325                 if ( !::SendMessage(GetHwnd(), TB_GETITEMRECT
, 
1328                     wxLogDebug(_T("TB_GETITEMRECT failed?")); 
1333                 // does it intersect the control? 
1335                 wxCopyRECTToRect(r
, rectItem
); 
1336                 if ( rectCtrl
.Intersects(rectItem
) ) 
1338                     // yes, do erase it! 
1339                     dc
.DrawRectangle(rectItem
); 
1341                     // Necessary in case we use a no-paint-on-size 
1342                     // style in the parent: the controls can disappear 
1343                     control
->Refresh(FALSE
); 
1352 void wxToolBar::HandleMouseMove(WXWPARAM 
WXUNUSED(wParam
), WXLPARAM lParam
) 
1354     wxCoord x 
= GET_X_LPARAM(lParam
), 
1355             y 
= GET_Y_LPARAM(lParam
); 
1356     wxToolBarToolBase
* tool 
= FindToolForPosition( x
, y 
); 
1358     // cursor left current tool 
1359     if( tool 
!= m_pInTool 
&& !tool 
) 
1365     // cursor entered a tool 
1366     if( tool 
!= m_pInTool 
&& tool 
) 
1369         OnMouseEnter( tool
->GetId() ); 
1373 WXLRESULT 
wxToolBar::MSWWindowProc(WXUINT nMsg
, WXWPARAM wParam
, WXLPARAM lParam
) 
1378             if ( HandleSize(wParam
, lParam
) ) 
1383             // we don't handle mouse moves, so always pass the message to 
1384             // wxControl::MSWWindowProc 
1385             HandleMouseMove(wParam
, lParam
); 
1389             if ( HandlePaint(wParam
, lParam
) ) 
1393     return wxControl::MSWWindowProc(nMsg
, wParam
, lParam
); 
1396 // ---------------------------------------------------------------------------- 
1397 // private functions 
1398 // ---------------------------------------------------------------------------- 
1400 WXHBITMAP 
wxToolBar::MapBitmap(WXHBITMAP bitmap
, int width
, int height
) 
1406         wxLogLastError(_T("CreateCompatibleDC")); 
1411     SelectInHDC 
bmpInHDC(hdcMem
, (HBITMAP
)bitmap
); 
1415         wxLogLastError(_T("SelectObject")); 
1420     wxCOLORMAP 
*cmap 
= wxGetStdColourMap(); 
1422     for ( int i 
= 0; i 
< width
; i
++ ) 
1424         for ( int j 
= 0; j 
< height
; j
++ ) 
1426             COLORREF pixel 
= ::GetPixel(hdcMem
, i
, j
); 
1428             for ( size_t k 
= 0; k 
< wxSTD_COL_MAX
; k
++ ) 
1430                 COLORREF col 
= cmap
[k
].from
; 
1431                 if ( abs(GetRValue(pixel
) - GetRValue(col
)) < 10 && 
1432                      abs(GetGValue(pixel
) - GetGValue(col
)) < 10 && 
1433                      abs(GetBValue(pixel
) - GetBValue(col
)) < 10 ) 
1435                     ::SetPixel(hdcMem
, i
, j
, cmap
[k
].to
); 
1444     // VZ: I leave here my attempts to map the bitmap to the system colours 
1445     //     faster by using BitBlt() even though it's broken currently - but 
1446     //     maybe someone else can finish it? It should be faster than iterating 
1447     //     over all pixels... 
1449     MemoryHDC hdcMask
, hdcDst
; 
1450     if ( !hdcMask 
|| !hdcDst 
) 
1452         wxLogLastError(_T("CreateCompatibleDC")); 
1457     // create the target bitmap 
1458     HBITMAP hbmpDst 
= ::CreateCompatibleBitmap(hdcDst
, width
, height
); 
1461         wxLogLastError(_T("CreateCompatibleBitmap")); 
1466     // create the monochrome mask bitmap 
1467     HBITMAP hbmpMask 
= ::CreateBitmap(width
, height
, 1, 1, 0); 
1470         wxLogLastError(_T("CreateBitmap(mono)")); 
1472         ::DeleteObject(hbmpDst
); 
1477     SelectInHDC 
bmpInDst(hdcDst
, hbmpDst
), 
1478                 bmpInMask(hdcMask
, hbmpMask
); 
1481     for ( n 
= 0; n 
< NUM_OF_MAPPED_COLOURS
; n
++ ) 
1483         // create the mask for this colour 
1484         ::SetBkColor(hdcMem
, ColorMap
[n
].from
); 
1485         ::BitBlt(hdcMask
, 0, 0, width
, height
, hdcMem
, 0, 0, SRCCOPY
); 
1487         // replace this colour with the target one in the dst bitmap 
1488         HBRUSH hbr 
= ::CreateSolidBrush(ColorMap
[n
].to
); 
1489         HGDIOBJ hbrOld 
= ::SelectObject(hdcDst
, hbr
); 
1491         ::MaskBlt(hdcDst
, 0, 0, width
, height
, 
1494                   MAKEROP4(PATCOPY
, SRCCOPY
)); 
1496         (void)::SelectObject(hdcDst
, hbrOld
); 
1497         ::DeleteObject(hbr
); 
1500     ::DeleteObject((HBITMAP
)bitmap
); 
1502     return (WXHBITMAP
)hbmpDst
; 
1506 #endif // wxUSE_TOOLBAR && Win95