]> git.saurik.com Git - wxWidgets.git/blob - src/msw/tbar95.cpp
don't allow setting an invalid font
[wxWidgets.git] / src / msw / tbar95.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: 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 and Markus Holzem
9 // Licence: wxWindows license
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 #ifdef __GNUG__
21 #pragma implementation "tbar95.h"
22 #endif
23
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
26
27 #ifdef __BORLANDC__
28 #pragma hdrstop
29 #endif
30
31 #ifndef WX_PRECOMP
32 #include "wx/frame.h"
33 #include "wx/log.h"
34 #include "wx/intl.h"
35 #include "wx/dynarray.h"
36 #include "wx/settings.h"
37 #include "wx/bitmap.h"
38 #include "wx/dcmemory.h"
39 #endif
40
41 #if wxUSE_TOOLBAR && defined(__WIN95__) && wxUSE_TOOLBAR_NATIVE
42
43 #include "wx/toolbar.h"
44
45 #if !defined(__GNUWIN32__) && !defined(__SALFORDC__)
46 #include "malloc.h"
47 #endif
48
49 #include "wx/msw/private.h"
50
51 #ifndef __TWIN32__
52
53 #if defined(__WIN95__) && !((defined(__GNUWIN32_OLD__) || defined(__TWIN32__)) && !defined(__CYGWIN10__))
54 #include <commctrl.h>
55 #else
56 #include "wx/msw/gnuwin32/extra.h"
57 #endif
58
59 #endif // __TWIN32__
60
61 #include "wx/msw/dib.h"
62 #include "wx/app.h" // for GetComCtl32Version
63
64 #if defined(__MWERKS__) && defined(__WXMSW__)
65 // including <windef.h> for max definition doesn't seem
66 // to work using CodeWarrior 6 Windows. So we define it
67 // here. (Otherwise we get a undefined identifier 'max'
68 // later on in this file.) (Added by dimitri@shortcut.nl)
69 # ifndef max
70 # define max(a,b) (((a) > (b)) ? (a) : (b))
71 # endif
72
73 #endif
74
75 // ----------------------------------------------------------------------------
76 // conditional compilation
77 // ----------------------------------------------------------------------------
78
79 // wxWindows previously always considered that toolbar buttons have light grey
80 // (0xc0c0c0) background and so ignored any bitmap masks - however, this
81 // doesn't work with XPMs which then appear to have black background. To make
82 // this work, we must respect the bitmap masks - which we do now. This should
83 // be ok in any case, but to restore 100% compatible with the old version
84 // behaviour, you can set this to 0.
85 #define USE_BITMAP_MASKS 1
86
87 // ----------------------------------------------------------------------------
88 // constants
89 // ----------------------------------------------------------------------------
90
91 // these standard constants are not always defined in compilers headers
92
93 // Styles
94 #ifndef TBSTYLE_FLAT
95 #define TBSTYLE_LIST 0x1000
96 #define TBSTYLE_FLAT 0x0800
97 #define TBSTYLE_TRANSPARENT 0x8000
98 #endif
99 // use TBSTYLE_TRANSPARENT if you use TBSTYLE_FLAT
100
101 // Messages
102 #ifndef TB_GETSTYLE
103 #define TB_SETSTYLE (WM_USER + 56)
104 #define TB_GETSTYLE (WM_USER + 57)
105 #endif
106
107 #ifndef TB_HITTEST
108 #define TB_HITTEST (WM_USER + 69)
109 #endif
110
111 // these values correspond to those used by comctl32.dll
112 #define DEFAULTBITMAPX 16
113 #define DEFAULTBITMAPY 15
114 #define DEFAULTBUTTONX 24
115 #define DEFAULTBUTTONY 24
116 #define DEFAULTBARHEIGHT 27
117
118 // ----------------------------------------------------------------------------
119 // wxWin macros
120 // ----------------------------------------------------------------------------
121
122 IMPLEMENT_DYNAMIC_CLASS(wxToolBar, wxToolBarBase)
123
124 BEGIN_EVENT_TABLE(wxToolBar, wxToolBarBase)
125 EVT_MOUSE_EVENTS(wxToolBar::OnMouseEvent)
126 EVT_SYS_COLOUR_CHANGED(wxToolBar::OnSysColourChanged)
127 END_EVENT_TABLE()
128
129 // ----------------------------------------------------------------------------
130 // private classes
131 // ----------------------------------------------------------------------------
132
133 class wxToolBarTool : public wxToolBarToolBase
134 {
135 public:
136 wxToolBarTool(wxToolBar *tbar,
137 int id,
138 const wxBitmap& bitmap1,
139 const wxBitmap& bitmap2,
140 bool toggle,
141 wxObject *clientData,
142 const wxString& shortHelpString,
143 const wxString& longHelpString)
144 : wxToolBarToolBase(tbar, id, bitmap1, bitmap2, toggle,
145 clientData, shortHelpString, longHelpString)
146 {
147 m_nSepCount = 0;
148 }
149
150 wxToolBarTool(wxToolBar *tbar, wxControl *control)
151 : wxToolBarToolBase(tbar, control)
152 {
153 m_nSepCount = 1;
154 }
155
156 // set/get the number of separators which we use to cover the space used by
157 // a control in the toolbar
158 void SetSeparatorsCount(size_t count) { m_nSepCount = count; }
159 size_t GetSeparatorsCount() const { return m_nSepCount; }
160
161 private:
162 size_t m_nSepCount;
163 };
164
165
166 // ============================================================================
167 // implementation
168 // ============================================================================
169
170 // ----------------------------------------------------------------------------
171 // wxToolBarTool
172 // ----------------------------------------------------------------------------
173
174 wxToolBarToolBase *wxToolBar::CreateTool(int id,
175 const wxBitmap& bitmap1,
176 const wxBitmap& bitmap2,
177 bool toggle,
178 wxObject *clientData,
179 const wxString& shortHelpString,
180 const wxString& longHelpString)
181 {
182 return new wxToolBarTool(this, id, bitmap1, bitmap2, toggle,
183 clientData, shortHelpString, longHelpString);
184 }
185
186 wxToolBarToolBase *wxToolBar::CreateTool(wxControl *control)
187 {
188 return new wxToolBarTool(this, control);
189 }
190
191 // ----------------------------------------------------------------------------
192 // wxToolBar construction
193 // ----------------------------------------------------------------------------
194
195 void wxToolBar::Init()
196 {
197 m_hBitmap = 0;
198
199 m_nButtons = 0;
200
201 m_defaultWidth = DEFAULTBITMAPX;
202 m_defaultHeight = DEFAULTBITMAPY;
203
204 m_pInTool = 0;
205 }
206
207 bool wxToolBar::Create(wxWindow *parent,
208 wxWindowID id,
209 const wxPoint& pos,
210 const wxSize& size,
211 long style,
212 const wxString& name)
213 {
214 // toolbars never have border, giving one to them results in broken
215 // appearance
216 style &= ~wxBORDER_MASK;
217 style |= wxBORDER_NONE;
218
219 // common initialisation
220 if ( !CreateControl(parent, id, pos, size, style, wxDefaultValidator, name) )
221 return FALSE;
222
223 // prepare flags
224 DWORD msflags = 0; // WS_VISIBLE | WS_CHILD always included
225
226 if ( style & wxCLIP_SIBLINGS )
227 msflags |= WS_CLIPSIBLINGS;
228
229 #ifdef TBSTYLE_TOOLTIPS
230 msflags |= TBSTYLE_TOOLTIPS;
231 #endif
232
233 if (style & wxTB_FLAT)
234 {
235 if (wxTheApp->GetComCtl32Version() > 400)
236 msflags |= TBSTYLE_FLAT;
237 }
238
239 // MSW-specific initialisation
240 if ( !wxControl::MSWCreateControl(TOOLBARCLASSNAME, msflags) )
241 return FALSE;
242
243 // toolbar-specific post initialisation
244 ::SendMessage(GetHwnd(), TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0);
245
246 // set up the colors and fonts
247 wxRGBToColour(m_backgroundColour, GetSysColor(COLOR_BTNFACE));
248 m_foregroundColour = *wxBLACK;
249
250 SetFont(wxSystemSettings::GetSystemFont(wxSYS_DEFAULT_GUI_FONT));
251
252 // position it
253 int x = pos.x;
254 int y = pos.y;
255 int width = size.x;
256 int height = size.y;
257
258 if (width <= 0)
259 width = 100;
260 if (height <= 0)
261 height = m_defaultHeight;
262 if (x < 0)
263 x = 0;
264 if (y < 0)
265 y = 0;
266
267 SetSize(x, y, width, height);
268
269 return TRUE;
270 }
271
272 wxToolBar::~wxToolBar()
273 {
274 // we must refresh the frame size when the toolbar is deleted but the frame
275 // is not - otherwise toolbar leaves a hole in the place it used to occupy
276 wxFrame *frame = wxDynamicCast(GetParent(), wxFrame);
277 if ( frame && !frame->IsBeingDeleted() )
278 {
279 frame->SendSizeEvent();
280 }
281
282 if ( m_hBitmap )
283 {
284 ::DeleteObject((HBITMAP) m_hBitmap);
285 }
286 }
287
288 // ----------------------------------------------------------------------------
289 // adding/removing tools
290 // ----------------------------------------------------------------------------
291
292 bool wxToolBar::DoInsertTool(size_t WXUNUSED(pos),
293 wxToolBarToolBase *tool)
294 {
295 // nothing special to do here - we really create the toolbar buttons in
296 // Realize() later
297 tool->Attach(this);
298
299 return TRUE;
300 }
301
302 bool wxToolBar::DoDeleteTool(size_t pos, wxToolBarToolBase *tool)
303 {
304 // the main difficulty we have here is with the controls in the toolbars:
305 // as we (sometimes) use several separators to cover up the space used by
306 // them, the indices are not the same for us and the toolbar
307
308 // first determine the position of the first button to delete: it may be
309 // different from pos if we use several separators to cover the space used
310 // by a control
311 wxToolBarToolsList::Node *node;
312 for ( node = m_tools.GetFirst(); node; node = node->GetNext() )
313 {
314 wxToolBarToolBase *tool2 = node->GetData();
315 if ( tool2 == tool )
316 {
317 // let node point to the next node in the list
318 node = node->GetNext();
319
320 break;
321 }
322
323 if ( tool2->IsControl() )
324 {
325 pos += ((wxToolBarTool *)tool2)->GetSeparatorsCount() - 1;
326 }
327 }
328
329 // now determine the number of buttons to delete and the area taken by them
330 size_t nButtonsToDelete = 1;
331
332 // get the size of the button we're going to delete
333 RECT r;
334 if ( !::SendMessage(GetHwnd(), TB_GETITEMRECT, pos, (LPARAM)&r) )
335 {
336 wxLogLastError(_T("TB_GETITEMRECT"));
337 }
338
339 int width = r.right - r.left;
340
341 if ( tool->IsControl() )
342 {
343 nButtonsToDelete = ((wxToolBarTool *)tool)->GetSeparatorsCount();
344
345 width *= nButtonsToDelete;
346 }
347
348 // do delete all buttons
349 m_nButtons -= nButtonsToDelete;
350 while ( nButtonsToDelete-- > 0 )
351 {
352 if ( !::SendMessage(GetHwnd(), TB_DELETEBUTTON, pos, 0) )
353 {
354 wxLogLastError(wxT("TB_DELETEBUTTON"));
355
356 return FALSE;
357 }
358 }
359
360 tool->Detach();
361
362 // and finally reposition all the controls after this button (the toolbar
363 // takes care of all normal items)
364 for ( /* node -> first after deleted */ ; node; node = node->GetNext() )
365 {
366 wxToolBarToolBase *tool2 = node->GetData();
367 if ( tool2->IsControl() )
368 {
369 int x;
370 wxControl *control = tool2->GetControl();
371 control->GetPosition(&x, NULL);
372 control->Move(x - width, -1);
373 }
374 }
375
376 return TRUE;
377 }
378
379 bool wxToolBar::Realize()
380 {
381 size_t nTools = GetToolsCount();
382 if ( nTools == 0 )
383 {
384 // nothing to do
385 return TRUE;
386 }
387
388 bool isVertical = (GetWindowStyle() & wxTB_VERTICAL) != 0;
389
390 // First, add the bitmap: we use one bitmap for all toolbar buttons
391 // ----------------------------------------------------------------
392
393 // if we already have a bitmap, we'll replace the existing one - otherwise
394 // we'll install a new one
395 HBITMAP oldToolBarBitmap = (HBITMAP)m_hBitmap;
396
397 int totalBitmapWidth = (int)(m_defaultWidth * nTools);
398 int totalBitmapHeight = (int)m_defaultHeight;
399
400 // Create a bitmap and copy all the tool bitmaps to it
401 #if USE_BITMAP_MASKS
402 wxMemoryDC dcAllButtons;
403 wxBitmap bitmap(totalBitmapWidth, totalBitmapHeight);
404 dcAllButtons.SelectObject(bitmap);
405 dcAllButtons.SetBackground(*wxLIGHT_GREY_BRUSH);
406 dcAllButtons.Clear();
407
408 m_hBitmap = bitmap.GetHBITMAP();
409 HBITMAP hBitmap = (HBITMAP)m_hBitmap;
410 #else // !USE_BITMAP_MASKS
411 HBITMAP hBitmap = ::CreateCompatibleBitmap(ScreenHDC(),
412 totalBitmapWidth,
413 totalBitmapHeight);
414 if ( !hBitmap )
415 {
416 wxLogLastError(_T("CreateCompatibleBitmap"));
417
418 return FALSE;
419 }
420
421 m_hBitmap = (WXHBITMAP)hBitmap;
422
423 HDC memoryDC = ::CreateCompatibleDC(NULL);
424 HBITMAP oldBitmap = (HBITMAP) ::SelectObject(memoryDC, hBitmap);
425
426 HDC memoryDC2 = ::CreateCompatibleDC(NULL);
427 #endif // USE_BITMAP_MASKS/!USE_BITMAP_MASKS
428
429 // the button position
430 wxCoord x = 0;
431
432 // the number of buttons (not separators)
433 int nButtons = 0;
434
435 wxToolBarToolsList::Node *node = m_tools.GetFirst();
436 while ( node )
437 {
438 wxToolBarToolBase *tool = node->GetData();
439 if ( tool->IsButton() )
440 {
441 const wxBitmap& bmp = tool->GetBitmap1();
442 if ( bmp.Ok() )
443 {
444 #if USE_BITMAP_MASKS
445 // notice the last parameter: do use mask
446 dcAllButtons.DrawBitmap(tool->GetBitmap1(), x, 0, TRUE);
447 #else // !USE_BITMAP_MASKS
448 HBITMAP hbmp = GetHbitmapOf(bmp);
449 HBITMAP oldBitmap2 = (HBITMAP)::SelectObject(memoryDC2, hbmp);
450 if ( !BitBlt(memoryDC, x, 0, m_defaultWidth, m_defaultHeight,
451 memoryDC2, 0, 0, SRCCOPY) )
452 {
453 wxLogLastError(wxT("BitBlt"));
454 }
455
456 ::SelectObject(memoryDC2, oldBitmap2);
457 #endif // USE_BITMAP_MASKS/!USE_BITMAP_MASKS
458 }
459 else
460 {
461 wxFAIL_MSG( _T("invalid tool button bitmap") );
462 }
463
464 // still inc width and number of buttons because otherwise the
465 // subsequent buttons will all be shifted which is rather confusing
466 // (and like this you'd see immediately which bitmap was bad)
467 x += m_defaultWidth;
468 nButtons++;
469 }
470
471 node = node->GetNext();
472 }
473
474 #if USE_BITMAP_MASKS
475 dcAllButtons.SelectObject(wxNullBitmap);
476
477 // don't delete this HBITMAP!
478 bitmap.SetHBITMAP(0);
479 #else // !USE_BITMAP_MASKS
480 ::SelectObject(memoryDC, oldBitmap);
481 ::DeleteDC(memoryDC);
482 ::DeleteDC(memoryDC2);
483 #endif // USE_BITMAP_MASKS/!USE_BITMAP_MASKS
484
485 // Map to system colours
486 hBitmap = (HBITMAP)MapBitmap((WXHBITMAP) hBitmap,
487 totalBitmapWidth, totalBitmapHeight);
488
489 int bitmapId = 0;
490
491 bool addBitmap = TRUE;
492
493 if ( oldToolBarBitmap )
494 {
495 #ifdef TB_REPLACEBITMAP
496 if ( wxTheApp->GetComCtl32Version() >= 400 )
497 {
498 TBREPLACEBITMAP replaceBitmap;
499 replaceBitmap.hInstOld = NULL;
500 replaceBitmap.hInstNew = NULL;
501 replaceBitmap.nIDOld = (UINT) oldToolBarBitmap;
502 replaceBitmap.nIDNew = (UINT) hBitmap;
503 replaceBitmap.nButtons = nButtons;
504 if ( !::SendMessage(GetHwnd(), TB_REPLACEBITMAP,
505 0, (LPARAM) &replaceBitmap) )
506 {
507 wxFAIL_MSG(wxT("Could not replace the old bitmap"));
508 }
509
510 ::DeleteObject(oldToolBarBitmap);
511
512 // already done
513 addBitmap = FALSE;
514 }
515 else
516 #endif // TB_REPLACEBITMAP
517 {
518 // we can't replace the old bitmap, so we will add another one
519 // (awfully inefficient, but what else to do?) and shift the bitmap
520 // indices accordingly
521 addBitmap = TRUE;
522
523 bitmapId = m_nButtons;
524 }
525
526 // Now delete all the buttons
527 for ( size_t pos = 0; pos < m_nButtons; pos++ )
528 {
529 if ( !::SendMessage(GetHwnd(), TB_DELETEBUTTON, 0, 0) )
530 {
531 wxLogDebug(wxT("TB_DELETEBUTTON failed"));
532 }
533 }
534
535 }
536
537 if ( addBitmap ) // no old bitmap or we can't replace it
538 {
539 TBADDBITMAP addBitmap;
540 addBitmap.hInst = 0;
541 addBitmap.nID = (UINT) hBitmap;
542 if ( ::SendMessage(GetHwnd(), TB_ADDBITMAP,
543 (WPARAM) nButtons, (LPARAM)&addBitmap) == -1 )
544 {
545 wxFAIL_MSG(wxT("Could not add bitmap to toolbar"));
546 }
547 }
548
549 // Next add the buttons and separators
550 // -----------------------------------
551
552 TBBUTTON *buttons = new TBBUTTON[nTools];
553
554 // this array will hold the indices of all controls in the toolbar
555 wxArrayInt controlIds;
556
557 int i = 0;
558 for ( node = m_tools.GetFirst(); node; node = node->GetNext() )
559 {
560 wxToolBarToolBase *tool = node->GetData();
561
562 // don't add separators to the vertical toolbar - looks ugly
563 if ( isVertical && tool->IsSeparator() )
564 continue;
565
566 TBBUTTON& button = buttons[i];
567
568 wxZeroMemory(button);
569
570 switch ( tool->GetStyle() )
571 {
572 case wxTOOL_STYLE_CONTROL:
573 button.idCommand = tool->GetId();
574 // fall through: create just a separator too
575
576 case wxTOOL_STYLE_SEPARATOR:
577 button.fsState = TBSTATE_ENABLED;
578 button.fsStyle = TBSTYLE_SEP;
579 break;
580
581 case wxTOOL_STYLE_BUTTON:
582 button.iBitmap = bitmapId;
583 button.idCommand = tool->GetId();
584
585 if ( tool->IsEnabled() )
586 button.fsState |= TBSTATE_ENABLED;
587 if ( tool->IsToggled() )
588 button.fsState |= TBSTATE_CHECKED;
589
590 button.fsStyle = tool->CanBeToggled() ? TBSTYLE_CHECK
591 : TBSTYLE_BUTTON;
592
593 bitmapId++;
594 break;
595 }
596
597 i++;
598 }
599
600 if ( !::SendMessage(GetHwnd(), TB_ADDBUTTONS,
601 (WPARAM)i, (LPARAM)buttons) )
602 {
603 wxLogLastError(wxT("TB_ADDBUTTONS"));
604 }
605
606 delete [] buttons;
607
608 // Deal with the controls finally
609 // ------------------------------
610
611 // adjust the controls size to fit nicely in the toolbar
612 int y = 0;
613 size_t index = 0;
614 for ( node = m_tools.GetFirst(); node; node = node->GetNext(), index++ )
615 {
616 wxToolBarToolBase *tool = node->GetData();
617
618 // we calculate the running y coord for vertical toolbars so we need to
619 // get the items size for all items but for the horizontal ones we
620 // don't need to deal with the non controls
621 bool isControl = tool->IsControl();
622 if ( !isControl && !isVertical )
623 continue;
624
625 // note that we use TB_GETITEMRECT and not TB_GETRECT because the
626 // latter only appeared in v4.70 of comctl32.dll
627 RECT r;
628 if ( !SendMessage(GetHwnd(), TB_GETITEMRECT,
629 index, (LPARAM)(LPRECT)&r) )
630 {
631 wxLogLastError(wxT("TB_GETITEMRECT"));
632 }
633
634 if ( !isControl )
635 {
636 // can only be control if isVertical
637 y += r.bottom - r.top;
638
639 continue;
640 }
641
642 wxControl *control = tool->GetControl();
643
644 wxSize size = control->GetSize();
645
646 // the position of the leftmost controls corner
647 int left = -1;
648
649 // TB_SETBUTTONINFO message is only supported by comctl32.dll 4.71+
650 #if defined(_WIN32_IE) && (_WIN32_IE >= 0x400 )
651 // available in headers, now check whether it is available now
652 // (during run-time)
653 if ( wxTheApp->GetComCtl32Version() >= 471 )
654 {
655 // set the (underlying) separators width to be that of the
656 // control
657 TBBUTTONINFO tbbi;
658 tbbi.cbSize = sizeof(tbbi);
659 tbbi.dwMask = TBIF_SIZE;
660 tbbi.cx = size.x;
661 if ( !SendMessage(GetHwnd(), TB_SETBUTTONINFO,
662 tool->GetId(), (LPARAM)&tbbi) )
663 {
664 // the id is probably invalid?
665 wxLogLastError(wxT("TB_SETBUTTONINFO"));
666 }
667 }
668 else
669 #endif // comctl32.dll 4.71
670 // TB_SETBUTTONINFO unavailable
671 {
672 // try adding several separators to fit the controls width
673 int widthSep = r.right - r.left;
674 left = r.left;
675
676 TBBUTTON tbb;
677 wxZeroMemory(tbb);
678 tbb.idCommand = 0;
679 tbb.fsState = TBSTATE_ENABLED;
680 tbb.fsStyle = TBSTYLE_SEP;
681
682 size_t nSeparators = size.x / widthSep;
683 for ( size_t nSep = 0; nSep < nSeparators; nSep++ )
684 {
685 if ( !SendMessage(GetHwnd(), TB_INSERTBUTTON,
686 index, (LPARAM)&tbb) )
687 {
688 wxLogLastError(wxT("TB_INSERTBUTTON"));
689 }
690
691 index++;
692 }
693
694 // remember the number of separators we used - we'd have to
695 // delete all of them later
696 ((wxToolBarTool *)tool)->SetSeparatorsCount(nSeparators);
697
698 // adjust the controls width to exactly cover the separators
699 control->SetSize((nSeparators + 1)*widthSep, -1);
700 }
701
702 // position the control itself correctly vertically
703 int height = r.bottom - r.top;
704 int diff = height - size.y;
705 if ( diff < 0 )
706 {
707 // the control is too high, resize to fit
708 control->SetSize(-1, height - 2);
709
710 diff = 2;
711 }
712
713 int top;
714 if ( isVertical )
715 {
716 left = 0;
717 top = y;
718
719 y += height + 2*GetMargins().y;
720 }
721 else // horizontal toolbar
722 {
723 if ( left == -1 )
724 left = r.left;
725
726 top = r.top;
727 }
728
729 control->Move(left, top + (diff + 1) / 2);
730 }
731
732 // the max index is the "real" number of buttons - i.e. counting even the
733 // separators which we added just for aligning the controls
734 m_nButtons = index;
735
736 if ( !isVertical )
737 {
738 if ( m_maxRows == 0 )
739 {
740 // if not set yet, only one row
741 SetRows(1);
742 }
743 }
744 else if ( m_nButtons > 0 ) // vertical non empty toolbar
745 {
746 if ( m_maxRows == 0 )
747 {
748 // if not set yet, have one column
749 SetRows(m_nButtons);
750 }
751 }
752
753 return TRUE;
754 }
755
756 // ----------------------------------------------------------------------------
757 // message handlers
758 // ----------------------------------------------------------------------------
759
760 bool wxToolBar::MSWCommand(WXUINT WXUNUSED(cmd), WXWORD id)
761 {
762 wxToolBarToolBase *tool = FindById((int)id);
763 if ( !tool )
764 return FALSE;
765
766 if ( tool->CanBeToggled() )
767 {
768 LRESULT state = ::SendMessage(GetHwnd(), TB_GETSTATE, id, 0);
769 tool->Toggle((state & TBSTATE_CHECKED) != 0);
770 }
771
772 bool toggled = tool->IsToggled();
773
774 // OnLeftClick() can veto the button state change - for buttons which may
775 // be toggled only, of couse
776 if ( !OnLeftClick((int)id, toggled) && tool->CanBeToggled() )
777 {
778 // revert back
779 toggled = !toggled;
780 tool->SetToggle(toggled);
781
782 ::SendMessage(GetHwnd(), TB_CHECKBUTTON, id, MAKELONG(toggled, 0));
783 }
784
785 return TRUE;
786 }
787
788 bool wxToolBar::MSWOnNotify(int WXUNUSED(idCtrl),
789 WXLPARAM lParam,
790 WXLPARAM *WXUNUSED(result))
791 {
792 // First check if this applies to us
793 NMHDR *hdr = (NMHDR *)lParam;
794
795 // the tooltips control created by the toolbar is sometimes Unicode, even
796 // in an ANSI application - this seems to be a bug in comctl32.dll v5
797 int code = (int)hdr->code;
798 if ( (code != TTN_NEEDTEXTA) && (code != TTN_NEEDTEXTW) )
799 return FALSE;
800
801 HWND toolTipWnd = (HWND)::SendMessage((HWND)GetHWND(), TB_GETTOOLTIPS, 0, 0);
802 if ( toolTipWnd != hdr->hwndFrom )
803 return FALSE;
804
805 LPTOOLTIPTEXT ttText = (LPTOOLTIPTEXT)lParam;
806 int id = (int)ttText->hdr.idFrom;
807
808 wxToolBarToolBase *tool = FindById(id);
809 if ( !tool )
810 return FALSE;
811
812 const wxString& help = tool->GetShortHelp();
813
814 if ( !help.IsEmpty() )
815 {
816 if ( code == TTN_NEEDTEXTA )
817 {
818 ttText->lpszText = (wxChar *)help.c_str();
819 }
820 else
821 {
822 #if wxUSE_UNICODE
823 ttText->lpszText = (wxChar *)help.c_str();
824 #else
825 // VZ: I don't know why it happens, but the versions of
826 // comctl32.dll starting from 4.70 sometimes send TTN_NEEDTEXTW
827 // even to ANSI programs (normally, this message is supposed
828 // to be sent to Unicode programs only) - hence we need to
829 // handle it as well, otherwise no tooltips will be shown in
830 // this case
831
832 size_t lenAnsi = help.Len();
833 #if defined( __MWERKS__ ) || defined( __CYGWIN__ )
834 // MetroWerks doesn't like calling mbstowcs with NULL argument
835 // neither Cygwin does
836 size_t lenUnicode = 2*lenAnsi;
837 #else
838 size_t lenUnicode = mbstowcs(NULL, help, lenAnsi);
839 #endif
840
841 // using the pointer of right type avoids us doing all sorts of
842 // pointer arithmetics ourselves
843 wchar_t *dst = (wchar_t *)ttText->szText,
844 *pwz = new wchar_t[lenUnicode + 1];
845 mbstowcs(pwz, help, lenAnsi + 1);
846 memcpy(dst, pwz, lenUnicode*sizeof(wchar_t));
847
848 // put the terminating _wide_ NUL
849 dst[lenUnicode] = 0;
850
851 delete [] pwz;
852 #endif
853 }
854 }
855
856 return TRUE;
857 }
858
859 // ----------------------------------------------------------------------------
860 // toolbar geometry
861 // ----------------------------------------------------------------------------
862
863 void wxToolBar::SetToolBitmapSize(const wxSize& size)
864 {
865 wxToolBarBase::SetToolBitmapSize(size);
866
867 ::SendMessage(GetHwnd(), TB_SETBITMAPSIZE, 0, MAKELONG(size.x, size.y));
868 }
869
870 void wxToolBar::SetRows(int nRows)
871 {
872 if ( nRows == m_maxRows )
873 {
874 // avoid resizing the frame uselessly
875 return;
876 }
877
878 // TRUE in wParam means to create at least as many rows, FALSE -
879 // at most as many
880 RECT rect;
881 ::SendMessage(GetHwnd(), TB_SETROWS,
882 MAKEWPARAM(nRows, !(GetWindowStyle() & wxTB_VERTICAL)),
883 (LPARAM) &rect);
884
885 m_maxRows = nRows;
886
887 UpdateSize();
888 }
889
890 // The button size is bigger than the bitmap size
891 wxSize wxToolBar::GetToolSize() const
892 {
893 // TB_GETBUTTONSIZE is supported from version 4.70
894 #if defined(_WIN32_IE) && (_WIN32_IE >= 0x300 ) \
895 && !( defined(__GNUWIN32__) && !wxCHECK_W32API_VERSION( 1, 0 ) )
896 if ( wxTheApp->GetComCtl32Version() >= 470 )
897 {
898 DWORD dw = ::SendMessage(GetHwnd(), TB_GETBUTTONSIZE, 0, 0);
899
900 return wxSize(LOWORD(dw), HIWORD(dw));
901 }
902 else
903 #endif // comctl32.dll 4.70+
904 {
905 // defaults
906 return wxSize(m_defaultWidth + 8, m_defaultHeight + 7);
907 }
908 }
909
910 static
911 wxToolBarToolBase *GetItemSkippingDummySpacers(const wxToolBarToolsList& tools,
912 size_t index )
913 {
914 wxToolBarToolsList::Node* current = tools.GetFirst();
915
916 for ( ; current != 0; current = current->GetNext() )
917 {
918 if ( index == 0 )
919 return current->GetData();
920
921 wxToolBarTool *tool = (wxToolBarTool *)current->GetData();
922 size_t separators = tool->GetSeparatorsCount();
923
924 // if it is a normal button, sepcount == 0, so skip 1 item (the button)
925 // otherwise, skip as many items as the separator count, plus the
926 // control itself
927 index -= separators ? separators + 1 : 1;
928 }
929
930 return 0;
931 }
932
933 wxToolBarToolBase *wxToolBar::FindToolForPosition(wxCoord x, wxCoord y) const
934 {
935 POINT pt;
936 pt.x = x;
937 pt.y = y;
938 int index = (int)::SendMessage(GetHwnd(), TB_HITTEST, 0, (LPARAM)&pt);
939 // MBN: when the point ( x, y ) is close to the toolbar border
940 // TB_HITTEST returns m_nButtons ( not -1 )
941 if ( index < 0 || (size_t)index >= m_nButtons )
942 {
943 // it's a separator or there is no tool at all there
944 return (wxToolBarToolBase *)NULL;
945 }
946
947 // if comctl32 version < 4.71 wxToolBar95 adds dummy spacers
948 #if defined(_WIN32_IE) && (_WIN32_IE >= 0x400 )
949 if ( wxTheApp->GetComCtl32Version() >= 471 )
950 {
951 return m_tools.Item((size_t)index)->GetData();
952 }
953 else
954 #endif
955 {
956 return GetItemSkippingDummySpacers( m_tools, (size_t) index );
957 }
958 }
959
960 void wxToolBar::UpdateSize()
961 {
962 // the toolbar size changed
963 SendMessage(GetHwnd(), TB_AUTOSIZE, 0, 0);
964
965 // we must also refresh the frame after the toolbar size (possibly) changed
966 wxFrame *frame = wxDynamicCast(GetParent(), wxFrame);
967 if ( frame )
968 {
969 frame->SendSizeEvent();
970 }
971 }
972
973 // ----------------------------------------------------------------------------
974 // tool state
975 // ----------------------------------------------------------------------------
976
977 void wxToolBar::DoEnableTool(wxToolBarToolBase *tool, bool enable)
978 {
979 ::SendMessage(GetHwnd(), TB_ENABLEBUTTON,
980 (WPARAM)tool->GetId(), (LPARAM)MAKELONG(enable, 0));
981 }
982
983 void wxToolBar::DoToggleTool(wxToolBarToolBase *tool, bool toggle)
984 {
985 ::SendMessage(GetHwnd(), TB_CHECKBUTTON,
986 (WPARAM)tool->GetId(), (LPARAM)MAKELONG(toggle, 0));
987 }
988
989 void wxToolBar::DoSetToggle(wxToolBarToolBase *WXUNUSED(tool), bool WXUNUSED(toggle))
990 {
991 // VZ: AFAIK, the button has to be created either with TBSTYLE_CHECK or
992 // without, so we really need to delete the button and recreate it here
993 wxFAIL_MSG( _T("not implemented") );
994 }
995
996 // ----------------------------------------------------------------------------
997 // event handlers
998 // ----------------------------------------------------------------------------
999
1000 // Responds to colour changes, and passes event on to children.
1001 void wxToolBar::OnSysColourChanged(wxSysColourChangedEvent& event)
1002 {
1003 wxRGBToColour(m_backgroundColour, ::GetSysColor(COLOR_BTNFACE));
1004
1005 // Remap the buttons
1006 Realize();
1007
1008 // Relayout the toolbar
1009 int nrows = m_maxRows;
1010 m_maxRows = 0; // otherwise SetRows() wouldn't do anything
1011 SetRows(nrows);
1012
1013 Refresh();
1014
1015 // let the event propagate further
1016 event.Skip();
1017 }
1018
1019 void wxToolBar::OnMouseEvent(wxMouseEvent& event)
1020 {
1021 if (event.RightDown())
1022 {
1023 // For now, we don't have an id. Later we could
1024 // try finding the tool.
1025 OnRightClick((int)-1, event.GetX(), event.GetY());
1026 }
1027 else
1028 {
1029 event.Skip();
1030 }
1031 }
1032
1033 long wxToolBar::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
1034 {
1035 if ( nMsg == WM_SIZE )
1036 {
1037 // calculate our minor dimenstion ourselves - we're confusing the
1038 // standard logic (TB_AUTOSIZE) with our horizontal toolbars and other
1039 // hacks
1040 RECT r;
1041 if ( ::SendMessage(GetHwnd(), TB_GETITEMRECT, 0, (LPARAM)&r) )
1042 {
1043 int w, h;
1044
1045 if ( GetWindowStyle() & wxTB_VERTICAL )
1046 {
1047 w = r.right - r.left;
1048 if ( m_maxRows )
1049 {
1050 w *= (m_nButtons + m_maxRows - 1)/m_maxRows;
1051 }
1052 h = HIWORD(lParam);
1053 }
1054 else
1055 {
1056 w = LOWORD(lParam);
1057 h = r.bottom - r.top;
1058 if ( m_maxRows )
1059 {
1060 h += 6; // FIXME: this is the separator line height...
1061 h *= m_maxRows;
1062 }
1063 }
1064
1065 if ( MAKELPARAM(w, h) != lParam )
1066 {
1067 // size really changed
1068 SetSize(w, h);
1069 }
1070
1071 // message processed
1072 return 0;
1073 }
1074 }
1075 else if ( nMsg == WM_MOUSEMOVE )
1076 {
1077 wxCoord x = GET_X_LPARAM(lParam), y = GET_Y_LPARAM(lParam);
1078 wxToolBarToolBase* tool = FindToolForPosition( x, y );
1079
1080 // cursor left current tool
1081 if( tool != m_pInTool && !tool )
1082 {
1083 m_pInTool = 0;
1084 OnMouseEnter( -1 );
1085 }
1086
1087 // cursor entered a tool
1088 if( tool != m_pInTool && tool )
1089 {
1090 m_pInTool = tool;
1091 OnMouseEnter( tool->GetId() );
1092 }
1093
1094 // we don't handle mouse moves, so fall through
1095 // to wxControl::MSWWindowProc
1096 }
1097
1098 return wxControl::MSWWindowProc(nMsg, wParam, lParam);
1099 }
1100
1101 // ----------------------------------------------------------------------------
1102 // private functions
1103 // ----------------------------------------------------------------------------
1104
1105 WXHBITMAP wxToolBar::MapBitmap(WXHBITMAP bitmap, int width, int height)
1106 {
1107 // number of the colours we map: if you change this, update
1108 // wxBITMAP_STD_COLOURS in the resources as well: it must have the same number
1109 // of pixels
1110 static const size_t NUM_OF_MAPPED_COLOURS = 4;
1111
1112 static bool s_coloursInit = FALSE;
1113 long s_stdColours[NUM_OF_MAPPED_COLOURS];
1114
1115 if (!s_coloursInit)
1116 {
1117 // When a bitmap is loaded, the RGB values can change (apparently
1118 // because Windows adjusts them to care for the old programs always
1119 // using 0xc0c0c0 while the transparent colour for the new Windows
1120 // versions is different). But we do this adjustment ourselves so we
1121 // want to avoid Windows' "help" and for this we need to have a
1122 // reference bitmap which can tell us what the RGB values change to.
1123 wxBitmap stdColourBitmap(_T("wxBITMAP_STD_COLOURS"));
1124 if (stdColourBitmap.Ok())
1125 {
1126 wxMemoryDC memDC;
1127 memDC.SelectObject(stdColourBitmap);
1128
1129 wxColour colour;
1130 for ( size_t i = 0; i < WXSIZEOF(s_stdColours); i++ )
1131 {
1132 memDC.GetPixel(i, 0, &colour);
1133 s_stdColours[i] = wxColourToRGB(colour);
1134 }
1135 }
1136 else
1137 {
1138 s_stdColours[0] = RGB(000,000,000) ;
1139 s_stdColours[1] = RGB(128,128,128) ;
1140 s_stdColours[2] = RGB(192,192,192) ;
1141 s_stdColours[3] = RGB(255,255,255) ;
1142 //s_stdColours[4] = RGB(000,000,255) ;
1143 //s_stdColours[5] = RGB(255,000,255) ;
1144 }
1145
1146 s_coloursInit = TRUE;
1147 }
1148
1149 COLORMAP ColorMap[NUM_OF_MAPPED_COLOURS];
1150
1151 // black (0, 0 0)
1152 ColorMap[0].from = s_stdColours[0];
1153 ColorMap[0].to = COLOR_BTNTEXT;
1154 // dark grey (128, 128, 128)
1155 ColorMap[1].from = s_stdColours[1];
1156 ColorMap[1].to = COLOR_BTNSHADOW;
1157 // bright grey (192, 192, 192)
1158 ColorMap[2].from = s_stdColours[2];
1159 ColorMap[2].to = COLOR_BTNFACE;
1160 // white (255, 255, 255)
1161 ColorMap[3].from = s_stdColours[3];
1162 ColorMap[3].to = COLOR_BTNHIGHLIGHT;
1163 // blue (0, 0, 255)
1164 // ColorMap[4].from = s_stdColours[4];
1165 // ColorMap[4].to = COLOR_HIGHLIGHT;
1166 // magenta (255, 0, 255)
1167 // ColorMap[4].from = s_stdColours[5];
1168 // ColorMap[4].to = COLOR_WINDOW;
1169
1170 for ( size_t n = 0; n < WXSIZEOF(ColorMap); n++ )
1171 {
1172 ColorMap[n].to = ::GetSysColor(ColorMap[n].to);
1173 }
1174
1175 MemoryHDC hdcMem;
1176
1177 if ( !hdcMem )
1178 {
1179 wxLogLastError(_T("CreateCompatibleDC"));
1180
1181 return bitmap;
1182 }
1183
1184 SelectInHDC bmpInHDC(hdcMem, (HBITMAP)bitmap);
1185
1186 if ( !bmpInHDC )
1187 {
1188 wxLogLastError(_T("SelectObject"));
1189
1190 return bitmap;
1191 }
1192
1193 // VZ: I leave here my attempts to map the bitmap to the system colours
1194 // faster by using BitBlt() even though it's broken currently - but
1195 // maybe someone else can finish it? It should be faster than iterating
1196 // over all pixels...
1197 #if 1
1198 for ( int i = 0; i < width; i++ )
1199 {
1200 for ( int j = 0; j < height; j++ )
1201 {
1202 COLORREF pixel = ::GetPixel(hdcMem, i, j);
1203
1204 for ( size_t k = 0; k < WXSIZEOF(ColorMap); k++ )
1205 {
1206 COLORREF col = ColorMap[k].from;
1207 if ( abs(GetRValue(pixel) - GetRValue(col)) < 10 &&
1208 abs(GetGValue(pixel) - GetGValue(col)) < 10 &&
1209 abs(GetBValue(pixel) - GetBValue(col)) < 10 )
1210 {
1211 ::SetPixel(hdcMem, i, j, ColorMap[k].to);
1212 break;
1213 }
1214 }
1215 }
1216 }
1217
1218 return bitmap;
1219 #else // 1
1220 MemoryHDC hdcMask, hdcDst;
1221 if ( !hdcMask || !hdcDst )
1222 {
1223 wxLogLastError(_T("CreateCompatibleDC"));
1224
1225 return bitmap;
1226 }
1227
1228 // create the target bitmap
1229 HBITMAP hbmpDst = ::CreateCompatibleBitmap(hdcDst, width, height);
1230 if ( !hbmpDst )
1231 {
1232 wxLogLastError(_T("CreateCompatibleBitmap"));
1233
1234 return bitmap;
1235 }
1236
1237 // create the monochrome mask bitmap
1238 HBITMAP hbmpMask = ::CreateBitmap(width, height, 1, 1, 0);
1239 if ( !hbmpMask )
1240 {
1241 wxLogLastError(_T("CreateBitmap(mono)"));
1242
1243 ::DeleteObject(hbmpDst);
1244
1245 return bitmap;
1246 }
1247
1248 SelectInHDC bmpInDst(hdcDst, hbmpDst),
1249 bmpInMask(hdcMask, hbmpMask);
1250
1251 // for each colour:
1252 for ( n = 0; n < NUM_OF_MAPPED_COLOURS; n++ )
1253 {
1254 // create the mask for this colour
1255 ::SetBkColor(hdcMem, ColorMap[n].from);
1256 ::BitBlt(hdcMask, 0, 0, width, height, hdcMem, 0, 0, SRCCOPY);
1257
1258 // replace this colour with the target one in the dst bitmap
1259 HBRUSH hbr = ::CreateSolidBrush(ColorMap[n].to);
1260 HGDIOBJ hbrOld = ::SelectObject(hdcDst, hbr);
1261
1262 ::MaskBlt(hdcDst, 0, 0, width, height,
1263 hdcMem, 0, 0,
1264 hbmpMask, 0, 0,
1265 MAKEROP4(PATCOPY, SRCCOPY));
1266
1267 (void)::SelectObject(hdcDst, hbrOld);
1268 ::DeleteObject(hbr);
1269 }
1270
1271 ::DeleteObject((HBITMAP)bitmap);
1272
1273 return (WXHBITMAP)hbmpDst;
1274 #endif // 0/1
1275 }
1276
1277 #endif // wxUSE_TOOLBAR && Win95
1278