]> git.saurik.com Git - wxWidgets.git/blob - src/msw/tbar95.cpp
better learn the operators...
[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 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 && !( defined(__GNUWIN32__) && !wxCHECK_W32API_VERSION( 1, 0 ) )
858 if ( wxTheApp->GetComCtl32Version() >= 470 )
859 {
860 DWORD dw = ::SendMessage(GetHwnd(), TB_GETBUTTONSIZE, 0, 0);
861
862 return wxSize(LOWORD(dw), HIWORD(dw));
863 }
864 else
865 #endif // comctl32.dll 4.70+
866 {
867 // defaults
868 return wxSize(m_defaultWidth + 8, m_defaultHeight + 7);
869 }
870 }
871
872 static wxToolBarToolBase *GetItemSkippingDummySpacers( const wxToolBarToolsList& tools, size_t index )
873 {
874 wxToolBarToolsList::Node* current = tools.GetFirst();
875
876 for( ; current != 0; current = current->GetNext() )
877 {
878 if( index == 0 )
879 return current->GetData();
880 size_t separators = ((wxToolBarTool*)current->GetData())->GetSeparatorsCount();
881 // if it is a normal button, sepcount == 0, so skip 1
882 // item ( the button )
883 // otherwise, skip as many items as the separator count,
884 // plus the control itself
885 index -= separators ? separators + 1: 1;
886 }
887
888 return 0;
889 }
890
891 wxToolBarToolBase *wxToolBar::FindToolForPosition(wxCoord x, wxCoord y) const
892 {
893 POINT pt;
894 pt.x = x;
895 pt.y = y;
896 int index = (int)::SendMessage(GetHwnd(), TB_HITTEST, 0, (LPARAM)&pt);
897 if ( index < 0 )
898 {
899 // it's a separator or there is no tool at all there
900 return (wxToolBarToolBase *)NULL;
901 }
902
903 // if comctl32 version < 4.71
904 // wxToolBar95 adds dummy spacers
905 #if defined(_WIN32_IE) && (_WIN32_IE >= 0x400 )
906 if ( wxTheApp->GetComCtl32Version() >= 471 )
907 {
908 return m_tools.Item((size_t)index)->GetData();
909 }
910 else
911 {
912 return GetItemSkippingDummySpacers( m_tools, (size_t) index );
913 }
914 #else
915 return GetItemSkippingDummySpacers( m_tools, (size_t) index );
916 #endif
917 }
918
919 void wxToolBar::UpdateSize()
920 {
921 // the toolbar size changed
922 SendMessage(GetHwnd(), TB_AUTOSIZE, 0, 0);
923
924 // we must also refresh the frame after the toolbar size (possibly) changed
925 wxFrame *frame = wxDynamicCast(GetParent(), wxFrame);
926 if ( frame )
927 {
928 frame->SendSizeEvent();
929 }
930 }
931
932 // ----------------------------------------------------------------------------
933 // tool state
934 // ----------------------------------------------------------------------------
935
936 void wxToolBar::DoEnableTool(wxToolBarToolBase *tool, bool enable)
937 {
938 ::SendMessage(GetHwnd(), TB_ENABLEBUTTON,
939 (WPARAM)tool->GetId(), (LPARAM)MAKELONG(enable, 0));
940 }
941
942 void wxToolBar::DoToggleTool(wxToolBarToolBase *tool, bool toggle)
943 {
944 ::SendMessage(GetHwnd(), TB_CHECKBUTTON,
945 (WPARAM)tool->GetId(), (LPARAM)MAKELONG(toggle, 0));
946 }
947
948 void wxToolBar::DoSetToggle(wxToolBarToolBase *WXUNUSED(tool), bool WXUNUSED(toggle))
949 {
950 // VZ: AFAIK, the button has to be created either with TBSTYLE_CHECK or
951 // without, so we really need to delete the button and recreate it here
952 wxFAIL_MSG( _T("not implemented") );
953 }
954
955 // ----------------------------------------------------------------------------
956 // event handlers
957 // ----------------------------------------------------------------------------
958
959 // Responds to colour changes, and passes event on to children.
960 void wxToolBar::OnSysColourChanged(wxSysColourChangedEvent& event)
961 {
962 wxRGBToColour(m_backgroundColour, ::GetSysColor(COLOR_BTNFACE));
963
964 // Remap the buttons
965 Realize();
966
967 // Relayout the toolbar
968 int nrows = m_maxRows;
969 m_maxRows = 0; // otherwise SetRows() wouldn't do anything
970 SetRows(nrows);
971
972 Refresh();
973
974 // let the event propagate further
975 event.Skip();
976 }
977
978 void wxToolBar::OnMouseEvent(wxMouseEvent& event)
979 {
980 if (event.RightDown())
981 {
982 // For now, we don't have an id. Later we could
983 // try finding the tool.
984 OnRightClick((int)-1, event.GetX(), event.GetY());
985 }
986 else
987 {
988 event.Skip();
989 }
990 }
991
992 long wxToolBar::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
993 {
994 if ( nMsg == WM_SIZE )
995 {
996 // calculate our minor dimenstion ourselves - we're confusing the
997 // standard logic (TB_AUTOSIZE) with our horizontal toolbars and other
998 // hacks
999 RECT r;
1000 if ( ::SendMessage(GetHwnd(), TB_GETITEMRECT, 0, (LPARAM)&r) )
1001 {
1002 int w, h;
1003
1004 if ( GetWindowStyle() & wxTB_VERTICAL )
1005 {
1006 w = r.right - r.left;
1007 if ( m_maxRows )
1008 {
1009 w *= (m_nButtons + m_maxRows - 1)/m_maxRows;
1010 }
1011 h = HIWORD(lParam);
1012 }
1013 else
1014 {
1015 w = LOWORD(lParam);
1016 h = r.bottom - r.top;
1017 if ( m_maxRows )
1018 {
1019 h += 6; // FIXME: this is the separator line height...
1020 h *= m_maxRows;
1021 }
1022 }
1023
1024 if ( MAKELPARAM(w, h) != lParam )
1025 {
1026 // size really changed
1027 SetSize(w, h);
1028 }
1029
1030 // message processed
1031 return 0;
1032 }
1033 }
1034 else if ( nMsg == WM_MOUSEMOVE )
1035 {
1036 wxCoord x = GET_X_LPARAM(lParam), y = GET_Y_LPARAM(lParam);
1037 wxToolBarToolBase* tool = FindToolForPosition( x, y );
1038
1039 // cursor left current tool
1040 if( tool != m_pInTool && !tool )
1041 {
1042 m_pInTool = 0;
1043 OnMouseEnter( -1 );
1044 }
1045
1046 // cursor entered a tool
1047 if( tool != m_pInTool && tool )
1048 {
1049 m_pInTool = tool;
1050 OnMouseEnter( tool->GetId() );
1051 }
1052
1053 // we don't handle mouse moves, so fall through
1054 // to wxControl::MSWWindowProc
1055 }
1056
1057 return wxControl::MSWWindowProc(nMsg, wParam, lParam);
1058 }
1059
1060 // ----------------------------------------------------------------------------
1061 // private functions
1062 // ----------------------------------------------------------------------------
1063
1064 bool wxToolBar::sm_coloursInit = FALSE;
1065 long wxToolBar::sm_stdColours[6];
1066
1067 void wxToolBar::MapBitmap(WXHBITMAP bitmap, int width, int height)
1068 {
1069 if (!sm_coloursInit)
1070 {
1071 // When a bitmap is loaded, the RGB values can change. So we need to have a
1072 // reference bitmap which can tell us what the RGB values change to.
1073 wxBitmap stdColourBitmap("wxBITMAP_STD_COLOURS", wxBITMAP_TYPE_RESOURCE);
1074 if (stdColourBitmap.Ok())
1075 {
1076 wxMemoryDC memDC;
1077 memDC.SelectObject(stdColourBitmap);
1078
1079 int i = 0;
1080 wxColour colour;
1081 for (i = 0; i < 6; i++)
1082 {
1083 memDC.GetPixel(i, 0, & colour);
1084 sm_stdColours[i] = RGB(colour.Red(), colour.Green(), colour.Blue());
1085 }
1086 sm_coloursInit = TRUE;
1087 memDC.SelectObject(wxNullBitmap);
1088 }
1089 else
1090 {
1091 sm_stdColours[0] = RGB(000,000,000) ;
1092 sm_stdColours[1] = RGB(128,128,128) ;
1093 sm_stdColours[2] = RGB(192,192,192) ;
1094 sm_stdColours[3] = RGB(255,255,255) ;
1095 sm_stdColours[4] = RGB(000,000,255) ;
1096 sm_stdColours[5] = RGB(255,000,255) ;
1097 sm_coloursInit = TRUE;
1098 }
1099 }
1100
1101 HBITMAP hBitmap = (HBITMAP) bitmap;
1102
1103 COLORMAP ColorMap[5];
1104
1105 ColorMap[0].from = sm_stdColours[0]; ColorMap[0].to = COLOR_BTNTEXT; // black (0, 0 0)
1106 ColorMap[1].from = sm_stdColours[1]; ColorMap[1].to = COLOR_BTNSHADOW; // dark grey (128, 128, 128)
1107 ColorMap[2].from = sm_stdColours[2]; ColorMap[2].to = COLOR_BTNFACE; // bright grey (192, 192, 192)
1108 ColorMap[3].from = sm_stdColours[3]; ColorMap[3].to = COLOR_BTNHIGHLIGHT; // white (255, 255, 255)
1109 // ColorMap[4].from = sm_stdColours[4]; ColorMap[4].to = COLOR_HIGHLIGHT; // blue (0, 0, 255)
1110 ColorMap[4].from = sm_stdColours[5]; ColorMap[4].to = COLOR_WINDOW; // magenta (255, 0, 255)
1111
1112 for ( size_t n = 0; n < WXSIZEOF(ColorMap); n++)
1113 {
1114 ColorMap[n].to = ::GetSysColor(ColorMap[n].to);
1115 }
1116
1117 HBITMAP hbmOld;
1118 HDC hdcMem = CreateCompatibleDC(NULL);
1119
1120 if (hdcMem)
1121 {
1122 hbmOld = (HBITMAP) SelectObject(hdcMem, hBitmap);
1123
1124 for ( int i = 0; i < width; i++ )
1125 {
1126 for ( int j = 0; j < height; j++ )
1127 {
1128 COLORREF pixel = ::GetPixel(hdcMem, i, j);
1129
1130 for ( size_t k = 0; k < WXSIZEOF(ColorMap); k++ )
1131 {
1132 int distance = abs( GetRValue( pixel ) - GetRValue( ColorMap[k].from )) ;
1133 distance = max( distance , abs(GetGValue(pixel ) - GetGValue( ColorMap[k].from ))) ;
1134 distance = max( distance , abs(GetBValue(pixel ) - GetBValue( ColorMap[k].from ))) ;
1135 if ( distance < 0x10 )
1136 {
1137 ::SetPixel(hdcMem, i, j, ColorMap[k].to);
1138 break;
1139 }
1140 }
1141 }
1142 }
1143
1144
1145 SelectObject(hdcMem, hbmOld);
1146 DeleteObject(hdcMem);
1147 }
1148 }
1149
1150 // Some experiments...
1151 #if 0
1152 // What we want to do is create another bitmap which has a depth of 4,
1153 // and set the bits. So probably we want to convert this HBITMAP into a
1154 // DIB, then call SetDIBits.
1155 // AAAGH. The stupid thing is that if newBitmap has a depth of 4 (less than that of
1156 // the screen), then SetDIBits fails.
1157 HBITMAP newBitmap = ::CreateBitmap(totalBitmapWidth, totalBitmapHeight, 1, 4, NULL);
1158 HANDLE newDIB = ::BitmapToDIB((HBITMAP) m_hBitmap, NULL);
1159 LPBITMAPINFOHEADER lpbmi = (LPBITMAPINFOHEADER) GlobalLock(newDIB);
1160
1161 dc = ::GetDC(NULL);
1162 // LPBITMAPINFOHEADER lpbmi = (LPBITMAPINFOHEADER) newDIB;
1163
1164 int result = ::SetDIBits(dc, newBitmap, 0, lpbmi->biHeight, FindDIBBits((LPSTR)lpbmi), (LPBITMAPINFO)lpbmi,
1165 DIB_PAL_COLORS);
1166 DWORD err = GetLastError();
1167
1168 ::ReleaseDC(NULL, dc);
1169
1170 // Delete the DIB
1171 GlobalUnlock (newDIB);
1172 GlobalFree (newDIB);
1173
1174 // WXHBITMAP hBitmap2 = wxCreateMappedBitmap((WXHINSTANCE) wxGetInstance(), (WXHBITMAP) m_hBitmap);
1175 // Substitute our new bitmap for the old one
1176 ::DeleteObject((HBITMAP) m_hBitmap);
1177 m_hBitmap = (WXHBITMAP) newBitmap;
1178 #endif
1179
1180
1181 #endif // wxUSE_TOOLBAR && Win95
1182