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