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