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