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