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