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