Fix crash on exit with Lesstif (and possibly Motif 1.x).
[wxWidgets.git] / src / motif / choice.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: choice.cpp
3 // Purpose: wxChoice
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 17/09/98
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
13 #pragma implementation "choice.h"
14 #endif
15
16 // For compilers that support precompilation, includes "wx.h".
17 #include "wx/wxprec.h"
18
19 #ifdef __VMS
20 #define XtDisplay XTDISPLAY
21 #define XtParent XTPARENT
22 #endif
23
24 #include "wx/defs.h"
25
26 #if wxUSE_CHOICE
27
28 #include "wx/choice.h"
29 #include "wx/utils.h"
30 #include "wx/arrstr.h"
31
32 #ifdef __VMS__
33 #pragma message disable nosimpint
34 #endif
35 #include <Xm/Xm.h>
36 #include <Xm/PushBG.h>
37 #include <Xm/PushB.h>
38 #include <Xm/RowColumn.h>
39 #ifdef __VMS__
40 #pragma message enable nosimpint
41 #endif
42
43 #include "wx/motif/private.h"
44
45 #define WIDTH_OVERHEAD 48
46 #define WIDTH_OVERHEAD_SUBTRACT 40
47 #define HEIGHT_OVERHEAD 15
48
49 IMPLEMENT_DYNAMIC_CLASS(wxChoice, wxControl)
50
51 void wxChoiceCallback (Widget w, XtPointer clientData,
52 XtPointer ptr);
53
54 wxChoice::wxChoice()
55 {
56 Init();
57 }
58
59 void wxChoice::Init()
60 {
61 m_noStrings = 0;
62 m_buttonWidget = (WXWidget) 0;
63 m_menuWidget = (WXWidget) 0;
64 m_formWidget = (WXWidget) 0;
65 }
66
67 bool wxChoice::Create(wxWindow *parent, wxWindowID id,
68 const wxPoint& pos,
69 const wxSize& size,
70 int n, const wxString choices[],
71 long style,
72 const wxValidator& validator,
73 const wxString& name)
74 {
75 if ( !CreateControl(parent, id, pos, size, style, validator, name) )
76 return false;
77
78 Widget parentWidget = (Widget) parent->GetClientWidget();
79
80 m_formWidget = (WXWidget) XtVaCreateManagedWidget(name.c_str(),
81 xmRowColumnWidgetClass, parentWidget,
82 XmNmarginHeight, 0,
83 XmNmarginWidth, 0,
84 XmNpacking, XmPACK_TIGHT,
85 XmNorientation, XmHORIZONTAL,
86 XmNresizeWidth, False,
87 XmNresizeHeight, False,
88 NULL);
89
90 XtVaSetValues ((Widget) m_formWidget, XmNspacing, 0, NULL);
91
92 /*
93 * Create the popup menu
94 */
95 m_menuWidget = (WXWidget) XmCreatePulldownMenu ((Widget) m_formWidget,
96 "choiceMenu", NULL, 0);
97
98 if (n > 0)
99 {
100 int i;
101 for (i = 0; i < n; i++)
102 Append (choices[i]);
103 }
104
105 /*
106 * Create button
107 */
108 Arg args[10];
109 Cardinal argcnt = 0;
110
111 XtSetArg (args[argcnt], XmNsubMenuId, (Widget) m_menuWidget); ++argcnt;
112 XtSetArg (args[argcnt], XmNmarginWidth, 0); ++argcnt;
113 XtSetArg (args[argcnt], XmNmarginHeight, 0); ++argcnt;
114 XtSetArg (args[argcnt], XmNpacking, XmPACK_TIGHT); ++argcnt;
115 m_buttonWidget = (WXWidget) XmCreateOptionMenu ((Widget) m_formWidget,
116 "choiceButton",
117 args, argcnt);
118
119 m_mainWidget = m_buttonWidget;
120
121 XtManageChild ((Widget) m_buttonWidget);
122
123 // New code from Roland Haenel (roland_haenel@ac.cybercity.de)
124 // Some time ago, I reported a problem with wxChoice-items under
125 // Linux and Motif 2.0 (they caused sporadic GPFs). Now it seems
126 // that I have found the code responsible for this behaviour.
127 #if XmVersion >= 1002
128 #if XmVersion < 2000
129 // JACS, 24/1/99: this seems to cause a malloc crash later on, e.g.
130 // in controls sample.
131 //
132 // Widget optionLabel = XmOptionLabelGadget ((Widget) m_buttonWidget);
133 // XtUnmanageChild (optionLabel);
134 #endif
135 #endif
136
137 wxSize bestSize = GetBestSize();
138 if( size.x > 0 ) bestSize.x = size.x;
139 if( size.y > 0 ) bestSize.y = size.y;
140
141 XtVaSetValues((Widget) m_formWidget, XmNresizePolicy, XmRESIZE_NONE, NULL);
142
143 ChangeFont(false);
144 ChangeBackgroundColour();
145
146 AttachWidget (parent, m_buttonWidget, m_formWidget,
147 pos.x, pos.y, bestSize.x, bestSize.y);
148
149 return true;
150 }
151
152 bool wxChoice::Create(wxWindow *parent, wxWindowID id,
153 const wxPoint& pos,
154 const wxSize& size,
155 const wxArrayString& choices,
156 long style,
157 const wxValidator& validator,
158 const wxString& name)
159 {
160 wxCArrayString chs(choices);
161 return Create(parent, id, pos, size, chs.GetCount(), chs.GetStrings(),
162 style, validator, name);
163 }
164
165 wxChoice::~wxChoice()
166 {
167 // For some reason destroying the menuWidget
168 // can cause crashes on some machines. It will
169 // be deleted implicitly by deleting the parent form
170 // anyway.
171 // XtDestroyWidget (menuWidget);
172
173 if (GetMainWidget())
174 {
175 DetachWidget(GetMainWidget()); // Removes event handlers
176 DetachWidget(m_formWidget);
177
178 XtDestroyWidget((Widget) m_formWidget);
179 m_formWidget = (WXWidget) 0;
180
181 // Presumably the other widgets have been deleted now, via the form
182 m_mainWidget = (WXWidget) 0;
183 m_buttonWidget = (WXWidget) 0;
184 }
185 if ( HasClientObjectData() )
186 m_clientDataDict.DestroyData();
187 }
188
189 int wxChoice::DoAppend(const wxString& item)
190 {
191 Widget w = XtVaCreateManagedWidget (wxStripMenuCodes(item),
192 #if USE_GADGETS
193 xmPushButtonGadgetClass, (Widget) m_menuWidget,
194 #else
195 xmPushButtonWidgetClass, (Widget) m_menuWidget,
196 #endif
197 NULL);
198
199 wxDoChangeBackgroundColour((WXWidget) w, m_backgroundColour);
200
201 if( m_font.Ok() )
202 wxDoChangeFont( w, m_font );
203
204 m_widgetArray.Add(w);
205
206 char mnem = wxFindMnemonic (item);
207 if (mnem != 0)
208 XtVaSetValues (w, XmNmnemonic, mnem, NULL);
209
210 XtAddCallback (w, XmNactivateCallback,
211 (XtCallbackProc) wxChoiceCallback,
212 (XtPointer) this);
213
214 if (m_noStrings == 0 && m_buttonWidget)
215 {
216 XtVaSetValues ((Widget) m_buttonWidget, XmNmenuHistory, w, NULL);
217 Widget label = XmOptionButtonGadget ((Widget) m_buttonWidget);
218 wxXmString text( item );
219 XtVaSetValues (label,
220 XmNlabelString, text(),
221 NULL);
222 }
223 m_stringList.Add(item);
224 m_noStrings ++;
225
226 return GetCount() - 1;
227 }
228
229 int wxChoice::DoInsert(const wxString& item, int pos)
230 {
231 wxCHECK_MSG(false, -1, wxT("insert not implemented"));
232
233 // wxCHECK_MSG((pos>=0) && (pos<=GetCount()), -1, wxT("invalid index"));
234 // if (pos == GetCount()) return DoAppend(item);
235 }
236
237 void wxChoice::Delete(int n)
238 {
239 Widget w = (Widget)m_widgetArray[n];
240 XtRemoveCallback(w, XmNactivateCallback, (XtCallbackProc)wxChoiceCallback,
241 (XtPointer)this);
242 m_stringList.Erase(m_stringList.Item(n));
243 m_widgetArray.RemoveAt(size_t(n));
244 m_clientDataDict.Delete(n, HasClientObjectData());
245
246 XtDestroyWidget(w);
247 m_noStrings --;
248 }
249
250 void wxChoice::Clear()
251 {
252 m_stringList.Clear ();
253 size_t i;
254 for (i = 0; i < m_noStrings; i++)
255 {
256 XtRemoveCallback((Widget) m_widgetArray[i],
257 XmNactivateCallback, (XtCallbackProc)wxChoiceCallback,
258 (XtPointer)this);
259 XtUnmanageChild ((Widget) m_widgetArray[i]);
260 XtDestroyWidget ((Widget) m_widgetArray[i]);
261 }
262 m_widgetArray.Clear();
263 if (m_buttonWidget)
264 XtVaSetValues ((Widget) m_buttonWidget,
265 XmNmenuHistory, (Widget) NULL,
266 NULL);
267
268 if ( HasClientObjectData() )
269 m_clientDataDict.DestroyData();
270
271 m_noStrings = 0;
272 }
273
274 int wxChoice::GetSelection() const
275 {
276 XmString text;
277 Widget label = XmOptionButtonGadget ((Widget) m_buttonWidget);
278 XtVaGetValues (label,
279 XmNlabelString, &text,
280 NULL);
281 wxXmString freeMe(text);
282 wxString s = wxXmStringToString( text );
283
284 if (!s.IsEmpty())
285 {
286 int i = 0;
287 for (wxStringList::compatibility_iterator node = m_stringList.GetFirst ();
288 node; node = node->GetNext ())
289 {
290 if (wxStrcmp(node->GetData(), s.c_str()) == 0)
291 {
292 return i;
293 }
294 else
295 i++;
296 } // for()
297
298 return -1;
299 }
300 return -1;
301 }
302
303 void wxChoice::SetSelection(int n)
304 {
305 m_inSetValue = true;
306
307 wxStringList::compatibility_iterator node = m_stringList.Item(n);
308 if (node)
309 {
310 #if 0
311 Dimension selectionWidth, selectionHeight;
312 #endif
313 wxXmString text( node->GetData() );
314 // MBN: this seems silly, at best, and causes wxChoices to be clipped:
315 // will remove "soon"
316 #if 0
317 XtVaGetValues ((Widget) m_widgetArray[n],
318 XmNwidth, &selectionWidth,
319 XmNheight, &selectionHeight,
320 NULL);
321 #endif
322 Widget label = XmOptionButtonGadget ((Widget) m_buttonWidget);
323 XtVaSetValues (label,
324 XmNlabelString, text(),
325 NULL);
326 #if 0
327 XtVaSetValues ((Widget) m_buttonWidget,
328 XmNwidth, selectionWidth, XmNheight, selectionHeight,
329 XmNmenuHistory, (Widget) m_widgetArray[n], NULL);
330 #endif
331 }
332 m_inSetValue = false;
333 }
334
335 int wxChoice::FindString(const wxString& s) const
336 {
337 int i = 0;
338 for (wxStringList::compatibility_iterator node = m_stringList.GetFirst();
339 node; node = node->GetNext ())
340 {
341 if (s == node->GetData())
342 return i;
343
344 i++;
345 }
346
347 return wxNOT_FOUND;
348 }
349
350 wxString wxChoice::GetString(int n) const
351 {
352 wxStringList::compatibility_iterator node = m_stringList.Item(n);
353 if (node)
354 return node->GetData();
355 else
356 return wxEmptyString;
357 }
358
359 void wxChoice::SetColumns(int n)
360 {
361 if (n<1) n = 1 ;
362
363 short numColumns = n ;
364 Arg args[3];
365
366 XtSetArg(args[0], XmNnumColumns, numColumns);
367 XtSetArg(args[1], XmNpacking, XmPACK_COLUMN);
368 XtSetValues((Widget) m_menuWidget,args,2) ;
369 }
370
371 int wxChoice::GetColumns(void) const
372 {
373 short numColumns ;
374
375 XtVaGetValues((Widget) m_menuWidget,XmNnumColumns,&numColumns,NULL) ;
376 return numColumns ;
377 }
378
379 void wxChoice::SetFocus()
380 {
381 XmProcessTraversal(XtParent((Widget)m_mainWidget), XmTRAVERSE_CURRENT);
382 }
383
384 void wxChoice::DoSetSize(int x, int y, int width, int height, int sizeFlags)
385 {
386 XtVaSetValues((Widget) m_formWidget, XmNresizePolicy, XmRESIZE_ANY, NULL);
387 bool managed = XtIsManaged((Widget) m_formWidget);
388
389 if (managed)
390 XtUnmanageChild ((Widget) m_formWidget);
391
392 int actualWidth = width - WIDTH_OVERHEAD_SUBTRACT,
393 actualHeight = height - HEIGHT_OVERHEAD;
394
395 if (width > -1)
396 {
397 size_t i;
398 for (i = 0; i < m_noStrings; i++)
399 XtVaSetValues ((Widget) m_widgetArray[i],
400 XmNwidth, actualWidth,
401 NULL);
402 XtVaSetValues ((Widget) m_buttonWidget, XmNwidth, actualWidth,
403 NULL);
404 }
405 if (height > -1)
406 {
407 #if 0
408 size_t i;
409 for (i = 0; i < m_noStrings; i++)
410 XtVaSetValues ((Widget) m_widgetArray[i],
411 XmNheight, actualHeight,
412 NULL);
413 #endif
414 XtVaSetValues ((Widget) m_buttonWidget, XmNheight, actualHeight,
415 NULL);
416 }
417
418 if (managed)
419 XtManageChild ((Widget) m_formWidget);
420 XtVaSetValues((Widget) m_formWidget, XmNresizePolicy, XmRESIZE_NONE, NULL);
421
422 wxControl::DoSetSize (x, y, width, height, sizeFlags);
423 }
424
425 void wxChoice::Command(wxCommandEvent & event)
426 {
427 SetSelection (event.GetInt());
428 ProcessCommand (event);
429 }
430
431 void wxChoiceCallback (Widget w, XtPointer clientData, XtPointer WXUNUSED(ptr))
432 {
433 wxChoice *item = (wxChoice *) clientData;
434 if (item)
435 {
436 if (item->InSetValue())
437 return;
438
439 int n = item->GetWidgets().Index(w);
440 if (n != wxNOT_FOUND)
441 {
442 wxCommandEvent event(wxEVT_COMMAND_CHOICE_SELECTED, item->GetId());
443 event.SetEventObject(item);
444 event.SetInt(n);
445 event.SetString( item->GetStrings().Item(n)->GetData() );
446 if ( item->HasClientObjectData() )
447 event.SetClientObject( item->GetClientObject(n) );
448 else if ( item->HasClientUntypedData() )
449 event.SetClientData( item->GetClientData(n) );
450 item->ProcessCommand (event);
451 }
452 }
453 }
454
455 void wxChoice::ChangeFont(bool keepOriginalSize)
456 {
457 // Note that this causes the widget to be resized back
458 // to its original size! We therefore have to set the size
459 // back again. TODO: a better way in Motif?
460 if (m_font.Ok())
461 {
462 Display* dpy = XtDisplay((Widget) m_mainWidget);
463 int width, height, width1, height1;
464 GetSize(& width, & height);
465
466 WXString fontTag = wxFont::GetFontTag();
467
468 XtVaSetValues ((Widget) m_formWidget,
469 fontTag, m_font.GetFontTypeC(dpy),
470 NULL);
471 XtVaSetValues ((Widget) m_buttonWidget,
472 fontTag, m_font.GetFontTypeC(dpy),
473 NULL);
474
475 for( size_t i = 0; i < m_noStrings; ++i )
476 XtVaSetValues( (Widget)m_widgetArray[i],
477 fontTag, m_font.GetFontTypeC(dpy),
478 NULL );
479
480 GetSize(& width1, & height1);
481 if (keepOriginalSize && (width != width1 || height != height1))
482 {
483 SetSize(wxDefaultCoord, wxDefaultCoord, width, height);
484 }
485 }
486 }
487
488 void wxChoice::ChangeBackgroundColour()
489 {
490 wxDoChangeBackgroundColour(m_formWidget, m_backgroundColour);
491 wxDoChangeBackgroundColour(m_buttonWidget, m_backgroundColour);
492 wxDoChangeBackgroundColour(m_menuWidget, m_backgroundColour);
493 size_t i;
494 for (i = 0; i < m_noStrings; i++)
495 wxDoChangeBackgroundColour(m_widgetArray[i], m_backgroundColour);
496 }
497
498 void wxChoice::ChangeForegroundColour()
499 {
500 wxDoChangeForegroundColour(m_formWidget, m_foregroundColour);
501 wxDoChangeForegroundColour(m_buttonWidget, m_foregroundColour);
502 wxDoChangeForegroundColour(m_menuWidget, m_foregroundColour);
503 size_t i;
504 for (i = 0; i < m_noStrings; i++)
505 wxDoChangeForegroundColour(m_widgetArray[i], m_foregroundColour);
506 }
507
508 int wxChoice::GetCount() const
509 {
510 return m_noStrings;
511 }
512
513 void wxChoice::DoSetItemClientData(int n, void* clientData)
514 {
515 m_clientDataDict.Set(n, (wxClientData*)clientData, false);
516 }
517
518 void* wxChoice::DoGetItemClientData(int n) const
519 {
520 return (void*)m_clientDataDict.Get(n);
521 }
522
523 void wxChoice::DoSetItemClientObject(int n, wxClientData* clientData)
524 {
525 // don't delete, wxItemContainer does that for us
526 m_clientDataDict.Set(n, clientData, false);
527 }
528
529 wxClientData* wxChoice::DoGetItemClientObject(int n) const
530 {
531 return m_clientDataDict.Get(n);
532 }
533
534 void wxChoice::SetString(int WXUNUSED(n), const wxString& WXUNUSED(s))
535 {
536 wxFAIL_MSG( wxT("wxChoice::SetString not implemented") );
537 }
538
539 wxSize wxChoice::GetItemsSize() const
540 {
541 int x, y, mx = 0, my = 0;
542
543 // get my
544 GetTextExtent( "|", &x, &my );
545
546 wxStringList::compatibility_iterator curr = m_stringList.GetFirst();
547 while( curr )
548 {
549 GetTextExtent( curr->GetData(), &x, &y );
550 mx = wxMax( mx, x );
551 my = wxMax( my, y );
552 curr = curr->GetNext();
553 }
554
555 return wxSize( mx, my );
556 }
557
558 wxSize wxChoice::DoGetBestSize() const
559 {
560 wxSize items = GetItemsSize();
561 // FIXME arbitrary constants
562 return wxSize( ( items.x ? items.x + WIDTH_OVERHEAD : 120 ),
563 items.y + HEIGHT_OVERHEAD );
564 }
565
566 #endif // wxUSE_CHOICE