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