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