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