Do not #include an header where a forward declaration suffixes. Do not
[wxWidgets.git] / src / motif / toolbar.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: motif/toolbar.cpp
3 // Purpose: wxToolBar
4 // Author: Julian Smart
5 // Modified by: 13.12.99 by VZ during toolbar classes reorganization
6 // Created: 04/01/98
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 #ifdef __GNUG__
21 #pragma implementation "toolbar.h"
22 #endif
23
24 #ifdef __VMS
25 #define XtDisplay XTDISPLAY
26 #endif
27
28 #include "wx/settings.h"
29 #include "wx/app.h"
30 #include "wx/timer.h"
31 #include "wx/toolbar.h"
32 #include "wx/frame.h"
33
34 #ifdef __VMS__
35 #pragma message disable nosimpint
36 #endif
37 #include <Xm/Xm.h>
38 #include <Xm/PushBG.h>
39 #include <Xm/PushB.h>
40 #include <Xm/Label.h>
41 #include <Xm/ToggleB.h>
42 #include <Xm/ToggleBG.h>
43 #include <Xm/Form.h>
44 #ifdef __VMS__
45 #pragma message enable nosimpint
46 #endif
47
48 #include "wx/motif/private.h"
49
50 // ----------------------------------------------------------------------------
51 // wxWin macros
52 // ----------------------------------------------------------------------------
53
54 #if !USE_SHARED_LIBRARY
55 IMPLEMENT_DYNAMIC_CLASS(wxToolBar, wxToolBarBase)
56 #endif
57
58 // ----------------------------------------------------------------------------
59 // private functions
60 // ----------------------------------------------------------------------------
61
62 static void wxToolButtonCallback (Widget w, XtPointer clientData,
63 XtPointer ptr);
64 static void wxToolButtonPopupCallback (Widget w, XtPointer client_data,
65 XEvent *event, Boolean *continue_to_dispatch);
66
67 // ----------------------------------------------------------------------------
68 // private classes
69 // ----------------------------------------------------------------------------
70
71 class wxToolBarTimer : public wxTimer
72 {
73 public:
74 virtual void Notify();
75
76 static Widget help_popup;
77 static Widget buttonWidget;
78 static wxString helpString;
79 };
80
81 class wxToolBarTool : public wxToolBarToolBase
82 {
83 public:
84 wxToolBarTool(wxToolBar *tbar,
85 int id,
86 const wxString& label,
87 const wxBitmap& bmpNormal,
88 const wxBitmap& bmpToggled,
89 wxItemKind kind,
90 wxObject *clientData,
91 const wxString& shortHelp,
92 const wxString& longHelp)
93 : wxToolBarToolBase(tbar, id, label, bmpNormal, bmpToggled, kind,
94 clientData, shortHelp, longHelp)
95 {
96 Init();
97 }
98
99 wxToolBarTool(wxToolBar *tbar, wxControl *control)
100 : wxToolBarToolBase(tbar, control)
101 {
102 Init();
103 }
104
105 virtual ~wxToolBarTool();
106
107 // accessors
108 void SetWidget(Widget widget) { m_widget = widget; }
109 Widget GetButtonWidget() const { return m_widget; }
110
111 void SetPixmap(Pixmap pixmap) { m_pixmap = pixmap; }
112 Pixmap GetPixmap() const { return m_pixmap; }
113
114 protected:
115 void Init();
116
117 Widget m_widget;
118 Pixmap m_pixmap;
119 };
120
121 // ----------------------------------------------------------------------------
122 // globals
123 // ----------------------------------------------------------------------------
124
125 static wxToolBarTimer* wxTheToolBarTimer = (wxToolBarTimer*) NULL;
126
127 Widget wxToolBarTimer::help_popup = (Widget) 0;
128 Widget wxToolBarTimer::buttonWidget = (Widget) 0;
129 wxString wxToolBarTimer::helpString;
130
131 // ============================================================================
132 // implementation
133 // ============================================================================
134
135 // ----------------------------------------------------------------------------
136 // wxToolBarTool
137 // ----------------------------------------------------------------------------
138
139 wxToolBarToolBase *wxToolBar::CreateTool(int id,
140 const wxString& label,
141 const wxBitmap& bmpNormal,
142 const wxBitmap& bmpToggled,
143 wxItemKind kind,
144 wxObject *clientData,
145 const wxString& shortHelp,
146 const wxString& longHelp)
147 {
148 return new wxToolBarTool(this, id, label, bmpNormal, bmpToggled, kind,
149 clientData, shortHelp, longHelp);
150 }
151
152
153 wxToolBarToolBase *wxToolBar::CreateTool(wxControl *control)
154 {
155 return new wxToolBarTool(this, control);
156 }
157
158 void wxToolBarTool::Init()
159 {
160 m_widget = (Widget)0;
161 m_pixmap = (Pixmap)0;
162 }
163
164 wxToolBarTool::~wxToolBarTool()
165 {
166 if ( m_widget )
167 XtDestroyWidget(m_widget);
168 // note: do not delete m_pixmap here because it will be deleted
169 // by the base class when the bitmap is destroyed.
170 }
171
172 // ----------------------------------------------------------------------------
173 // wxToolBar construction
174 // ----------------------------------------------------------------------------
175
176 void wxToolBar::Init()
177 {
178 m_maxWidth = -1;
179 m_maxHeight = -1;
180 m_defaultWidth = 24;
181 m_defaultHeight = 22;
182 m_toolPacking = 2;
183 m_toolSeparation = 8;
184 m_xMargin = 2;
185 m_yMargin = 2;
186 m_maxRows = 100;
187 m_maxCols = 100;
188 }
189
190 bool wxToolBar::Create(wxWindow *parent,
191 wxWindowID id,
192 const wxPoint& pos,
193 const wxSize& size,
194 long style,
195 const wxString& name)
196 {
197 if( !wxControl::CreateControl( parent, id, pos, size, style,
198 wxDefaultValidator, name ) )
199 return FALSE;
200
201 m_backgroundColour = wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE);
202
203 Widget parentWidget = (Widget) parent->GetClientWidget();
204
205 Widget toolbar = XtVaCreateManagedWidget("toolbar",
206 xmBulletinBoardWidgetClass, (Widget) parentWidget,
207 XmNmarginWidth, 0,
208 XmNmarginHeight, 0,
209 XmNresizePolicy, XmRESIZE_NONE,
210 NULL);
211 /*
212 Widget toolbar = XtVaCreateManagedWidget("toolbar",
213 xmFormWidgetClass, (Widget) m_clientWidget,
214 XmNtraversalOn, False,
215 XmNhorizontalSpacing, 0,
216 XmNverticalSpacing, 0,
217 XmNleftOffset, 0,
218 XmNrightOffset, 0,
219 XmNmarginWidth, 0,
220 XmNmarginHeight, 0,
221 NULL);
222 */
223
224 m_mainWidget = (WXWidget) toolbar;
225
226 ChangeFont(FALSE);
227
228 wxPoint rPos = pos;
229 wxSize rSize = size;
230
231 if( rPos.x == -1 ) rPos.x = 0;
232 if( rPos.y == -1 ) rPos.y = 0;
233 if( rSize.x == -1 && GetParent() )
234 rSize.x = GetParent()->GetSize().x;
235
236 SetCanAddEventHandler(TRUE);
237 AttachWidget (parent, m_mainWidget, (WXWidget) NULL,
238 rPos.x, rPos.y, rSize.x, rSize.y);
239
240 ChangeBackgroundColour();
241
242 return TRUE;
243 }
244
245 wxToolBar::~wxToolBar()
246 {
247 delete wxTheToolBarTimer;
248 wxTheToolBarTimer = NULL;
249 }
250
251 bool wxToolBar::Realize()
252 {
253 if ( m_tools.GetCount() == 0 )
254 {
255 // nothing to do
256 return TRUE;
257 }
258
259 bool isVertical = GetWindowStyle() & wxTB_VERTICAL;
260
261 // Separator spacing
262 const int separatorSize = GetToolSeparation(); // 8;
263 wxSize margins = GetToolMargins();
264 int packing = GetToolPacking();
265 int marginX = margins.x;
266 int marginY = margins.y;
267
268 int currentX = marginX;
269 int currentY = marginY;
270
271 int buttonHeight = 0, buttonWidth = 0;
272
273 int currentSpacing = 0;
274
275 Widget button;
276 Pixmap pixmap, insensPixmap;
277 wxBitmap bmp;
278
279 wxToolBarToolsList::Node *node = m_tools.GetFirst();
280 while ( node )
281 {
282 wxToolBarTool *tool = (wxToolBarTool *)node->GetData();
283
284 switch ( tool->GetStyle() )
285 {
286 case wxTOOL_STYLE_CONTROL:
287 {
288 wxControl* control = tool->GetControl();
289 wxSize sz = control->GetSize();
290 wxPoint pos = control->GetPosition();
291 // Allow a control to specify a y[x]-offset by setting
292 // its initial position, but still don't allow it to
293 // position itself above the top[left] margin.
294 int controlY = (pos.y > 0) ? currentY + pos.y : currentY;
295 int controlX = (pos.x > 0) ? currentX + pos.x : currentX;
296 control->Move( isVertical ? controlX : currentX,
297 isVertical ? currentY : controlY );
298 if ( isVertical )
299 currentY += sz.y + packing;
300 else
301 currentX += sz.x + packing;
302
303 break;
304 }
305 case wxTOOL_STYLE_SEPARATOR:
306 // skip separators for vertical toolbars
307 if( isVertical ) break;
308 currentX += separatorSize;
309 break;
310
311 case wxTOOL_STYLE_BUTTON:
312 button = (Widget) 0;
313
314 if ( tool->CanBeToggled() )
315 {
316 button = XtVaCreateWidget("toggleButton",
317 xmToggleButtonWidgetClass, (Widget) m_mainWidget,
318 XmNx, currentX, XmNy, currentY,
319 XmNindicatorOn, False,
320 XmNshadowThickness, 2,
321 XmNborderWidth, 0,
322 XmNspacing, 0,
323 XmNmarginWidth, 0,
324 XmNmarginHeight, 0,
325 XmNmultiClick, XmMULTICLICK_KEEP,
326 XmNlabelType, XmPIXMAP,
327 NULL);
328 XtAddCallback ((Widget) button, XmNvalueChangedCallback, (XtCallbackProc) wxToolButtonCallback,
329 (XtPointer) this);
330
331 XtVaSetValues ((Widget) button,
332 XmNselectColor, m_backgroundColour.AllocColour(XtDisplay((Widget) button)),
333 NULL);
334 }
335 else
336 {
337 button = XtVaCreateWidget("button",
338 xmPushButtonWidgetClass, (Widget) m_mainWidget,
339 XmNx, currentX, XmNy, currentY,
340 XmNpushButtonEnabled, True,
341 XmNmultiClick, XmMULTICLICK_KEEP,
342 XmNlabelType, XmPIXMAP,
343 NULL);
344 XtAddCallback (button,
345 XmNactivateCallback, (XtCallbackProc) wxToolButtonCallback,
346 (XtPointer) this);
347 }
348
349 DoChangeBackgroundColour((WXWidget) button, m_backgroundColour, TRUE);
350
351 tool->SetWidget(button);
352
353 // For each button, if there is a mask, we must create
354 // a new wxBitmap that has the correct background colour
355 // for the button. Otherwise the background will just be
356 // e.g. black if a transparent XPM has been loaded.
357 bmp = tool->GetNormalBitmap();
358 if ( bmp.GetMask() )
359 {
360 int backgroundPixel;
361 XtVaGetValues(button, XmNbackground, &backgroundPixel,
362 NULL);
363
364 wxColour col;
365 col.SetPixel(backgroundPixel);
366 bmp = wxCreateMaskedBitmap(bmp, col);
367
368 tool->SetNormalBitmap(bmp);
369 }
370
371 // Create a selected/toggled bitmap. If there isn't a 2nd
372 // bitmap, we need to create it (with a darker, selected
373 // background)
374 int backgroundPixel;
375 if ( tool->CanBeToggled() )
376 XtVaGetValues(button, XmNselectColor, &backgroundPixel,
377 NULL);
378 else
379 XtVaGetValues(button, XmNarmColor, &backgroundPixel,
380 NULL);
381 wxColour col;
382 col.SetPixel(backgroundPixel);
383
384 // FIXME: we use disabled bitmap as the bitmap for the toggled
385 // state, we probably need a GetToggledBitmap() instead
386 wxBitmap bmpToggled = tool->GetDisabledBitmap();
387 if ( bmpToggled.Ok() && bmpToggled.GetMask())
388 {
389 // Use what's there
390 wxBitmap newBitmap = wxCreateMaskedBitmap(bmpToggled, col);
391 tool->SetDisabledBitmap(newBitmap);
392 }
393 else
394 {
395 // Use unselected bitmap
396 if ( bmp.GetMask() )
397 {
398 wxBitmap newBitmap = wxCreateMaskedBitmap(bmp, col);
399 tool->SetDisabledBitmap(newBitmap);
400 }
401 else
402 tool->SetDisabledBitmap(bmp);
403 }
404
405 // FIXME: and here we should use GetDisabledBitmap()...
406 pixmap = (Pixmap) bmp.GetPixmap();
407 insensPixmap = (Pixmap) bmp.GetInsensPixmap();
408
409 if (tool->CanBeToggled())
410 {
411 // Toggle button
412 Pixmap pixmap2 = (Pixmap) 0;
413 Pixmap insensPixmap2 = (Pixmap) 0;
414
415 // If there's a bitmap for the toggled state, use it,
416 // otherwise generate one.
417 //
418 // FIXME: see above
419 if ( bmpToggled.Ok() )
420 {
421 pixmap2 = (Pixmap) bmpToggled.GetPixmap();
422 insensPixmap2 = (Pixmap) bmpToggled.GetInsensPixmap();
423 }
424 else
425 {
426 pixmap2 = (Pixmap) bmp.GetArmPixmap(button);
427 insensPixmap2 = XCreateInsensitivePixmap((Display*) wxGetDisplay(), pixmap2);
428 }
429
430 tool->SetPixmap(pixmap2);
431
432 XtVaSetValues (button,
433 XmNfillOnSelect, True,
434 XmNlabelPixmap, pixmap,
435 XmNselectPixmap, pixmap2,
436 XmNlabelInsensitivePixmap, insensPixmap,
437 XmNselectInsensitivePixmap, insensPixmap2,
438 XmNlabelType, XmPIXMAP,
439 NULL);
440 }
441 else
442 {
443 Pixmap pixmap2 = (Pixmap) 0;
444
445 // If there's a bitmap for the armed state, use it,
446 // otherwise generate one.
447 if ( bmpToggled.Ok() )
448 {
449 pixmap2 = (Pixmap) bmpToggled.GetPixmap();
450 }
451 else
452 {
453 pixmap2 = (Pixmap) bmp.GetArmPixmap(button);
454
455 }
456
457 tool->SetPixmap(pixmap2);
458
459 // Normal button
460 XtVaSetValues(button,
461 XmNlabelPixmap, pixmap,
462 XmNlabelInsensitivePixmap, insensPixmap,
463 XmNarmPixmap, pixmap2,
464 NULL);
465 }
466
467 XtManageChild(button);
468
469 {
470 Dimension width, height;
471 XtVaGetValues(button,
472 XmNwidth, &width,
473 XmNheight, & height,
474 NULL);
475 if ( isVertical )
476 currentY += height + packing;
477 else
478 currentX += width + packing;
479 buttonHeight = wxMax(buttonHeight, height);
480 buttonWidth = wxMax(buttonWidth, width);
481 }
482
483 XtAddEventHandler (button, EnterWindowMask | LeaveWindowMask,
484 False, wxToolButtonPopupCallback, (XtPointer) this);
485
486 currentSpacing = 0;
487 break;
488 }
489
490 node = node->GetNext();
491 }
492
493 SetSize( -1, -1,
494 isVertical ? buttonWidth + 2 * marginX : currentX,
495 isVertical ? currentY : buttonHeight + 2*marginY );
496
497 return TRUE;
498 }
499
500 wxToolBarToolBase *wxToolBar::FindToolForPosition(wxCoord WXUNUSED(x),
501 wxCoord WXUNUSED(y)) const
502 {
503 wxFAIL_MSG( _T("TODO") );
504
505 return (wxToolBarToolBase *)NULL;
506 }
507
508 bool wxToolBar::DoInsertTool(size_t WXUNUSED(pos), wxToolBarToolBase *tool)
509 {
510 tool->Attach(this);
511
512 return TRUE;
513 }
514
515 bool wxToolBar::DoDeleteTool(size_t WXUNUSED(pos), wxToolBarToolBase *tool)
516 {
517 tool->Detach();
518
519 return TRUE;
520 }
521
522 void wxToolBar::DoEnableTool(wxToolBarToolBase *toolBase, bool enable)
523 {
524 wxToolBarTool *tool = (wxToolBarTool *)toolBase;
525
526 XtSetSensitive(tool->GetButtonWidget(), (Boolean) enable);
527 }
528
529 void wxToolBar::DoToggleTool(wxToolBarToolBase *toolBase, bool toggle)
530 {
531 wxToolBarTool *tool = (wxToolBarTool *)toolBase;
532
533 XmToggleButtonSetState(tool->GetButtonWidget(), (Boolean) toggle, False);
534 }
535
536 void wxToolBar::DoSetToggle(wxToolBarToolBase * WXUNUSED(tool),
537 bool WXUNUSED(toggle))
538 {
539 // nothing to do
540 }
541
542 void wxToolBar::DoSetSize(int x, int y, int width, int height, int sizeFlags)
543 {
544 int old_width, old_height;
545 GetSize(&old_width, &old_height);
546
547 wxToolBarBase::DoSetSize(x, y, width, height, sizeFlags);
548
549 // Correct width and height if needed.
550 if ( width == -1 || height == -1 )
551 {
552 int tmp_width, tmp_height;
553 GetSize(&tmp_width, &tmp_height);
554
555 if ( width == -1 )
556 width = tmp_width;
557 if ( height == -1 )
558 height = tmp_height;
559 }
560
561 // We must refresh the frame size when the toolbar changes size
562 // otherwise the toolbar can be shown incorrectly
563 if ( old_width != width || old_height != height )
564 {
565 // But before we send the size event check it
566 // we have a frame that is not being deleted.
567 wxFrame *frame = wxDynamicCast(GetParent(), wxFrame);
568 if ( frame && !frame->IsBeingDeleted() )
569 {
570 frame->SendSizeEvent();
571 }
572 }
573 }
574
575 // ----------------------------------------------------------------------------
576 // Motif callbacks
577 // ----------------------------------------------------------------------------
578
579 wxToolBarToolBase *wxToolBar::FindToolByWidget(WXWidget w) const
580 {
581 wxToolBarToolsList::Node* node = m_tools.GetFirst();
582 while ( node )
583 {
584 wxToolBarTool *tool = (wxToolBarTool *)node->GetData();
585 if ( tool->GetButtonWidget() == w)
586 {
587 return tool;
588 }
589
590 node = node->GetNext();
591 }
592
593 return (wxToolBarToolBase *)NULL;
594 }
595
596 static void wxToolButtonCallback(Widget w,
597 XtPointer clientData,
598 XtPointer WXUNUSED(ptr))
599 {
600 wxToolBar *toolBar = (wxToolBar *) clientData;
601 wxToolBarToolBase *tool = toolBar->FindToolByWidget((WXWidget) w);
602 if ( !tool )
603 return;
604
605 if ( tool->CanBeToggled() )
606 tool->Toggle();
607
608 if ( !toolBar->OnLeftClick(tool->GetId(), tool->IsToggled()) )
609 {
610 // revert
611 tool->Toggle();
612 }
613 }
614
615
616 static void wxToolButtonPopupCallback(Widget w,
617 XtPointer client_data,
618 XEvent *event,
619 Boolean *WXUNUSED(continue_to_dispatch))
620 {
621 // TODO: retrieve delay before popping up tooltip from wxSystemSettings.
622 static const int delayMilli = 800;
623
624 wxToolBar* toolBar = (wxToolBar*) client_data;
625 wxToolBarToolBase *tool = toolBar->FindToolByWidget((WXWidget) w);
626
627 if ( !tool )
628 return;
629
630 wxString tooltip = tool->GetShortHelp();
631 if ( !tooltip )
632 return;
633
634 if (!wxTheToolBarTimer)
635 wxTheToolBarTimer = new wxToolBarTimer;
636
637 wxToolBarTimer::buttonWidget = w;
638 wxToolBarTimer::helpString = tooltip;
639
640 /************************************************************/
641 /* Popup help label */
642 /************************************************************/
643 if (event->type == EnterNotify)
644 {
645 if (wxToolBarTimer::help_popup != (Widget) 0)
646 {
647 XtDestroyWidget (wxToolBarTimer::help_popup);
648 XtPopdown (wxToolBarTimer::help_popup);
649 }
650 wxToolBarTimer::help_popup = (Widget) 0;
651
652 // One shot
653 wxTheToolBarTimer->Start(delayMilli, TRUE);
654
655 }
656 /************************************************************/
657 /* Popdown help label */
658 /************************************************************/
659 else if (event->type == LeaveNotify)
660 {
661 if (wxTheToolBarTimer)
662 wxTheToolBarTimer->Stop();
663 if (wxToolBarTimer::help_popup != (Widget) 0)
664 {
665 XtDestroyWidget (wxToolBarTimer::help_popup);
666 XtPopdown (wxToolBarTimer::help_popup);
667 }
668 wxToolBarTimer::help_popup = (Widget) 0;
669 }
670 }
671
672 void wxToolBarTimer::Notify()
673 {
674 Position x, y;
675
676 /************************************************************/
677 /* Create shell without window decorations */
678 /************************************************************/
679 help_popup = XtVaCreatePopupShell ("shell",
680 overrideShellWidgetClass, (Widget) wxTheApp->GetTopLevelWidget(),
681 NULL);
682
683 /************************************************************/
684 /* Get absolute position on display of toolbar button */
685 /************************************************************/
686 XtTranslateCoords (buttonWidget,
687 (Position) 0,
688 (Position) 0,
689 &x, &y);
690
691 // Move the tooltip more or less above the button
692 int yOffset = 20; // TODO: What should be really?
693 y -= yOffset;
694 if (y < yOffset) y = 0;
695
696 /************************************************************/
697 /* Set the position of the help popup */
698 /************************************************************/
699 XtVaSetValues (help_popup,
700 XmNx, (Position) x,
701 XmNy, (Position) y,
702 NULL);
703
704 /************************************************************/
705 /* Create help label */
706 /************************************************************/
707 XmString text = XmStringCreateSimple ((char*) (const char*) helpString);
708 XtVaCreateManagedWidget ("help_label",
709 xmLabelWidgetClass, help_popup,
710 XmNlabelString, text,
711 XtVaTypedArg,
712 XmNforeground, XtRString, "black",
713 strlen("black")+1,
714 XtVaTypedArg,
715 XmNbackground, XtRString, "LightGoldenrod",
716 strlen("LightGoldenrod")+1,
717 NULL);
718 XmStringFree (text);
719
720 /************************************************************/
721 /* Popup help label */
722 /************************************************************/
723 XtPopup (help_popup, XtGrabNone);
724 }
725