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