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