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