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