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