]> git.saurik.com Git - wxWidgets.git/blame - src/motif/toolbar.cpp
1. DoSetSize() simplified, DoGetBestSize() introduced
[wxWidgets.git] / src / motif / toolbar.cpp
CommitLineData
4bb6408c
JS
1/////////////////////////////////////////////////////////////////////////////
2// Name: toolbar.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
bf6c2b35 9// Licence: wxWindows licence
4bb6408c
JS
10/////////////////////////////////////////////////////////////////////////////
11
12#ifdef __GNUG__
13#pragma implementation "toolbar.h"
14#endif
15
16#include "wx/wx.h"
1a3ac83f
JS
17#include "wx/app.h"
18#include "wx/timer.h"
1ccbb61a 19#include "wx/toolbar.h"
0d57be45
JS
20
21#include <Xm/Xm.h>
22#include <Xm/PushBG.h>
23#include <Xm/PushB.h>
1a3ac83f 24#include <Xm/Label.h>
0d57be45
JS
25#include <Xm/ToggleB.h>
26#include <Xm/ToggleBG.h>
27#include <Xm/Form.h>
28
29#include "wx/motif/private.h"
4bb6408c
JS
30
31#if !USE_SHARED_LIBRARY
32IMPLEMENT_DYNAMIC_CLASS(wxToolBar, wxToolBarBase)
33
34BEGIN_EVENT_TABLE(wxToolBar, wxToolBarBase)
35END_EVENT_TABLE()
36#endif
37
1a3ac83f 38static void wxToolButtonCallback (Widget w, XtPointer clientData,
bf6c2b35 39 XtPointer ptr);
1a3ac83f
JS
40static void wxToolButtonPopupCallback (Widget w, XtPointer client_data,
41 XEvent *event, Boolean *continue_to_dispatch);
42
1a3ac83f
JS
43class wxToolBarTimer: public wxTimer
44{
45public:
46 wxToolBarTimer() { }
47 virtual void Notify();
48
49 static Widget help_popup;
50 static Widget buttonWidget;
51 static wxString helpString;
52};
53
54static wxToolBarTimer* wxTheToolBarTimer = (wxToolBarTimer*) NULL;
55
56Widget wxToolBarTimer::help_popup = (Widget) 0;
57Widget wxToolBarTimer::buttonWidget = (Widget) 0;
58wxString wxToolBarTimer::helpString = "";
59
0d57be45
JS
60wxToolBar::wxToolBar():
61 m_widgets(wxKEY_INTEGER)
4bb6408c
JS
62{
63 m_maxWidth = -1;
64 m_maxHeight = -1;
65 m_defaultWidth = 24;
66 m_defaultHeight = 22;
4bb6408c
JS
67}
68
69bool wxToolBar::Create(wxWindow *parent, wxWindowID id, const wxPoint& pos, const wxSize& size,
70 long style, const wxString& name)
71{
72 m_maxWidth = -1;
73 m_maxHeight = -1;
bf6c2b35 74
4bb6408c
JS
75 m_defaultWidth = 24;
76 m_defaultHeight = 22;
77 SetName(name);
0d57be45
JS
78 m_backgroundColour = wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE);
79 m_foregroundColour = parent->GetForegroundColour();
4bb6408c
JS
80 m_windowStyle = style;
81
82 SetParent(parent);
83
84 if (parent) parent->AddChild(this);
85
0d57be45
JS
86 Widget parentWidget = (Widget) parent->GetClientWidget();
87
88 Widget toolbar = XtVaCreateManagedWidget("toolbar",
7fe7d506
JS
89 xmBulletinBoardWidgetClass, (Widget) parentWidget,
90 XmNmarginWidth, 0,
91 XmNmarginHeight, 0,
92 XmNresizePolicy, XmRESIZE_NONE,
93 NULL);
94/*
95 Widget toolbar = XtVaCreateManagedWidget("toolbar",
96 xmFormWidgetClass, (Widget) m_clientWidget,
0d57be45
JS
97 XmNtraversalOn, False,
98 XmNhorizontalSpacing, 0,
99 XmNverticalSpacing, 0,
1a3ac83f
JS
100 XmNleftOffset, 0,
101 XmNrightOffset, 0,
102 XmNmarginWidth, 0,
103 XmNmarginHeight, 0,
0d57be45 104 NULL);
7fe7d506 105*/
0d57be45
JS
106
107 m_mainWidget = (WXWidget) toolbar;
108
da175b2c 109 m_font = parent->GetFont();
4b5f3fe6
JS
110 ChangeFont(FALSE);
111
0d57be45
JS
112 SetCanAddEventHandler(TRUE);
113 AttachWidget (parent, m_mainWidget, (WXWidget) NULL, pos.x, pos.y, size.x, size.y);
114
0d57be45 115 ChangeBackgroundColour();
bf6c2b35 116
0d57be45 117 return TRUE;
4bb6408c
JS
118}
119
120wxToolBar::~wxToolBar()
121{
1a3ac83f
JS
122 delete wxTheToolBarTimer;
123 wxTheToolBarTimer = NULL;
124 ClearTools();
125 DestroyPixmaps();
4bb6408c
JS
126}
127
7fe7d506
JS
128bool wxToolBar::CreateTools()
129{
130 if (m_tools.Number() == 0)
131 return FALSE;
132
133 // Separator spacing
134 const int separatorSize = GetToolSeparation(); // 8;
135 wxSize margins = GetToolMargins();
136 int marginX = margins.x;
137 int marginY = margins.y;
138
139 int currentX = marginX;
140 int currentY = marginY;
141
142 int buttonHeight = 0;
143
144 int currentSpacing = 0;
145
146 m_widgets.Clear();
147 Widget prevButton = (Widget) 0;
148 wxNode* node = m_tools.First();
149 while (node)
150 {
151 wxToolBarTool *tool = (wxToolBarTool *)node->Data();
152
153 if (tool->m_toolStyle == wxTOOL_STYLE_SEPARATOR)
154 currentX += separatorSize;
155 else if (tool->m_bitmap1.Ok())
156 {
157 Widget button = (Widget) 0;
158
159 if (tool->m_isToggle)
160 {
bf6c2b35 161 button = XtVaCreateWidget("toggleButton",
7fe7d506
JS
162 xmToggleButtonWidgetClass, (Widget) m_mainWidget,
163 XmNx, currentX, XmNy, currentY,
bf6c2b35 164 // XmNpushButtonEnabled, True,
7fe7d506
JS
165 XmNmultiClick, XmMULTICLICK_KEEP,
166 XmNlabelType, XmPIXMAP,
167 NULL);
168 XtAddCallback ((Widget) button, XmNvalueChangedCallback, (XtCallbackProc) wxToolButtonCallback,
bf6c2b35 169 (XtPointer) this);
a91b47e8
JS
170
171 XtVaSetValues ((Widget) button,
172 XmNselectColor, m_backgroundColour.AllocColour(XtDisplay((Widget) button)),
173 NULL);
7fe7d506
JS
174 }
175 else
176 {
bf6c2b35 177 button = XtVaCreateWidget("button",
7fe7d506
JS
178 xmPushButtonWidgetClass, (Widget) m_mainWidget,
179 XmNx, currentX, XmNy, currentY,
180 XmNpushButtonEnabled, True,
181 XmNmultiClick, XmMULTICLICK_KEEP,
182 XmNlabelType, XmPIXMAP,
183 NULL);
184 XtAddCallback (button,
185 XmNactivateCallback, (XtCallbackProc) wxToolButtonCallback,
186 (XtPointer) this);
187 }
188
94b49b93
JS
189 DoChangeBackgroundColour((WXWidget) button, m_backgroundColour, TRUE);
190
7fe7d506
JS
191 // For each button, if there is a mask, we must create
192 // a new wxBitmap that has the correct background colour
193 // for the button. Otherwise the background will just be
194 // e.g. black if a transparent XPM has been loaded.
195 wxBitmap originalBitmap = tool->m_bitmap1;
196
197 if (tool->m_bitmap1.GetMask())
198 {
199 int backgroundPixel;
200 XtVaGetValues(button, XmNbackground, &backgroundPixel,
bf6c2b35 201 NULL);
7fe7d506
JS
202
203
204 wxColour col;
205 col.SetPixel(backgroundPixel);
bf6c2b35 206
7fe7d506
JS
207 wxBitmap newBitmap = wxCreateMaskedBitmap(tool->m_bitmap1, col);
208
209 tool->m_bitmap1 = newBitmap;
210 }
211
212 // Create a selected/toggled bitmap. If there isn't a m_bitmap2,
213 // we need to create it (with a darker, selected background)
214 int backgroundPixel;
215 if (tool->m_isToggle)
216 XtVaGetValues(button, XmNselectColor, &backgroundPixel,
bf6c2b35 217 NULL);
7fe7d506
JS
218 else
219 XtVaGetValues(button, XmNarmColor, &backgroundPixel,
bf6c2b35 220 NULL);
7fe7d506
JS
221
222 wxColour col;
223 col.SetPixel(backgroundPixel);
224
225 if (tool->m_bitmap2.Ok() && tool->m_bitmap2.GetMask())
226 {
227 // Use what's there
228 wxBitmap newBitmap = wxCreateMaskedBitmap(tool->m_bitmap2, col);
229 tool->m_bitmap2 = newBitmap;
230 }
231 else
232 {
233 // Use unselected bitmap
234 if (originalBitmap.GetMask())
235 {
236 wxBitmap newBitmap = wxCreateMaskedBitmap(originalBitmap, col);
237 tool->m_bitmap2 = newBitmap;
bf6c2b35 238 }
7fe7d506
JS
239 else
240 tool->m_bitmap2 = tool->m_bitmap1;
241 }
242
243 Pixmap pixmap = (Pixmap) tool->m_bitmap1.GetPixmap();
244 Pixmap insensPixmap = (Pixmap) tool->m_bitmap1.GetInsensPixmap();
245
246 if (tool->m_isToggle)
247 {
248 // Toggle button
249 Pixmap pixmap2 = (Pixmap) 0;
250 Pixmap insensPixmap2 = (Pixmap) 0;
251
252 // If there's a bitmap for the toggled state, use it,
253 // otherwise generate one.
254 if (tool->m_bitmap2.Ok())
255 {
256 pixmap2 = (Pixmap) tool->m_bitmap2.GetPixmap();
257 insensPixmap2 = (Pixmap) tool->m_bitmap2.GetInsensPixmap();
258 }
259 else
260 {
261 pixmap2 = (Pixmap) tool->m_bitmap1.GetArmPixmap(button);
262 insensPixmap2 = XCreateInsensitivePixmap((Display*) wxGetDisplay(), pixmap2);
263 m_pixmaps.Append((wxObject*) insensPixmap2); // Store for later deletion
264 }
265 XtVaSetValues (button,
266 XmNindicatorOn, False,
bf6c2b35
VZ
267 XmNshadowThickness, 2,
268 // XmNborderWidth, 0,
269 // XmNspacing, 0,
270 XmNmarginWidth, 0,
271 XmNmarginHeight, 0,
7fe7d506
JS
272 XmNfillOnSelect, True,
273 XmNlabelPixmap, pixmap,
274 XmNselectPixmap, pixmap2,
275 XmNlabelInsensitivePixmap, insensPixmap,
276 XmNselectInsensitivePixmap, insensPixmap2,
277 XmNlabelType, XmPIXMAP,
278 NULL);
279 }
280 else
281 {
282 Pixmap pixmap2 = (Pixmap) 0;
283
284 // If there's a bitmap for the armed state, use it,
285 // otherwise generate one.
286 if (tool->m_bitmap2.Ok())
287 {
288 pixmap2 = (Pixmap) tool->m_bitmap2.GetPixmap();
289 }
290 else
291 {
292 pixmap2 = (Pixmap) tool->m_bitmap1.GetArmPixmap(button);
293
294 }
295 // Normal button
296 XtVaSetValues(button,
297 XmNlabelPixmap, pixmap,
298 XmNlabelInsensitivePixmap, insensPixmap,
299 XmNarmPixmap, pixmap2,
300 NULL);
301 }
302 XtManageChild(button);
303
304 Dimension width, height;
305 XtVaGetValues(button, XmNwidth, & width, XmNheight, & height,
bf6c2b35 306 NULL);
7fe7d506
JS
307 currentX += width + marginX;
308 buttonHeight = wxMax(buttonHeight, height);
309
bf6c2b35 310 XtAddEventHandler (button, EnterWindowMask | LeaveWindowMask,
7fe7d506
JS
311 False, wxToolButtonPopupCallback, (XtPointer) this);
312 m_widgets.Append(tool->m_index, (wxObject*) button);
313
314 prevButton = button;
315 currentSpacing = 0;
316 }
317 node = node->Next();
318 }
319
320 SetSize(-1, -1, currentX, buttonHeight + 2*marginY);
321
322 return TRUE;
323}
324
4bb6408c
JS
325void wxToolBar::SetToolBitmapSize(const wxSize& size)
326{
4b5f3fe6 327 // TODO not necessary?
4bb6408c 328 m_defaultWidth = size.x; m_defaultHeight = size.y;
4bb6408c
JS
329}
330
331wxSize wxToolBar::GetMaxSize() const
332{
4b5f3fe6
JS
333 int w, h;
334 GetSize(& w, & h);
335
336 return wxSize(w, h);
4bb6408c
JS
337}
338
339// The button size is bigger than the bitmap size
340wxSize wxToolBar::GetToolSize() const
341{
4b5f3fe6 342 // TODO not necessary?
4bb6408c
JS
343 return wxSize(m_defaultWidth + 8, m_defaultHeight + 7);
344}
345
346void wxToolBar::EnableTool(int toolIndex, bool enable)
347{
348 wxNode *node = m_tools.Find((long)toolIndex);
349 if (node)
350 {
351 wxToolBarTool *tool = (wxToolBarTool *)node->Data();
352 tool->m_enabled = enable;
1a3ac83f
JS
353
354 WXWidget widget = FindWidgetForIndex(tool->m_index);
355 if (widget == (WXWidget) 0)
356 return;
357
358 XtSetSensitive((Widget) widget, (Boolean) enable);
4bb6408c
JS
359 }
360}
361
362void wxToolBar::ToggleTool(int toolIndex, bool toggle)
363{
364 wxNode *node = m_tools.Find((long)toolIndex);
365 if (node)
366 {
367 wxToolBarTool *tool = (wxToolBarTool *)node->Data();
368 if (tool->m_isToggle)
369 {
370 tool->m_toggleState = toggle;
1a3ac83f
JS
371
372 WXWidget widget = FindWidgetForIndex(tool->m_index);
373 if (widget == (WXWidget) 0)
374 return;
375
376 XmToggleButtonSetState((Widget) widget, (Boolean) toggle, False);
4bb6408c
JS
377 }
378 }
379}
380
381void wxToolBar::ClearTools()
382{
0d57be45
JS
383 wxNode* node = m_widgets.First();
384 while (node)
385 {
386 Widget button = (Widget) node->Data();
387 XtDestroyWidget(button);
388 node = node->Next();
389 }
390 m_widgets.Clear();
1a3ac83f 391 DestroyPixmaps();
0d57be45 392
4bb6408c
JS
393 wxToolBarBase::ClearTools();
394}
395
1a3ac83f
JS
396void wxToolBar::DestroyPixmaps()
397{
398 wxNode* node = m_pixmaps.First();
399 while (node)
400 {
401 Pixmap pixmap = (Pixmap) node->Data();
402 XmDestroyPixmap (DefaultScreenOfDisplay ((Display*) GetXDisplay()), pixmap);
403 node = node->Next();
404 }
405 m_pixmaps.Clear();
406}
407
4bb6408c
JS
408// If pushedBitmap is NULL, a reversed version of bitmap is
409// created and used as the pushed/toggled image.
410// If toggle is TRUE, the button toggles between the two states.
411
412wxToolBarTool *wxToolBar::AddTool(int index, const wxBitmap& bitmap, const wxBitmap& pushedBitmap,
413 bool toggle, long xPos, long yPos, wxObject *clientData, const wxString& helpString1, const wxString& helpString2)
414{
c0ed460c 415 wxToolBarTool *tool = new wxToolBarTool(index, bitmap, wxNullBitmap, toggle, xPos, yPos, helpString1, helpString2);
4bb6408c
JS
416 tool->m_clientData = clientData;
417
418 if (xPos > -1)
419 tool->m_x = xPos;
420 else
421 tool->m_x = m_xMargin;
422
423 if (yPos > -1)
424 tool->m_y = yPos;
425 else
426 tool->m_y = m_yMargin;
427
a91b47e8 428 wxSize size = GetToolSize();
1ccbb61a 429 tool->SetSize(size.x, size.y);
4bb6408c
JS
430
431 m_tools.Append((long)index, tool);
432 return tool;
433}
434
1a3ac83f
JS
435int wxToolBar::FindIndexForWidget(WXWidget w)
436{
437 wxNode* node = m_widgets.First();
438 while (node)
439 {
440 WXWidget widget = (WXWidget) node->Data();
441 if (widget == w)
4fabb575 442 return (int) node->GetKeyInteger();
1a3ac83f
JS
443 node = node->Next();
444 }
445 return -1;
446}
447
448WXWidget wxToolBar::FindWidgetForIndex(int index)
449{
450 wxNode* node = m_widgets.Find((long) index);
451 if (!node)
452 return (WXWidget) 0;
453 else
454 return (WXWidget) node->Data();
455}
456
7fe7d506
JS
457WXWidget wxToolBar::GetTopWidget() const
458{
459 return m_mainWidget;
460}
461
462WXWidget wxToolBar::GetClientWidget() const
463{
464 return m_mainWidget;
465}
466
467WXWidget wxToolBar::GetMainWidget() const
468{
469 return m_mainWidget;
470}
471
472
1a3ac83f 473void wxToolButtonCallback (Widget w, XtPointer clientData,
bf6c2b35 474 XtPointer ptr)
1a3ac83f
JS
475{
476 wxToolBar *toolBar = (wxToolBar *) clientData;
477 int index = toolBar->FindIndexForWidget((WXWidget) w);
478
479 if (index != -1)
480 {
481 wxNode *node = toolBar->GetTools().Find((long)index);
482 if (!node)
483 return;
484 wxToolBarTool *tool = (wxToolBarTool *)node->Data();
485 if (tool->m_isToggle)
a91b47e8 486 tool->m_toggleState = !tool->m_toggleState;
1a3ac83f
JS
487
488 (void) toolBar->OnLeftClick(index, tool->m_toggleState);
489 }
490
491}
492
1a3ac83f
JS
493
494static void wxToolButtonPopupCallback (Widget w, XtPointer client_data,
495 XEvent *event, Boolean *continue_to_dispatch)
496{
497 // TODO: retrieve delay before popping up tooltip from wxSystemSettings.
498 int delayMilli = 800;
499 wxToolBar* toolBar = (wxToolBar*) client_data;
500
501 int index = toolBar->FindIndexForWidget((WXWidget) w);
502
503 if (index != -1)
504 {
505 wxNode *node = toolBar->GetTools().Find((long)index);
506 if (!node)
507 return;
1a3ac83f
JS
508 wxString str(toolBar->GetToolShortHelp(index));
509 if (str.IsNull() || str == "")
510 return;
511
512 if (!wxTheToolBarTimer)
513 wxTheToolBarTimer = new wxToolBarTimer;
514
bf6c2b35
VZ
515 wxToolBarTimer::buttonWidget = w;
516 wxToolBarTimer::helpString = str;
1a3ac83f
JS
517
518
519 /************************************************************/
520 /* Popup help label */
521 /************************************************************/
522 if (event->type == EnterNotify)
523 {
524 if (wxToolBarTimer::help_popup != (Widget) 0)
525 {
526 XtDestroyWidget (wxToolBarTimer::help_popup);
527 XtPopdown (wxToolBarTimer::help_popup);
bf6c2b35 528 }
1a3ac83f
JS
529 wxToolBarTimer::help_popup = (Widget) 0;
530
531 // One shot
532 wxTheToolBarTimer->Start(delayMilli, TRUE);
533
534 }
535 /************************************************************/
536 /* Popdown help label */
537 /************************************************************/
538 else if (event->type == LeaveNotify)
539 {
540 if (wxTheToolBarTimer)
541 wxTheToolBarTimer->Stop();
542 if (wxToolBarTimer::help_popup != (Widget) 0)
543 {
544 XtDestroyWidget (wxToolBarTimer::help_popup);
545 XtPopdown (wxToolBarTimer::help_popup);
bf6c2b35 546 }
1a3ac83f
JS
547 wxToolBarTimer::help_popup = (Widget) 0;
548 }
549 }
550}
551
552void wxToolBarTimer::Notify()
553{
554 Position x, y;
555
556 /************************************************************/
557 /* Create shell without window decorations */
558 /************************************************************/
bf6c2b35
VZ
559 help_popup = XtVaCreatePopupShell ("shell",
560 overrideShellWidgetClass, (Widget) wxTheApp->GetTopLevelWidget(),
1a3ac83f
JS
561 NULL);
562
563 /************************************************************/
564 /* Get absolute position on display of toolbar button */
565 /************************************************************/
566 XtTranslateCoords (buttonWidget,
bf6c2b35
VZ
567 (Position) 0,
568 (Position) 0,
1a3ac83f
JS
569 &x, &y);
570
571 // Move the tooltip more or less above the button
572 int yOffset = 20; // TODO: What should be really?
573 y -= yOffset;
574 if (y < yOffset) y = 0;
575
576 /************************************************************/
577 /* Set the position of the help popup */
578 /************************************************************/
bf6c2b35
VZ
579 XtVaSetValues (help_popup,
580 XmNx, (Position) x,
581 XmNy, (Position) y,
1a3ac83f 582 NULL);
bf6c2b35 583
1a3ac83f
JS
584 /************************************************************/
585 /* Create help label */
586 /************************************************************/
587 XmString text = XmStringCreateSimple ((char*) (const char*) helpString);
bf6c2b35
VZ
588 XtVaCreateManagedWidget ("help_label",
589 xmLabelWidgetClass, help_popup,
1a3ac83f 590 XmNlabelString, text,
bf6c2b35
VZ
591 XtVaTypedArg,
592 XmNforeground, XtRString, "black",
593 strlen("black")+1,
594 XtVaTypedArg,
595 XmNbackground, XtRString, "LightGoldenrod",
596 strlen("LightGoldenrod")+1,
1a3ac83f
JS
597 NULL);
598 XmStringFree (text);
599
600 /************************************************************/
601 /* Popup help label */
602 /************************************************************/
603 XtPopup (help_popup, XtGrabNone);
604}
605