]> git.saurik.com Git - wxWidgets.git/blob - src/msw/toolbar.cpp
Don't draw over dummy separators in MSW toolbar, just don't draw them at all.
[wxWidgets.git] / src / msw / toolbar.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/toolbar.cpp
3 // Purpose: wxToolBar
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 04/01/98
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
22
23 #ifdef __BORLANDC__
24 #pragma hdrstop
25 #endif
26
27 #if wxUSE_TOOLBAR && wxUSE_TOOLBAR_NATIVE && !defined(__SMARTPHONE__)
28
29 #include "wx/toolbar.h"
30
31 #ifndef WX_PRECOMP
32 #include "wx/msw/wrapcctl.h" // include <commctrl.h> "properly"
33 #include "wx/dynarray.h"
34 #include "wx/frame.h"
35 #include "wx/log.h"
36 #include "wx/intl.h"
37 #include "wx/settings.h"
38 #include "wx/bitmap.h"
39 #include "wx/dcmemory.h"
40 #include "wx/control.h"
41 #include "wx/app.h" // for GetComCtl32Version
42 #include "wx/image.h"
43 #include "wx/stattext.h"
44 #endif
45
46 #include "wx/artprov.h"
47 #include "wx/sysopt.h"
48 #include "wx/dcclient.h"
49 #include "wx/scopedarray.h"
50
51 #include "wx/msw/private.h"
52 #include "wx/msw/dc.h"
53
54 #if wxUSE_UXTHEME
55 #include "wx/msw/uxtheme.h"
56 #endif
57
58 // this define controls whether the code for button colours remapping (only
59 // useful for 16 or 256 colour images) is active at all, it's always turned off
60 // for CE where it doesn't compile (and is probably not needed anyhow) and may
61 // also be turned off for other systems if you always use 24bpp images and so
62 // never need it
63 #ifndef __WXWINCE__
64 #define wxREMAP_BUTTON_COLOURS
65 #endif // !__WXWINCE__
66
67 // ----------------------------------------------------------------------------
68 // constants
69 // ----------------------------------------------------------------------------
70
71 // these standard constants are not always defined in compilers headers
72
73 // Styles
74 #ifndef TBSTYLE_FLAT
75 #define TBSTYLE_LIST 0x1000
76 #define TBSTYLE_FLAT 0x0800
77 #endif
78
79 #ifndef TBSTYLE_TRANSPARENT
80 #define TBSTYLE_TRANSPARENT 0x8000
81 #endif
82
83 #ifndef TBSTYLE_TOOLTIPS
84 #define TBSTYLE_TOOLTIPS 0x0100
85 #endif
86
87 // Messages
88 #ifndef TB_GETSTYLE
89 #define TB_SETSTYLE (WM_USER + 56)
90 #define TB_GETSTYLE (WM_USER + 57)
91 #endif
92
93 #ifndef TB_HITTEST
94 #define TB_HITTEST (WM_USER + 69)
95 #endif
96
97 #ifndef TB_GETMAXSIZE
98 #define TB_GETMAXSIZE (WM_USER + 83)
99 #endif
100
101 // ----------------------------------------------------------------------------
102 // wxWin macros
103 // ----------------------------------------------------------------------------
104
105 IMPLEMENT_DYNAMIC_CLASS(wxToolBar, wxControl)
106
107 /*
108 TOOLBAR PROPERTIES
109 tool
110 bitmap
111 bitmap2
112 tooltip
113 longhelp
114 radio (bool)
115 toggle (bool)
116 separator
117 style ( wxNO_BORDER | wxTB_HORIZONTAL)
118 bitmapsize
119 margins
120 packing
121 separation
122
123 dontattachtoframe
124 */
125
126 BEGIN_EVENT_TABLE(wxToolBar, wxToolBarBase)
127 EVT_MOUSE_EVENTS(wxToolBar::OnMouseEvent)
128 EVT_SYS_COLOUR_CHANGED(wxToolBar::OnSysColourChanged)
129 END_EVENT_TABLE()
130
131 // ----------------------------------------------------------------------------
132 // private classes
133 // ----------------------------------------------------------------------------
134
135 class wxToolBarTool : public wxToolBarToolBase
136 {
137 public:
138 wxToolBarTool(wxToolBar *tbar,
139 int id,
140 const wxString& label,
141 const wxBitmap& bmpNormal,
142 const wxBitmap& bmpDisabled,
143 wxItemKind kind,
144 wxObject *clientData,
145 const wxString& shortHelp,
146 const wxString& longHelp)
147 : wxToolBarToolBase(tbar, id, label, bmpNormal, bmpDisabled, kind,
148 clientData, shortHelp, longHelp)
149 {
150 m_nSepCount = 0;
151 m_staticText = NULL;
152 }
153
154 wxToolBarTool(wxToolBar *tbar, wxControl *control, const wxString& label)
155 : wxToolBarToolBase(tbar, control, label)
156 {
157 if ( IsControl() && !m_label.empty() )
158 {
159 // create a control to render the control's label
160 m_staticText = new wxStaticText
161 (
162 m_tbar,
163 wxID_ANY,
164 m_label,
165 wxDefaultPosition,
166 wxDefaultSize,
167 wxALIGN_CENTRE | wxST_NO_AUTORESIZE
168 );
169 }
170 else // no label
171 {
172 m_staticText = NULL;
173 }
174
175 m_nSepCount = 1;
176 }
177
178 virtual ~wxToolBarTool()
179 {
180 delete m_staticText;
181 }
182
183 virtual void SetLabel(const wxString& label)
184 {
185 if ( label == m_label )
186 return;
187
188 wxToolBarToolBase::SetLabel(label);
189
190 if ( m_staticText )
191 m_staticText->SetLabel(label);
192
193 // we need to update the label shown in the toolbar because it has a
194 // pointer to the internal buffer of the old label
195 //
196 // TODO: use TB_SETBUTTONINFO
197 }
198
199 wxStaticText* GetStaticText()
200 {
201 wxASSERT_MSG( IsControl(),
202 wxT("only makes sense for embedded control tools") );
203
204 return m_staticText;
205 }
206
207 // set/get the number of separators which we use to cover the space used by
208 // a control in the toolbar
209 void SetSeparatorsCount(size_t count) { m_nSepCount = count; }
210 size_t GetSeparatorsCount() const { return m_nSepCount; }
211
212 // we need ids for the spacers which we want to modify later on, this
213 // function will allocate a valid/unique id for a spacer if not done yet
214 void AllocSpacerId()
215 {
216 if ( m_id == wxID_SEPARATOR )
217 m_id = wxWindow::NewControlId();
218 }
219
220 // this method is used for controls only and offsets the control by the
221 // given amount (in pixels) in horizontal direction
222 void MoveBy(int offset)
223 {
224 wxControl * const control = GetControl();
225
226 control->Move(control->GetPosition().x + offset, wxDefaultCoord);
227
228 if ( m_staticText )
229 {
230 m_staticText->Move(m_staticText->GetPosition().x + offset,
231 wxDefaultCoord);
232 }
233 }
234
235 private:
236 size_t m_nSepCount;
237 wxStaticText *m_staticText;
238
239 wxDECLARE_NO_COPY_CLASS(wxToolBarTool);
240 };
241
242 // ----------------------------------------------------------------------------
243 // helper functions
244 // ----------------------------------------------------------------------------
245
246 // return the rectangle of the item at the given index
247 //
248 // returns an empty (0, 0, 0, 0) rectangle if fails so the caller may compare
249 // r.right or r.bottom with 0 to check for this
250 static RECT wxGetTBItemRect(HWND hwnd, int index)
251 {
252 RECT r;
253
254 // note that we use TB_GETITEMRECT and not TB_GETRECT because the latter
255 // only appeared in v4.70 of comctl32.dll
256 if ( !::SendMessage(hwnd, TB_GETITEMRECT, index, (LPARAM)&r) )
257 {
258 wxLogLastError(wxT("TB_GETITEMRECT"));
259
260 r.top =
261 r.left =
262 r.right =
263 r.bottom = 0;
264 }
265
266 return r;
267 }
268
269 // ============================================================================
270 // implementation
271 // ============================================================================
272
273 // ----------------------------------------------------------------------------
274 // wxToolBarTool
275 // ----------------------------------------------------------------------------
276
277 wxToolBarToolBase *wxToolBar::CreateTool(int id,
278 const wxString& label,
279 const wxBitmap& bmpNormal,
280 const wxBitmap& bmpDisabled,
281 wxItemKind kind,
282 wxObject *clientData,
283 const wxString& shortHelp,
284 const wxString& longHelp)
285 {
286 return new wxToolBarTool(this, id, label, bmpNormal, bmpDisabled, kind,
287 clientData, shortHelp, longHelp);
288 }
289
290 wxToolBarToolBase *
291 wxToolBar::CreateTool(wxControl *control, const wxString& label)
292 {
293 return new wxToolBarTool(this, control, label);
294 }
295
296 // ----------------------------------------------------------------------------
297 // wxToolBar construction
298 // ----------------------------------------------------------------------------
299
300 void wxToolBar::Init()
301 {
302 m_hBitmap = 0;
303 m_disabledImgList = NULL;
304
305 m_nButtons = 0;
306 m_totalFixedSize = 0;
307
308 // even though modern Windows applications typically use 24*24 (or even
309 // 32*32) size for their bitmaps, the native control itself still uses the
310 // old 16*15 default size (see TB_SETBITMAPSIZE documentation in MSDN), so
311 // default to it so that we don't call SetToolBitmapSize() unnecessarily in
312 // wxToolBarBase::AdjustToolBitmapSize()
313 m_defaultWidth = 16;
314 m_defaultHeight = 15;
315
316 m_pInTool = NULL;
317 }
318
319 bool wxToolBar::Create(wxWindow *parent,
320 wxWindowID id,
321 const wxPoint& pos,
322 const wxSize& size,
323 long style,
324 const wxString& name)
325 {
326 // common initialisation
327 if ( !CreateControl(parent, id, pos, size, style, wxDefaultValidator, name) )
328 return false;
329
330 FixupStyle();
331
332 // MSW-specific initialisation
333 if ( !MSWCreateToolbar(pos, size) )
334 return false;
335
336 wxSetCCUnicodeFormat(GetHwnd());
337
338 // workaround for flat toolbar on Windows XP classic style: we have to set
339 // the style after creating the control; doing it at creation time doesn't work
340 #if wxUSE_UXTHEME
341 if ( style & wxTB_FLAT )
342 {
343 LRESULT style = GetMSWToolbarStyle();
344
345 if ( !(style & TBSTYLE_FLAT) )
346 ::SendMessage(GetHwnd(), TB_SETSTYLE, 0, style | TBSTYLE_FLAT);
347 }
348 #endif // wxUSE_UXTHEME
349
350 return true;
351 }
352
353 bool wxToolBar::MSWCreateToolbar(const wxPoint& pos, const wxSize& size)
354 {
355 if ( !MSWCreateControl(TOOLBARCLASSNAME, wxEmptyString, pos, size) )
356 return false;
357
358 // toolbar-specific post initialisation
359 ::SendMessage(GetHwnd(), TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0);
360
361 #ifdef TB_SETEXTENDEDSTYLE
362 if ( wxApp::GetComCtl32Version() >= 471 )
363 ::SendMessage(GetHwnd(), TB_SETEXTENDEDSTYLE, 0, TBSTYLE_EX_DRAWDDARROWS);
364 #endif
365
366 return true;
367 }
368
369 void wxToolBar::Recreate()
370 {
371 const HWND hwndOld = GetHwnd();
372 if ( !hwndOld )
373 {
374 // we haven't been created yet, no need to recreate
375 return;
376 }
377
378 // get the position and size before unsubclassing the old toolbar
379 const wxPoint pos = GetPosition();
380 const wxSize size = GetSize();
381
382 UnsubclassWin();
383
384 if ( !MSWCreateToolbar(pos, size) )
385 {
386 // what can we do?
387 wxFAIL_MSG( wxT("recreating the toolbar failed") );
388
389 return;
390 }
391
392 // reparent all our children under the new toolbar
393 for ( wxWindowList::compatibility_iterator node = m_children.GetFirst();
394 node;
395 node = node->GetNext() )
396 {
397 wxWindow *win = node->GetData();
398 if ( !win->IsTopLevel() )
399 ::SetParent(GetHwndOf(win), GetHwnd());
400 }
401
402 // only destroy the old toolbar now --
403 // after all the children had been reparented
404 ::DestroyWindow(hwndOld);
405
406 // it is for the old bitmap control and can't be used with the new one
407 if ( m_hBitmap )
408 {
409 ::DeleteObject((HBITMAP) m_hBitmap);
410 m_hBitmap = 0;
411 }
412
413 if ( m_disabledImgList )
414 {
415 delete m_disabledImgList;
416 m_disabledImgList = NULL;
417 }
418
419 Realize();
420 }
421
422 wxToolBar::~wxToolBar()
423 {
424 // we must refresh the frame size when the toolbar is deleted but the frame
425 // is not - otherwise toolbar leaves a hole in the place it used to occupy
426 SendSizeEventToParent();
427
428 if ( m_hBitmap )
429 ::DeleteObject((HBITMAP) m_hBitmap);
430
431 delete m_disabledImgList;
432 }
433
434 wxSize wxToolBar::DoGetBestSize() const
435 {
436 wxSize sizeBest;
437
438 SIZE size;
439 if ( !::SendMessage(GetHwnd(), TB_GETMAXSIZE, 0, (LPARAM)&size) )
440 {
441 // maybe an old (< 0x400) Windows version? try to approximate the
442 // toolbar size ourselves
443 sizeBest = GetToolSize();
444 sizeBest.y += 2 * ::GetSystemMetrics(SM_CYBORDER); // Add borders
445 sizeBest.x *= GetToolsCount();
446
447 // reverse horz and vertical components if necessary
448 if ( IsVertical() )
449 {
450 int t = sizeBest.x;
451 sizeBest.x = sizeBest.y;
452 sizeBest.y = t;
453 }
454 }
455 else // TB_GETMAXSIZE succeeded
456 {
457 // but it could still return an incorrect result due to what appears to
458 // be a bug in old comctl32.dll versions which don't handle controls in
459 // the toolbar correctly, so work around it (see SF patch 1902358)
460 if ( !IsVertical() && wxApp::GetComCtl32Version() < 600 )
461 {
462 // calculate the toolbar width in alternative way
463 const RECT rcFirst = wxGetTBItemRect(GetHwnd(), 0);
464 const RECT rcLast = wxGetTBItemRect(GetHwnd(), GetToolsCount() - 1);
465
466 const int widthAlt = rcLast.right - rcFirst.left;
467 if ( widthAlt > size.cx )
468 size.cx = widthAlt;
469 }
470
471 sizeBest.x = size.cx;
472 sizeBest.y = size.cy;
473 }
474
475 if ( !IsVertical() )
476 {
477 // Without the extra height, DoGetBestSize can report a size that's
478 // smaller than the actual window, causing windows to overlap slightly
479 // in some circumstances, leading to missing borders (especially noticeable
480 // in AUI layouts).
481 if (!(GetWindowStyle() & wxTB_NODIVIDER))
482 sizeBest.y += 2;
483 sizeBest.y ++;
484 }
485
486 CacheBestSize(sizeBest);
487
488 return sizeBest;
489 }
490
491 WXDWORD wxToolBar::MSWGetStyle(long style, WXDWORD *exstyle) const
492 {
493 // toolbars never have border, giving one to them results in broken
494 // appearance
495 WXDWORD msStyle = wxControl::MSWGetStyle
496 (
497 (style & ~wxBORDER_MASK) | wxBORDER_NONE, exstyle
498 );
499
500 if ( !(style & wxTB_NO_TOOLTIPS) )
501 msStyle |= TBSTYLE_TOOLTIPS;
502
503 if ( style & (wxTB_FLAT | wxTB_HORZ_LAYOUT) )
504 {
505 // static as it doesn't change during the program lifetime
506 static const int s_verComCtl = wxApp::GetComCtl32Version();
507
508 // comctl32.dll 4.00 doesn't support the flat toolbars and using this
509 // style with 6.00 (part of Windows XP) leads to the toolbar with
510 // incorrect background colour - and not using it still results in the
511 // correct (flat) toolbar, so don't use it there
512 if ( s_verComCtl > 400 && s_verComCtl < 600 )
513 msStyle |= TBSTYLE_FLAT | TBSTYLE_TRANSPARENT;
514
515 if ( s_verComCtl >= 470 && style & wxTB_HORZ_LAYOUT )
516 msStyle |= TBSTYLE_LIST;
517 }
518
519 if ( style & wxTB_NODIVIDER )
520 msStyle |= CCS_NODIVIDER;
521
522 if ( style & wxTB_NOALIGN )
523 msStyle |= CCS_NOPARENTALIGN;
524
525 if ( style & wxTB_VERTICAL )
526 msStyle |= CCS_VERT;
527
528 if( style & wxTB_BOTTOM )
529 msStyle |= CCS_BOTTOM;
530
531 if ( style & wxTB_RIGHT )
532 msStyle |= CCS_RIGHT;
533
534 return msStyle;
535 }
536
537 // ----------------------------------------------------------------------------
538 // adding/removing tools
539 // ----------------------------------------------------------------------------
540
541 bool wxToolBar::DoInsertTool(size_t WXUNUSED(pos),
542 wxToolBarToolBase * WXUNUSED(tool))
543 {
544 // nothing special to do here - we really create the toolbar buttons in
545 // Realize() later
546 InvalidateBestSize();
547 return true;
548 }
549
550 bool wxToolBar::DoDeleteTool(size_t pos, wxToolBarToolBase *tool)
551 {
552 // the main difficulty we have here is with the controls in the toolbars:
553 // as we (sometimes) use several separators to cover up the space used by
554 // them, the indices are not the same for us and the toolbar
555
556 // first determine the position of the first button to delete: it may be
557 // different from pos if we use several separators to cover the space used
558 // by a control
559 wxToolBarToolsList::compatibility_iterator node;
560 for ( node = m_tools.GetFirst(); node; node = node->GetNext() )
561 {
562 wxToolBarToolBase *tool2 = node->GetData();
563 if ( tool2 == tool )
564 {
565 // let node point to the next node in the list
566 node = node->GetNext();
567
568 break;
569 }
570
571 if ( tool2->IsControl() )
572 pos += ((wxToolBarTool *)tool2)->GetSeparatorsCount() - 1;
573 }
574
575 // now determine the number of buttons to delete and the area taken by them
576 size_t nButtonsToDelete = 1;
577
578 // get the size of the button we're going to delete
579 const RECT r = wxGetTBItemRect(GetHwnd(), pos);
580
581 int width = r.right - r.left;
582
583 if ( tool->IsControl() )
584 {
585 nButtonsToDelete = ((wxToolBarTool *)tool)->GetSeparatorsCount();
586 width *= nButtonsToDelete;
587 }
588
589 // do delete all buttons
590 m_nButtons -= nButtonsToDelete;
591 while ( nButtonsToDelete-- > 0 )
592 {
593 if ( !::SendMessage(GetHwnd(), TB_DELETEBUTTON, pos, 0) )
594 {
595 wxLogLastError(wxT("TB_DELETEBUTTON"));
596
597 return false;
598 }
599 }
600
601 // and finally reposition all the controls after this button (the toolbar
602 // takes care of all normal items)
603 for ( /* node -> first after deleted */ ; node; node = node->GetNext() )
604 {
605 wxToolBarTool *tool2 = (wxToolBarTool*)node->GetData();
606 if ( tool2->IsControl() )
607 {
608 tool2->MoveBy(-width);
609 }
610 }
611
612 InvalidateBestSize();
613
614 return true;
615 }
616
617 void wxToolBar::CreateDisabledImageList()
618 {
619 if (m_disabledImgList != NULL)
620 {
621 delete m_disabledImgList;
622 m_disabledImgList = NULL;
623 }
624
625 // as we can't use disabled image list with older versions of comctl32.dll,
626 // don't even bother creating it
627 if ( wxApp::GetComCtl32Version() >= 470 )
628 {
629 // search for the first disabled button img in the toolbar, if any
630 for ( wxToolBarToolsList::compatibility_iterator
631 node = m_tools.GetFirst(); node; node = node->GetNext() )
632 {
633 wxToolBarToolBase *tool = node->GetData();
634 wxBitmap bmpDisabled = tool->GetDisabledBitmap();
635 if ( bmpDisabled.Ok() )
636 {
637 m_disabledImgList = new wxImageList
638 (
639 m_defaultWidth,
640 m_defaultHeight,
641 bmpDisabled.GetMask() != NULL,
642 GetToolsCount()
643 );
644 break;
645 }
646 }
647
648 // we don't have any disabled bitmaps
649 }
650 }
651
652 bool wxToolBar::Realize()
653 {
654 if ( !wxToolBarBase::Realize() )
655 return false;
656
657 const size_t nTools = GetToolsCount();
658
659 #ifdef wxREMAP_BUTTON_COLOURS
660 // don't change the values of these constants, they can be set from the
661 // user code via wxSystemOptions
662 enum
663 {
664 Remap_None = -1,
665 Remap_Bg,
666 Remap_Buttons,
667 Remap_TransparentBg
668 };
669
670 // the user-specified option overrides anything, but if it wasn't set, only
671 // remap the buttons on 8bpp displays as otherwise the bitmaps usually look
672 // much worse after remapping
673 static const wxChar *remapOption = wxT("msw.remap");
674 const int remapValue = wxSystemOptions::HasOption(remapOption)
675 ? wxSystemOptions::GetOptionInt(remapOption)
676 : wxDisplayDepth() <= 8 ? Remap_Buttons
677 : Remap_None;
678
679 #endif // wxREMAP_BUTTON_COLOURS
680
681 // delete all old buttons, if any
682 for ( size_t pos = 0; pos < m_nButtons; pos++ )
683 {
684 if ( !::SendMessage(GetHwnd(), TB_DELETEBUTTON, 0, 0) )
685 {
686 wxLogDebug(wxT("TB_DELETEBUTTON failed"));
687 }
688 }
689
690 // First, add the bitmap: we use one bitmap for all toolbar buttons
691 // ----------------------------------------------------------------
692
693 wxToolBarToolsList::compatibility_iterator node;
694 int bitmapId = 0;
695
696 if ( !HasFlag(wxTB_NOICONS) )
697 {
698 // if we already have a bitmap, we'll replace the existing one --
699 // otherwise we'll install a new one
700 HBITMAP oldToolBarBitmap = (HBITMAP)m_hBitmap;
701
702 const wxCoord totalBitmapWidth = m_defaultWidth *
703 wx_truncate_cast(wxCoord, nTools),
704 totalBitmapHeight = m_defaultHeight;
705
706 // Create a bitmap and copy all the tool bitmaps into it
707 wxMemoryDC dcAllButtons;
708 wxBitmap bitmap(totalBitmapWidth, totalBitmapHeight);
709 dcAllButtons.SelectObject(bitmap);
710
711 #ifdef wxREMAP_BUTTON_COLOURS
712 if ( remapValue != Remap_TransparentBg )
713 #endif // wxREMAP_BUTTON_COLOURS
714 {
715 // VZ: why do we hardcode grey colour for CE?
716 dcAllButtons.SetBackground(wxBrush(
717 #ifdef __WXWINCE__
718 wxColour(0xc0, 0xc0, 0xc0)
719 #else // !__WXWINCE__
720 GetBackgroundColour()
721 #endif // __WXWINCE__/!__WXWINCE__
722 ));
723 dcAllButtons.Clear();
724 }
725
726 m_hBitmap = bitmap.GetHBITMAP();
727 HBITMAP hBitmap = (HBITMAP)m_hBitmap;
728
729 #ifdef wxREMAP_BUTTON_COLOURS
730 if ( remapValue == Remap_Bg )
731 {
732 dcAllButtons.SelectObject(wxNullBitmap);
733
734 // Even if we're not remapping the bitmap
735 // content, we still have to remap the background.
736 hBitmap = (HBITMAP)MapBitmap((WXHBITMAP) hBitmap,
737 totalBitmapWidth, totalBitmapHeight);
738
739 dcAllButtons.SelectObject(bitmap);
740 }
741 #endif // wxREMAP_BUTTON_COLOURS
742
743 // the button position
744 wxCoord x = 0;
745
746 // the number of buttons (not separators)
747 int nButtons = 0;
748
749 CreateDisabledImageList();
750 for ( node = m_tools.GetFirst(); node; node = node->GetNext() )
751 {
752 wxToolBarToolBase *tool = node->GetData();
753 if ( tool->IsButton() )
754 {
755 const wxBitmap& bmp = tool->GetNormalBitmap();
756
757 const int w = bmp.GetWidth();
758 const int h = bmp.GetHeight();
759
760 if ( bmp.Ok() )
761 {
762 int xOffset = wxMax(0, (m_defaultWidth - w)/2);
763 int yOffset = wxMax(0, (m_defaultHeight - h)/2);
764
765 // notice the last parameter: do use mask
766 dcAllButtons.DrawBitmap(bmp, x + xOffset, yOffset, true);
767 }
768 else
769 {
770 wxFAIL_MSG( wxT("invalid tool button bitmap") );
771 }
772
773 // also deal with disabled bitmap if we want to use them
774 if ( m_disabledImgList )
775 {
776 wxBitmap bmpDisabled = tool->GetDisabledBitmap();
777 #if wxUSE_IMAGE && wxUSE_WXDIB
778 if ( !bmpDisabled.Ok() )
779 {
780 // no disabled bitmap specified but we still need to
781 // fill the space in the image list with something, so
782 // we grey out the normal bitmap
783 wxImage
784 imgGreyed = bmp.ConvertToImage().ConvertToGreyscale();
785
786 #ifdef wxREMAP_BUTTON_COLOURS
787 if ( remapValue == Remap_Buttons )
788 {
789 // we need to have light grey background colour for
790 // MapBitmap() to work correctly
791 for ( int y = 0; y < h; y++ )
792 {
793 for ( int x = 0; x < w; x++ )
794 {
795 if ( imgGreyed.IsTransparent(x, y) )
796 imgGreyed.SetRGB(x, y,
797 wxLIGHT_GREY->Red(),
798 wxLIGHT_GREY->Green(),
799 wxLIGHT_GREY->Blue());
800 }
801 }
802 }
803 #endif // wxREMAP_BUTTON_COLOURS
804
805 bmpDisabled = wxBitmap(imgGreyed);
806 }
807 #endif // wxUSE_IMAGE
808
809 #ifdef wxREMAP_BUTTON_COLOURS
810 if ( remapValue == Remap_Buttons )
811 MapBitmap(bmpDisabled.GetHBITMAP(), w, h);
812 #endif // wxREMAP_BUTTON_COLOURS
813
814 m_disabledImgList->Add(bmpDisabled);
815 }
816
817 // still inc width and number of buttons because otherwise the
818 // subsequent buttons will all be shifted which is rather confusing
819 // (and like this you'd see immediately which bitmap was bad)
820 x += m_defaultWidth;
821 nButtons++;
822 }
823 }
824
825 dcAllButtons.SelectObject(wxNullBitmap);
826
827 // don't delete this HBITMAP!
828 bitmap.SetHBITMAP(0);
829
830 #ifdef wxREMAP_BUTTON_COLOURS
831 if ( remapValue == Remap_Buttons )
832 {
833 // Map to system colours
834 hBitmap = (HBITMAP)MapBitmap((WXHBITMAP) hBitmap,
835 totalBitmapWidth, totalBitmapHeight);
836 }
837 #endif // wxREMAP_BUTTON_COLOURS
838
839 bool addBitmap = true;
840
841 if ( oldToolBarBitmap )
842 {
843 #ifdef TB_REPLACEBITMAP
844 if ( wxApp::GetComCtl32Version() >= 400 )
845 {
846 TBREPLACEBITMAP replaceBitmap;
847 replaceBitmap.hInstOld = NULL;
848 replaceBitmap.hInstNew = NULL;
849 replaceBitmap.nIDOld = (UINT_PTR)oldToolBarBitmap;
850 replaceBitmap.nIDNew = (UINT_PTR)hBitmap;
851 replaceBitmap.nButtons = nButtons;
852 if ( !::SendMessage(GetHwnd(), TB_REPLACEBITMAP,
853 0, (LPARAM) &replaceBitmap) )
854 {
855 wxFAIL_MSG(wxT("Could not replace the old bitmap"));
856 }
857
858 ::DeleteObject(oldToolBarBitmap);
859
860 // already done
861 addBitmap = false;
862 }
863 else
864 #endif // TB_REPLACEBITMAP
865 {
866 // we can't replace the old bitmap, so we will add another one
867 // (awfully inefficient, but what else to do?) and shift the bitmap
868 // indices accordingly
869 addBitmap = true;
870
871 bitmapId = m_nButtons;
872 }
873 }
874
875 if ( addBitmap ) // no old bitmap or we can't replace it
876 {
877 TBADDBITMAP addBitmap;
878 addBitmap.hInst = 0;
879 addBitmap.nID = (UINT_PTR)hBitmap;
880 if ( ::SendMessage(GetHwnd(), TB_ADDBITMAP,
881 (WPARAM) nButtons, (LPARAM)&addBitmap) == -1 )
882 {
883 wxFAIL_MSG(wxT("Could not add bitmap to toolbar"));
884 }
885 }
886
887 // disable image lists are only supported in comctl32.dll 4.70+
888 if ( wxApp::GetComCtl32Version() >= 470 )
889 {
890 HIMAGELIST hil = m_disabledImgList
891 ? GetHimagelistOf(m_disabledImgList)
892 : 0;
893
894 // notice that we set the image list even if don't have one right
895 // now as we could have it before and need to reset it in this case
896 HIMAGELIST oldImageList = (HIMAGELIST)
897 ::SendMessage(GetHwnd(), TB_SETDISABLEDIMAGELIST, 0, (LPARAM)hil);
898
899 // delete previous image list if any
900 if ( oldImageList )
901 ::DeleteObject(oldImageList);
902 }
903 }
904
905
906 // Next add the buttons and separators
907 // -----------------------------------
908
909 wxScopedArray<TBBUTTON> buttons(new TBBUTTON[nTools]);
910
911 // this array will hold the indices of all controls in the toolbar
912 wxArrayInt controlIds;
913
914 bool lastWasRadio = false;
915 int i = 0;
916 for ( node = m_tools.GetFirst(); node; node = node->GetNext() )
917 {
918 wxToolBarTool *tool = static_cast<wxToolBarTool *>(node->GetData());
919
920 // don't add separators to the vertical toolbar with old comctl32.dll
921 // versions as they didn't handle this properly
922 if ( IsVertical() && tool->IsSeparator() &&
923 wxApp::GetComCtl32Version() <= 472 )
924 {
925 continue;
926 }
927
928 TBBUTTON& button = buttons[i];
929
930 wxZeroMemory(button);
931
932 bool isRadio = false;
933 switch ( tool->GetStyle() )
934 {
935 case wxTOOL_STYLE_CONTROL:
936 case wxTOOL_STYLE_SEPARATOR:
937 if ( tool->IsStretchableSpace() )
938 {
939 // we're going to modify the size of this button later and
940 // so we need a valid id for it and not wxID_SEPARATOR
941 // which is used by spacers by default
942 tool->AllocSpacerId();
943
944 // also set the number of separators so that the logic in
945 // HandlePaint() works correctly
946 tool->SetSeparatorsCount(1);
947 }
948
949 button.idCommand = tool->GetId();
950 button.fsState = TBSTATE_ENABLED;
951 button.fsStyle = TBSTYLE_SEP;
952 break;
953
954 case wxTOOL_STYLE_BUTTON:
955 if ( !HasFlag(wxTB_NOICONS) )
956 button.iBitmap = bitmapId;
957
958 if ( HasFlag(wxTB_TEXT) )
959 {
960 const wxString& label = tool->GetLabel();
961 if ( !label.empty() )
962 button.iString = (INT_PTR)label.wx_str();
963 }
964
965 button.idCommand = tool->GetId();
966
967 if ( tool->IsEnabled() )
968 button.fsState |= TBSTATE_ENABLED;
969 if ( tool->IsToggled() )
970 button.fsState |= TBSTATE_CHECKED;
971
972 switch ( tool->GetKind() )
973 {
974 case wxITEM_RADIO:
975 button.fsStyle = TBSTYLE_CHECKGROUP;
976
977 if ( !lastWasRadio )
978 {
979 // the first item in the radio group is checked by
980 // default to be consistent with wxGTK and the menu
981 // radio items
982 button.fsState |= TBSTATE_CHECKED;
983
984 if (tool->Toggle(true))
985 {
986 DoToggleTool(tool, true);
987 }
988 }
989 else if ( tool->IsToggled() )
990 {
991 wxToolBarToolsList::compatibility_iterator nodePrev = node->GetPrevious();
992 int prevIndex = i - 1;
993 while ( nodePrev )
994 {
995 TBBUTTON& prevButton = buttons[prevIndex];
996 wxToolBarToolBase *tool = nodePrev->GetData();
997 if ( !tool->IsButton() || tool->GetKind() != wxITEM_RADIO )
998 break;
999
1000 if ( tool->Toggle(false) )
1001 DoToggleTool(tool, false);
1002
1003 prevButton.fsState &= ~TBSTATE_CHECKED;
1004 nodePrev = nodePrev->GetPrevious();
1005 prevIndex--;
1006 }
1007 }
1008
1009 isRadio = true;
1010 break;
1011
1012 case wxITEM_CHECK:
1013 button.fsStyle = TBSTYLE_CHECK;
1014 break;
1015
1016 case wxITEM_NORMAL:
1017 button.fsStyle = TBSTYLE_BUTTON;
1018 break;
1019
1020 case wxITEM_DROPDOWN:
1021 button.fsStyle = TBSTYLE_DROPDOWN;
1022 break;
1023
1024 default:
1025 wxFAIL_MSG( wxT("unexpected toolbar button kind") );
1026 button.fsStyle = TBSTYLE_BUTTON;
1027 break;
1028 }
1029
1030 bitmapId++;
1031 break;
1032 }
1033
1034 lastWasRadio = isRadio;
1035
1036 i++;
1037 }
1038
1039 if ( !::SendMessage(GetHwnd(), TB_ADDBUTTONS, i, (LPARAM)buttons.get()) )
1040 {
1041 wxLogLastError(wxT("TB_ADDBUTTONS"));
1042 }
1043
1044
1045 // Adjust controls and stretchable spaces
1046 // --------------------------------------
1047
1048 // adjust the controls size to fit nicely in the toolbar and compute its
1049 // total size while doing it
1050 m_totalFixedSize = 0;
1051 int toolIndex = 0;
1052 for ( node = m_tools.GetFirst(); node; node = node->GetNext(), toolIndex++ )
1053 {
1054 wxToolBarTool * const tool = (wxToolBarTool*)node->GetData();
1055
1056 const RECT r = wxGetTBItemRect(GetHwnd(), toolIndex);
1057
1058 if ( !tool->IsControl() )
1059 {
1060 if ( IsVertical() )
1061 m_totalFixedSize += r.bottom - r.top;
1062 else
1063 m_totalFixedSize += r.right - r.left;
1064
1065 continue;
1066 }
1067
1068 if ( IsVertical() )
1069 {
1070 // don't embed controls in the vertical toolbar, this doesn't look
1071 // good and wxGTK doesn't do it neither (and the code below can't
1072 // deal with this case)
1073 continue;
1074 }
1075
1076 wxControl * const control = tool->GetControl();
1077 wxStaticText * const staticText = tool->GetStaticText();
1078
1079 wxSize size = control->GetSize();
1080 wxSize staticTextSize;
1081 if ( staticText )
1082 {
1083 staticTextSize = staticText->GetSize();
1084 staticTextSize.y += 3; // margin between control and its label
1085 }
1086
1087 // TB_SETBUTTONINFO message is only supported by comctl32.dll 4.71+
1088 #ifdef TB_SETBUTTONINFO
1089 // available in headers, now check whether it is available now
1090 // (during run-time)
1091 if ( wxApp::GetComCtl32Version() >= 471 )
1092 {
1093 // set the (underlying) separators width to be that of the
1094 // control
1095 TBBUTTONINFO tbbi;
1096 tbbi.cbSize = sizeof(tbbi);
1097 tbbi.dwMask = TBIF_SIZE;
1098 tbbi.cx = (WORD)size.x;
1099 if ( !::SendMessage(GetHwnd(), TB_SETBUTTONINFO,
1100 tool->GetId(), (LPARAM)&tbbi) )
1101 {
1102 // the id is probably invalid?
1103 wxLogLastError(wxT("TB_SETBUTTONINFO"));
1104 }
1105 }
1106 else
1107 #endif // comctl32.dll 4.71
1108 // TB_SETBUTTONINFO unavailable
1109 {
1110 // try adding several separators to fit the controls width
1111 int widthSep = r.right - r.left;
1112
1113 TBBUTTON tbb;
1114 wxZeroMemory(tbb);
1115 tbb.idCommand = 0;
1116 tbb.fsState = TBSTATE_ENABLED;
1117 tbb.fsStyle = TBSTYLE_SEP;
1118
1119 size_t nSeparators = size.x / widthSep;
1120 for ( size_t nSep = 0; nSep < nSeparators; nSep++ )
1121 {
1122 if ( !::SendMessage(GetHwnd(), TB_INSERTBUTTON,
1123 toolIndex, (LPARAM)&tbb) )
1124 {
1125 wxLogLastError(wxT("TB_INSERTBUTTON"));
1126 }
1127
1128 toolIndex++;
1129 }
1130
1131 // remember the number of separators we used - we'd have to
1132 // delete all of them later
1133 tool->SetSeparatorsCount(nSeparators);
1134
1135 // adjust the controls width to exactly cover the separators
1136 size.x = (nSeparators + 1)*widthSep;
1137 control->SetSize(size.x, wxDefaultCoord);
1138 }
1139
1140 // position the control itself correctly vertically centering it on the
1141 // icon area of the toolbar
1142 int height = r.bottom - r.top - staticTextSize.y;
1143
1144 int diff = height - size.y;
1145 if ( diff < 0 || !HasFlag(wxTB_TEXT) )
1146 {
1147 // not enough room for the static text
1148 if ( staticText )
1149 staticText->Hide();
1150
1151 // recalculate height & diff without the staticText control
1152 height = r.bottom - r.top;
1153 diff = height - size.y;
1154 if ( diff < 0 )
1155 {
1156 // the control is too high, resize to fit
1157 control->SetSize(wxDefaultCoord, height - 2);
1158
1159 diff = 2;
1160 }
1161 }
1162 else // enough space for both the control and the label
1163 {
1164 if ( staticText )
1165 staticText->Show();
1166 }
1167
1168 control->Move(r.left, r.top + (diff + 1) / 2);
1169 if ( staticText )
1170 {
1171 staticText->Move(r.left + (size.x - staticTextSize.x)/2,
1172 r.bottom - staticTextSize.y);
1173 }
1174
1175 m_totalFixedSize += size.x;
1176 }
1177
1178 // the max index is the "real" number of buttons - i.e. counting even the
1179 // separators which we added just for aligning the controls
1180 m_nButtons = toolIndex;
1181
1182 if ( !IsVertical() )
1183 {
1184 if ( m_maxRows == 0 )
1185 // if not set yet, only one row
1186 SetRows(1);
1187 }
1188 else if ( m_nButtons > 0 ) // vertical non empty toolbar
1189 {
1190 // if not set yet, have one column
1191 m_maxRows = 1;
1192 SetRows(m_nButtons);
1193 }
1194
1195 InvalidateBestSize();
1196 UpdateSize();
1197
1198 return true;
1199 }
1200
1201 void wxToolBar::UpdateStretchableSpacersSize()
1202 {
1203 #ifdef TB_SETBUTTONINFO
1204 // we can't resize the spacers if TB_SETBUTTONINFO is not supported (we
1205 // could try to do it with multiple separators as for the controls but this
1206 // is too painful and it just doesn't seem to be worth doing for the
1207 // ancient systems)
1208 if ( wxApp::GetComCtl32Version() < 471 )
1209 return;
1210
1211 // check if we have any stretchable spacers in the first place
1212 unsigned numSpaces = 0;
1213 wxToolBarToolsList::compatibility_iterator node;
1214 for ( node = m_tools.GetFirst(); node; node = node->GetNext() )
1215 {
1216 wxToolBarTool * const tool = (wxToolBarTool*)node->GetData();
1217 if ( tool->IsStretchableSpace() )
1218 numSpaces++;
1219 }
1220
1221 if ( !numSpaces )
1222 return;
1223
1224 // we do, adjust their size: either distribute the extra size among them or
1225 // reduce their size if there is not enough place for all tools
1226 const int totalSize = IsVertical() ? GetClientSize().y : GetClientSize().x;
1227 const int extraSize = totalSize - m_totalFixedSize;
1228 const int sizeSpacer = extraSize > 0 ? extraSize / numSpaces : 0;
1229
1230 // the last spacer should consume all remaining space if we have too much
1231 // of it (which can be greater than sizeSpacer because of the rounding)
1232 const int sizeLastSpacer = extraSize > 0
1233 ? extraSize - (numSpaces - 1)*sizeSpacer
1234 : 0;
1235
1236 // cumulated offset by which we need to move all the following controls to
1237 // the right: while the toolbar takes care of the normal items, we must
1238 // move the controls manually ourselves to ensure they remain at the
1239 // correct place
1240 int offset = 0;
1241 int toolIndex = 0;
1242 for ( node = m_tools.GetFirst(); node; node = node->GetNext(), toolIndex++ )
1243 {
1244 wxToolBarTool * const tool = (wxToolBarTool*)node->GetData();
1245
1246 if ( tool->IsControl() && offset )
1247 {
1248 tool->MoveBy(offset);
1249
1250 continue;
1251 }
1252
1253 if ( !tool->IsStretchableSpace() )
1254 continue;
1255
1256 const RECT rcOld = wxGetTBItemRect(GetHwnd(), toolIndex);
1257
1258 WinStruct<TBBUTTONINFO> tbbi;
1259 tbbi.dwMask = TBIF_SIZE;
1260 tbbi.cx = --numSpaces ? sizeSpacer : sizeLastSpacer;
1261
1262 if ( !::SendMessage(GetHwnd(), TB_SETBUTTONINFO,
1263 tool->GetId(), (LPARAM)&tbbi) )
1264 {
1265 wxLogLastError(wxT("TB_SETBUTTONINFO"));
1266 }
1267 else
1268 {
1269 // we successfully resized this one, move all the controls after it
1270 // by the corresponding amount (may be positive or negative)
1271 offset += tbbi.cx - (rcOld.right - rcOld.left);
1272 }
1273 }
1274 #endif // TB_SETBUTTONINFO
1275 }
1276
1277 // ----------------------------------------------------------------------------
1278 // message handlers
1279 // ----------------------------------------------------------------------------
1280
1281 bool wxToolBar::MSWCommand(WXUINT WXUNUSED(cmd), WXWORD id_)
1282 {
1283 // cast to signed is important as we compare this id with (signed) ints in
1284 // FindById() and without the cast we'd get a positive int from a
1285 // "negative" (i.e. > 32767) WORD
1286 const int id = (signed short)id_;
1287
1288 wxToolBarToolBase *tool = FindById(id);
1289 if ( !tool )
1290 return false;
1291
1292 bool toggled = false; // just to suppress warnings
1293
1294 LRESULT state = ::SendMessage(GetHwnd(), TB_GETSTATE, id, 0);
1295
1296 if ( tool->CanBeToggled() )
1297 {
1298 toggled = (state & TBSTATE_CHECKED) != 0;
1299
1300 // ignore the event when a radio button is released, as this doesn't
1301 // seem to happen at all, and is handled otherwise
1302 if ( tool->GetKind() == wxITEM_RADIO && !toggled )
1303 return true;
1304
1305 tool->Toggle(toggled);
1306 UnToggleRadioGroup(tool);
1307 }
1308
1309 // Without the two lines of code below, if the toolbar was repainted during
1310 // OnLeftClick(), then it could end up without the tool bitmap temporarily
1311 // (see http://lists.nongnu.org/archive/html/lmi/2008-10/msg00014.html).
1312 // The Update() call bellow ensures that this won't happen, by repainting
1313 // invalidated areas of the toolbar immediately.
1314 //
1315 // To complicate matters, the tool would be drawn in depressed state (this
1316 // code is called when mouse button is released, not pressed). That's not
1317 // ideal, having the tool pressed for the duration of OnLeftClick()
1318 // provides the user with useful visual clue that the app is busy reacting
1319 // to the event. So we manually put the tool into pressed state, handle the
1320 // event and then finally restore tool's original state.
1321 ::SendMessage(GetHwnd(), TB_SETSTATE, id, MAKELONG(state | TBSTATE_PRESSED, 0));
1322 Update();
1323
1324 bool allowLeftClick = OnLeftClick(id, toggled);
1325
1326 // Restore the unpressed state. Enabled/toggled state might have been
1327 // changed since so take care of it.
1328 if (tool->IsEnabled())
1329 state |= TBSTATE_ENABLED;
1330 else
1331 state &= ~TBSTATE_ENABLED;
1332 if (tool->IsToggled())
1333 state |= TBSTATE_CHECKED;
1334 else
1335 state &= ~TBSTATE_CHECKED;
1336 ::SendMessage(GetHwnd(), TB_SETSTATE, id, MAKELONG(state, 0));
1337
1338 // OnLeftClick() can veto the button state change - for buttons which
1339 // may be toggled only, of couse
1340 if ( !allowLeftClick && tool->CanBeToggled() )
1341 {
1342 // revert back
1343 tool->Toggle(!toggled);
1344
1345 ::SendMessage(GetHwnd(), TB_CHECKBUTTON, id, MAKELONG(!toggled, 0));
1346 }
1347
1348 return true;
1349 }
1350
1351 bool wxToolBar::MSWOnNotify(int WXUNUSED(idCtrl),
1352 WXLPARAM lParam,
1353 WXLPARAM *WXUNUSED(result))
1354 {
1355 LPNMHDR hdr = (LPNMHDR)lParam;
1356 if ( hdr->code == TBN_DROPDOWN )
1357 {
1358 LPNMTOOLBAR tbhdr = (LPNMTOOLBAR)lParam;
1359
1360 wxCommandEvent evt(wxEVT_COMMAND_TOOL_DROPDOWN_CLICKED, tbhdr->iItem);
1361 if ( HandleWindowEvent(evt) )
1362 {
1363 // Event got handled, don't display default popup menu
1364 return false;
1365 }
1366
1367 const wxToolBarToolBase * const tool = FindById(tbhdr->iItem);
1368 wxCHECK_MSG( tool, false, wxT("drop down message for unknown tool") );
1369
1370 wxMenu * const menu = tool->GetDropdownMenu();
1371 if ( !menu )
1372 return false;
1373
1374 // Display popup menu below button
1375 const RECT r = wxGetTBItemRect(GetHwnd(), GetToolPos(tbhdr->iItem));
1376 if ( r.right )
1377 PopupMenu(menu, r.left, r.bottom);
1378
1379 return true;
1380 }
1381
1382
1383 if( !HasFlag(wxTB_NO_TOOLTIPS) )
1384 {
1385 #if wxUSE_TOOLTIPS
1386 // First check if this applies to us
1387
1388 // the tooltips control created by the toolbar is sometimes Unicode, even
1389 // in an ANSI application - this seems to be a bug in comctl32.dll v5
1390 UINT code = hdr->code;
1391 if ( (code != (UINT) TTN_NEEDTEXTA) && (code != (UINT) TTN_NEEDTEXTW) )
1392 return false;
1393
1394 HWND toolTipWnd = (HWND)::SendMessage(GetHwnd(), TB_GETTOOLTIPS, 0, 0);
1395 if ( toolTipWnd != hdr->hwndFrom )
1396 return false;
1397
1398 LPTOOLTIPTEXT ttText = (LPTOOLTIPTEXT)lParam;
1399 int id = (int)ttText->hdr.idFrom;
1400
1401 wxToolBarToolBase *tool = FindById(id);
1402 if ( tool )
1403 return HandleTooltipNotify(code, lParam, tool->GetShortHelp());
1404 #else
1405 wxUnusedVar(lParam);
1406 #endif
1407 }
1408
1409 return false;
1410 }
1411
1412 // ----------------------------------------------------------------------------
1413 // toolbar geometry
1414 // ----------------------------------------------------------------------------
1415
1416 void wxToolBar::SetToolBitmapSize(const wxSize& size)
1417 {
1418 wxToolBarBase::SetToolBitmapSize(size);
1419
1420 ::SendMessage(GetHwnd(), TB_SETBITMAPSIZE, 0, MAKELONG(size.x, size.y));
1421 }
1422
1423 void wxToolBar::SetRows(int nRows)
1424 {
1425 if ( nRows == m_maxRows )
1426 {
1427 // avoid resizing the frame uselessly
1428 return;
1429 }
1430
1431 // TRUE in wParam means to create at least as many rows, FALSE -
1432 // at most as many
1433 RECT rect;
1434 ::SendMessage(GetHwnd(), TB_SETROWS,
1435 MAKEWPARAM(nRows, !(GetWindowStyle() & wxTB_VERTICAL)),
1436 (LPARAM) &rect);
1437
1438 m_maxRows = nRows;
1439
1440 UpdateSize();
1441 }
1442
1443 // The button size is bigger than the bitmap size
1444 wxSize wxToolBar::GetToolSize() const
1445 {
1446 // TB_GETBUTTONSIZE is supported from version 4.70
1447 #if defined(_WIN32_IE) && (_WIN32_IE >= 0x300 ) \
1448 && !( defined(__GNUWIN32__) && !wxCHECK_W32API_VERSION( 1, 0 ) ) \
1449 && !defined (__DIGITALMARS__)
1450 if ( wxApp::GetComCtl32Version() >= 470 )
1451 {
1452 DWORD dw = ::SendMessage(GetHwnd(), TB_GETBUTTONSIZE, 0, 0);
1453
1454 return wxSize(LOWORD(dw), HIWORD(dw));
1455 }
1456 else
1457 #endif // comctl32.dll 4.70+
1458 {
1459 // defaults
1460 return wxSize(m_defaultWidth + 8, m_defaultHeight + 7);
1461 }
1462 }
1463
1464 static
1465 wxToolBarToolBase *GetItemSkippingDummySpacers(const wxToolBarToolsList& tools,
1466 size_t index )
1467 {
1468 wxToolBarToolsList::compatibility_iterator current = tools.GetFirst();
1469
1470 for ( ; current ; current = current->GetNext() )
1471 {
1472 if ( index == 0 )
1473 return current->GetData();
1474
1475 wxToolBarTool *tool = (wxToolBarTool *)current->GetData();
1476 size_t separators = tool->GetSeparatorsCount();
1477
1478 // if it is a normal button, sepcount == 0, so skip 1 item (the button)
1479 // otherwise, skip as many items as the separator count, plus the
1480 // control itself
1481 index -= separators ? separators + 1 : 1;
1482 }
1483
1484 return 0;
1485 }
1486
1487 wxToolBarToolBase *wxToolBar::FindToolForPosition(wxCoord x, wxCoord y) const
1488 {
1489 POINT pt;
1490 pt.x = x;
1491 pt.y = y;
1492 int index = (int)::SendMessage(GetHwnd(), TB_HITTEST, 0, (LPARAM)&pt);
1493
1494 // MBN: when the point ( x, y ) is close to the toolbar border
1495 // TB_HITTEST returns m_nButtons ( not -1 )
1496 if ( index < 0 || (size_t)index >= m_nButtons )
1497 // it's a separator or there is no tool at all there
1498 return NULL;
1499
1500 // when TB_SETBUTTONINFO is available (both during compile- and run-time),
1501 // we don't use the dummy separators hack
1502 #ifdef TB_SETBUTTONINFO
1503 if ( wxApp::GetComCtl32Version() >= 471 )
1504 {
1505 return m_tools.Item((size_t)index)->GetData();
1506 }
1507 else
1508 #endif // TB_SETBUTTONINFO
1509 {
1510 return GetItemSkippingDummySpacers( m_tools, (size_t) index );
1511 }
1512 }
1513
1514 void wxToolBar::UpdateSize()
1515 {
1516 wxPoint pos = GetPosition();
1517 ::SendMessage(GetHwnd(), TB_AUTOSIZE, 0, 0);
1518 if (pos != GetPosition())
1519 Move(pos);
1520
1521 // In case Realize is called after the initial display (IOW the programmer
1522 // may have rebuilt the toolbar) give the frame the option of resizing the
1523 // toolbar to full width again, but only if the parent is a frame and the
1524 // toolbar is managed by the frame. Otherwise assume that some other
1525 // layout mechanism is controlling the toolbar size and leave it alone.
1526 SendSizeEventToParent();
1527 }
1528
1529 // ----------------------------------------------------------------------------
1530 // toolbar styles
1531 // ---------------------------------------------------------------------------
1532
1533 // get the TBSTYLE of the given toolbar window
1534 long wxToolBar::GetMSWToolbarStyle() const
1535 {
1536 return ::SendMessage(GetHwnd(), TB_GETSTYLE, 0, 0L);
1537 }
1538
1539 void wxToolBar::SetWindowStyleFlag(long style)
1540 {
1541 // the style bits whose changes force us to recreate the toolbar
1542 static const long MASK_NEEDS_RECREATE = wxTB_TEXT | wxTB_NOICONS;
1543
1544 const long styleOld = GetWindowStyle();
1545
1546 wxToolBarBase::SetWindowStyleFlag(style);
1547
1548 // don't recreate an empty toolbar: not only this is unnecessary, but it is
1549 // also fatal as we'd then try to recreate the toolbar when it's just being
1550 // created
1551 if ( GetToolsCount() &&
1552 (style & MASK_NEEDS_RECREATE) != (styleOld & MASK_NEEDS_RECREATE) )
1553 {
1554 // to remove the text labels, simply re-realizing the toolbar is enough
1555 // but I don't know of any way to add the text to an existing toolbar
1556 // other than by recreating it entirely
1557 Recreate();
1558 }
1559 }
1560
1561 // ----------------------------------------------------------------------------
1562 // tool state
1563 // ----------------------------------------------------------------------------
1564
1565 void wxToolBar::DoEnableTool(wxToolBarToolBase *tool, bool enable)
1566 {
1567 ::SendMessage(GetHwnd(), TB_ENABLEBUTTON,
1568 (WPARAM)tool->GetId(), (LPARAM)MAKELONG(enable, 0));
1569 }
1570
1571 void wxToolBar::DoToggleTool(wxToolBarToolBase *tool, bool toggle)
1572 {
1573 ::SendMessage(GetHwnd(), TB_CHECKBUTTON,
1574 (WPARAM)tool->GetId(), (LPARAM)MAKELONG(toggle, 0));
1575 }
1576
1577 void wxToolBar::DoSetToggle(wxToolBarToolBase *WXUNUSED(tool), bool WXUNUSED(toggle))
1578 {
1579 // VZ: AFAIK, the button has to be created either with TBSTYLE_CHECK or
1580 // without, so we really need to delete the button and recreate it here
1581 wxFAIL_MSG( wxT("not implemented") );
1582 }
1583
1584 void wxToolBar::SetToolNormalBitmap( int id, const wxBitmap& bitmap )
1585 {
1586 wxToolBarTool* tool = static_cast<wxToolBarTool*>(FindById(id));
1587 if ( tool )
1588 {
1589 wxCHECK_RET( tool->IsButton(), wxT("Can only set bitmap on button tools."));
1590
1591 tool->SetNormalBitmap(bitmap);
1592 Realize();
1593 }
1594 }
1595
1596 void wxToolBar::SetToolDisabledBitmap( int id, const wxBitmap& bitmap )
1597 {
1598 wxToolBarTool* tool = static_cast<wxToolBarTool*>(FindById(id));
1599 if ( tool )
1600 {
1601 wxCHECK_RET( tool->IsButton(), wxT("Can only set bitmap on button tools."));
1602
1603 tool->SetDisabledBitmap(bitmap);
1604 Realize();
1605 }
1606 }
1607
1608 // ----------------------------------------------------------------------------
1609 // event handlers
1610 // ----------------------------------------------------------------------------
1611
1612 // Responds to colour changes, and passes event on to children.
1613 void wxToolBar::OnSysColourChanged(wxSysColourChangedEvent& event)
1614 {
1615 wxRGBToColour(m_backgroundColour, ::GetSysColor(COLOR_BTNFACE));
1616
1617 // Remap the buttons
1618 Realize();
1619
1620 // Relayout the toolbar
1621 int nrows = m_maxRows;
1622 m_maxRows = 0; // otherwise SetRows() wouldn't do anything
1623 SetRows(nrows);
1624
1625 Refresh();
1626
1627 // let the event propagate further
1628 event.Skip();
1629 }
1630
1631 void wxToolBar::OnMouseEvent(wxMouseEvent& event)
1632 {
1633 if ( event.Leaving() )
1634 {
1635 if ( m_pInTool )
1636 {
1637 OnMouseEnter(wxID_ANY);
1638 m_pInTool = NULL;
1639 }
1640
1641 event.Skip();
1642 return;
1643 }
1644
1645 if ( event.RightDown() )
1646 {
1647 // find the tool under the mouse
1648 wxCoord x = 0, y = 0;
1649 event.GetPosition(&x, &y);
1650
1651 wxToolBarToolBase *tool = FindToolForPosition(x, y);
1652 OnRightClick(tool ? tool->GetId() : -1, x, y);
1653 }
1654 else
1655 {
1656 event.Skip();
1657 }
1658 }
1659
1660 bool wxToolBar::HandleSize(WXWPARAM WXUNUSED(wParam), WXLPARAM lParam)
1661 {
1662 // wait until we have some tools
1663 if ( !GetToolsCount() )
1664 return false;
1665
1666 // calculate our minor dimension ourselves - we're confusing the standard
1667 // logic (TB_AUTOSIZE) with our horizontal toolbars and other hacks
1668 const RECT r = wxGetTBItemRect(GetHwnd(), 0);
1669 if ( !r.right )
1670 return false;
1671
1672 int w, h;
1673
1674 if ( IsVertical() )
1675 {
1676 w = r.right - r.left;
1677 if ( m_maxRows )
1678 {
1679 w *= (m_nButtons + m_maxRows - 1)/m_maxRows;
1680 }
1681 h = HIWORD(lParam);
1682 }
1683 else
1684 {
1685 w = LOWORD(lParam);
1686 if (HasFlag( wxTB_FLAT ))
1687 h = r.bottom - r.top - 3;
1688 else
1689 h = r.bottom - r.top;
1690 if ( m_maxRows )
1691 {
1692 // FIXME: hardcoded separator line height...
1693 h += HasFlag(wxTB_NODIVIDER) ? 4 : 6;
1694 h *= m_maxRows;
1695 }
1696 }
1697
1698 if ( MAKELPARAM(w, h) != lParam )
1699 {
1700 // size really changed
1701 SetSize(w, h);
1702 }
1703
1704 UpdateStretchableSpacersSize();
1705
1706 // message processed
1707 return true;
1708 }
1709
1710 #ifndef __WXWINCE__
1711
1712 bool wxToolBar::HandlePaint(WXWPARAM WXUNUSED(wParam), WXLPARAM WXUNUSED(lParam))
1713 {
1714 // exclude the area occupied by the controls and stretchable spaces from
1715 // the update region to prevent the toolbar from drawing separators in it
1716 int toolIndex = 0;
1717 for ( wxToolBarToolsList::compatibility_iterator node = m_tools.GetFirst();
1718 node;
1719 node = node->GetNext() )
1720 {
1721 wxToolBarTool * const
1722 tool = static_cast<wxToolBarTool *>(node->GetData());
1723
1724 if ( tool->IsControl() || tool->IsStretchableSpace() )
1725 {
1726 const size_t numSeps = tool->GetSeparatorsCount();
1727 for ( size_t n = 0; n < numSeps; n++, toolIndex++ )
1728 {
1729 const RECT rcItem = wxGetTBItemRect(GetHwnd(), toolIndex);
1730
1731 const wxRegion rgnItem(wxRectFromRECT(rcItem));
1732 if ( !ValidateRgn(GetHwnd(), GetHrgnOf(rgnItem)) )
1733 {
1734 wxLogLastError(wxT("ValidateRgn()"));
1735 }
1736 }
1737 }
1738 else
1739 {
1740 // normal tools never correspond to more than one native button
1741 toolIndex++;
1742 }
1743 }
1744
1745 // still let the native control draw everything else normally
1746 return false;
1747 }
1748 #endif // __WXWINCE__
1749
1750 void wxToolBar::HandleMouseMove(WXWPARAM WXUNUSED(wParam), WXLPARAM lParam)
1751 {
1752 wxCoord x = GET_X_LPARAM(lParam),
1753 y = GET_Y_LPARAM(lParam);
1754 wxToolBarToolBase* tool = FindToolForPosition( x, y );
1755
1756 // has the current tool changed?
1757 if ( tool != m_pInTool )
1758 {
1759 m_pInTool = tool;
1760 OnMouseEnter(tool ? tool->GetId() : wxID_ANY);
1761 }
1762 }
1763
1764 WXLRESULT wxToolBar::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
1765 {
1766 switch ( nMsg )
1767 {
1768 case WM_MOUSEMOVE:
1769 // we don't handle mouse moves, so always pass the message to
1770 // wxControl::MSWWindowProc (HandleMouseMove just calls OnMouseEnter)
1771 HandleMouseMove(wParam, lParam);
1772 break;
1773
1774 case WM_SIZE:
1775 if ( HandleSize(wParam, lParam) )
1776 return 0;
1777 break;
1778
1779 #ifndef __WXWINCE__
1780 case WM_PAINT:
1781 // refreshing the controls in the toolbar inside a composite window
1782 // results in an endless stream of WM_PAINT messages -- and seems
1783 // to be unnecessary anyhow as everything works just fine without
1784 // any special workarounds in this case
1785 if ( !IsDoubleBuffered() && HandlePaint(wParam, lParam) )
1786 return 0;
1787 break;
1788 #endif // __WXWINCE__
1789 }
1790
1791 return wxControl::MSWWindowProc(nMsg, wParam, lParam);
1792 }
1793
1794 // ----------------------------------------------------------------------------
1795 // private functions
1796 // ----------------------------------------------------------------------------
1797
1798 #ifdef wxREMAP_BUTTON_COLOURS
1799
1800 WXHBITMAP wxToolBar::MapBitmap(WXHBITMAP bitmap, int width, int height)
1801 {
1802 MemoryHDC hdcMem;
1803
1804 if ( !hdcMem )
1805 {
1806 wxLogLastError(wxT("CreateCompatibleDC"));
1807
1808 return bitmap;
1809 }
1810
1811 SelectInHDC bmpInHDC(hdcMem, (HBITMAP)bitmap);
1812
1813 if ( !bmpInHDC )
1814 {
1815 wxLogLastError(wxT("SelectObject"));
1816
1817 return bitmap;
1818 }
1819
1820 wxCOLORMAP *cmap = wxGetStdColourMap();
1821
1822 for ( int i = 0; i < width; i++ )
1823 {
1824 for ( int j = 0; j < height; j++ )
1825 {
1826 COLORREF pixel = ::GetPixel(hdcMem, i, j);
1827
1828 for ( size_t k = 0; k < wxSTD_COL_MAX; k++ )
1829 {
1830 COLORREF col = cmap[k].from;
1831 if ( abs(GetRValue(pixel) - GetRValue(col)) < 10 &&
1832 abs(GetGValue(pixel) - GetGValue(col)) < 10 &&
1833 abs(GetBValue(pixel) - GetBValue(col)) < 10 )
1834 {
1835 if ( cmap[k].to != pixel )
1836 ::SetPixel(hdcMem, i, j, cmap[k].to);
1837 break;
1838 }
1839 }
1840 }
1841 }
1842
1843 return bitmap;
1844 }
1845
1846 #endif // wxREMAP_BUTTON_COLOURS
1847
1848 #endif // wxUSE_TOOLBAR