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