Added some missing STL-like wxArray/wxArrayString constructors.
[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 #ifdef __VMS
17 #define XtDisplay XTDISPLAY
18 #define XtParent XTPARENT
19 #endif
20
21 #include "wx/defs.h"
22
23 #include "wx/choice.h"
24 #include "wx/utils.h"
25 #include "wx/arrstr.h"
26
27 #ifdef __VMS__
28 #pragma message disable nosimpint
29 #endif
30 #include <Xm/Xm.h>
31 #include <Xm/PushBG.h>
32 #include <Xm/PushB.h>
33 #include <Xm/RowColumn.h>
34 #ifdef __VMS__
35 #pragma message enable nosimpint
36 #endif
37
38 #include "wx/motif/private.h"
39
40 #define WIDTH_OVERHEAD 48
41 #define WIDTH_OVERHEAD_SUBTRACT 40
42 #define HEIGHT_OVERHEAD 15
43
44 IMPLEMENT_DYNAMIC_CLASS(wxChoice, wxControl)
45
46 void wxChoiceCallback (Widget w, XtPointer clientData,
47 XtPointer ptr);
48
49 wxChoice::wxChoice()
50 {
51 Init();
52 }
53
54 void wxChoice::Init()
55 {
56 m_noStrings = 0;
57 m_buttonWidget = (WXWidget) 0;
58 m_menuWidget = (WXWidget) 0;
59 m_formWidget = (WXWidget) 0;
60 }
61
62 bool wxChoice::Create(wxWindow *parent, wxWindowID id,
63 const wxPoint& pos,
64 const wxSize& size,
65 int n, const wxString choices[],
66 long style,
67 const wxValidator& validator,
68 const wxString& name)
69 {
70 if ( !CreateControl(parent, id, pos, size, style, validator, name) )
71 return FALSE;
72
73 Widget parentWidget = (Widget) parent->GetClientWidget();
74
75 m_formWidget = (WXWidget) XtVaCreateManagedWidget(name.c_str(),
76 xmRowColumnWidgetClass, parentWidget,
77 XmNmarginHeight, 0,
78 XmNmarginWidth, 0,
79 XmNpacking, XmPACK_TIGHT,
80 XmNorientation, XmHORIZONTAL,
81 XmNresizeWidth, False,
82 XmNresizeHeight, False,
83 NULL);
84
85 XtVaSetValues ((Widget) m_formWidget, XmNspacing, 0, NULL);
86
87 /*
88 * Create the popup menu
89 */
90 m_menuWidget = (WXWidget) XmCreatePulldownMenu ((Widget) m_formWidget,
91 "choiceMenu", 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 "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 int wxChoice::DoAppend(const wxString& item)
185 {
186 Widget w = XtVaCreateManagedWidget (wxStripMenuCodes(item),
187 #if USE_GADGETS
188 xmPushButtonGadgetClass, (Widget) m_menuWidget,
189 #else
190 xmPushButtonWidgetClass, (Widget) m_menuWidget,
191 #endif
192 NULL);
193
194 wxDoChangeBackgroundColour((WXWidget) w, m_backgroundColour);
195
196 if( m_font.Ok() )
197 wxDoChangeFont( w, m_font );
198
199 m_widgetArray.Add(w);
200
201 char mnem = wxFindMnemonic (item);
202 if (mnem != 0)
203 XtVaSetValues (w, XmNmnemonic, mnem, NULL);
204
205 XtAddCallback (w, XmNactivateCallback,
206 (XtCallbackProc) wxChoiceCallback,
207 (XtPointer) this);
208
209 if (m_noStrings == 0 && m_buttonWidget)
210 {
211 XtVaSetValues ((Widget) m_buttonWidget, XmNmenuHistory, w, NULL);
212 Widget label = XmOptionButtonGadget ((Widget) m_buttonWidget);
213 wxXmString text( item );
214 XtVaSetValues (label,
215 XmNlabelString, text(),
216 NULL);
217 }
218 m_stringList.Add(item);
219 m_noStrings ++;
220
221 return GetCount() - 1;
222 }
223
224 int wxChoice::DoInsert(const wxString& item, int pos)
225 {
226 wxCHECK_MSG(FALSE, -1, wxT("insert not implemented"));
227
228 // wxCHECK_MSG((pos>=0) && (pos<=GetCount()), -1, wxT("invalid index"));
229 // if (pos == GetCount()) return DoAppend(item);
230 }
231
232 void wxChoice::Delete(int n)
233 {
234 Widget w = (Widget)m_widgetArray[n];
235 XtRemoveCallback(w, XmNactivateCallback, (XtCallbackProc)wxChoiceCallback,
236 (XtPointer)this);
237 m_stringList.Erase(m_stringList.Item(n));
238 m_widgetArray.RemoveAt(size_t(n));
239 m_clientDataDict.Delete(n, HasClientObjectData());
240
241 XtDestroyWidget(w);
242 m_noStrings --;
243 }
244
245 void wxChoice::Clear()
246 {
247 m_stringList.Clear ();
248 size_t i;
249 for (i = 0; i < m_noStrings; i++)
250 {
251 XtRemoveCallback((Widget) m_widgetArray[i],
252 XmNactivateCallback, (XtCallbackProc)wxChoiceCallback,
253 (XtPointer)this);
254 XtUnmanageChild ((Widget) m_widgetArray[i]);
255 XtDestroyWidget ((Widget) m_widgetArray[i]);
256 }
257 m_widgetArray.Clear();
258 if (m_buttonWidget)
259 XtVaSetValues ((Widget) m_buttonWidget,
260 XmNmenuHistory, (Widget) NULL,
261 NULL);
262
263 if ( HasClientObjectData() )
264 m_clientDataDict.DestroyData();
265
266 m_noStrings = 0;
267 }
268
269 int wxChoice::GetSelection() const
270 {
271 XmString text;
272 Widget label = XmOptionButtonGadget ((Widget) m_buttonWidget);
273 XtVaGetValues (label,
274 XmNlabelString, &text,
275 NULL);
276 wxXmString freeMe(text);
277 wxString s = wxXmStringToString( text );
278
279 if (!s.IsEmpty())
280 {
281 int i = 0;
282 for (wxStringList::compatibility_iterator node = m_stringList.GetFirst ();
283 node; node = node->GetNext ())
284 {
285 if (wxStrcmp(node->GetData(), s.c_str()) == 0)
286 {
287 return i;
288 }
289 else
290 i++;
291 } // for()
292
293 return -1;
294 }
295 return -1;
296 }
297
298 void wxChoice::SetSelection(int n)
299 {
300 m_inSetValue = TRUE;
301
302 wxStringList::compatibility_iterator node = m_stringList.Item(n);
303 if (node)
304 {
305 #if 0
306 Dimension selectionWidth, selectionHeight;
307 #endif
308 wxXmString text( node->GetData() );
309 // MBN: this seems silly, at best, and causes wxChoices to be clipped:
310 // will remove "soon"
311 #if 0
312 XtVaGetValues ((Widget) m_widgetArray[n],
313 XmNwidth, &selectionWidth,
314 XmNheight, &selectionHeight,
315 NULL);
316 #endif
317 Widget label = XmOptionButtonGadget ((Widget) m_buttonWidget);
318 XtVaSetValues (label,
319 XmNlabelString, text(),
320 NULL);
321 #if 0
322 XtVaSetValues ((Widget) m_buttonWidget,
323 XmNwidth, selectionWidth, XmNheight, selectionHeight,
324 XmNmenuHistory, (Widget) m_widgetArray[n], NULL);
325 #endif
326 }
327 m_inSetValue = FALSE;
328 }
329
330 int wxChoice::FindString(const wxString& s) const
331 {
332 int i = 0;
333 for (wxStringList::compatibility_iterator node = m_stringList.GetFirst();
334 node; node = node->GetNext ())
335 {
336 if (s == node->GetData())
337 return i;
338
339 i++;
340 }
341
342 return -1;
343 }
344
345 wxString wxChoice::GetString(int n) const
346 {
347 wxStringList::compatibility_iterator node = m_stringList.Item(n);
348 if (node)
349 return node->GetData();
350 else
351 return wxEmptyString;
352 }
353
354 void wxChoice::SetColumns(int n)
355 {
356 if (n<1) n = 1 ;
357
358 short numColumns = n ;
359 Arg args[3];
360
361 XtSetArg(args[0], XmNnumColumns, numColumns);
362 XtSetArg(args[1], XmNpacking, XmPACK_COLUMN);
363 XtSetValues((Widget) m_menuWidget,args,2) ;
364 }
365
366 int wxChoice::GetColumns(void) const
367 {
368 short numColumns ;
369
370 XtVaGetValues((Widget) m_menuWidget,XmNnumColumns,&numColumns,NULL) ;
371 return numColumns ;
372 }
373
374 void wxChoice::SetFocus()
375 {
376 XmProcessTraversal(XtParent((Widget)m_mainWidget), XmTRAVERSE_CURRENT);
377 }
378
379 void wxChoice::DoSetSize(int x, int y, int width, int height, int sizeFlags)
380 {
381 XtVaSetValues((Widget) m_formWidget, XmNresizePolicy, XmRESIZE_ANY, NULL);
382 bool managed = XtIsManaged((Widget) m_formWidget);
383
384 if (managed)
385 XtUnmanageChild ((Widget) m_formWidget);
386
387 int actualWidth = width - WIDTH_OVERHEAD_SUBTRACT,
388 actualHeight = height - HEIGHT_OVERHEAD;
389
390 if (width > -1)
391 {
392 size_t i;
393 for (i = 0; i < m_noStrings; i++)
394 XtVaSetValues ((Widget) m_widgetArray[i],
395 XmNwidth, actualWidth,
396 NULL);
397 XtVaSetValues ((Widget) m_buttonWidget, XmNwidth, actualWidth,
398 NULL);
399 }
400 if (height > -1)
401 {
402 #if 0
403 size_t i;
404 for (i = 0; i < m_noStrings; i++)
405 XtVaSetValues ((Widget) m_widgetArray[i],
406 XmNheight, actualHeight,
407 NULL);
408 #endif
409 XtVaSetValues ((Widget) m_buttonWidget, XmNheight, actualHeight,
410 NULL);
411 }
412
413 if (managed)
414 XtManageChild ((Widget) m_formWidget);
415 XtVaSetValues((Widget) m_formWidget, XmNresizePolicy, XmRESIZE_NONE, NULL);
416
417 wxControl::DoSetSize (x, y, width, height, sizeFlags);
418 }
419
420 void wxChoice::Command(wxCommandEvent & event)
421 {
422 SetSelection (event.GetInt());
423 ProcessCommand (event);
424 }
425
426 void wxChoiceCallback (Widget w, XtPointer clientData, XtPointer WXUNUSED(ptr))
427 {
428 wxChoice *item = (wxChoice *) clientData;
429 if (item)
430 {
431 if (item->InSetValue())
432 return;
433
434 int n = item->GetWidgets().Index(w);
435 if (n != wxNOT_FOUND)
436 {
437 wxCommandEvent event(wxEVT_COMMAND_CHOICE_SELECTED, item->GetId());
438 event.SetEventObject(item);
439 event.m_commandInt = n;
440 event.m_commandString = item->GetStrings().Item(n)->GetData();
441 if ( item->HasClientObjectData() )
442 event.SetClientObject( item->GetClientObject(n) );
443 else if ( item->HasClientUntypedData() )
444 event.SetClientData( item->GetClientData(n) );
445 item->ProcessCommand (event);
446 }
447 }
448 }
449
450 void wxChoice::ChangeFont(bool keepOriginalSize)
451 {
452 // Note that this causes the widget to be resized back
453 // to its original size! We therefore have to set the size
454 // back again. TODO: a better way in Motif?
455 if (m_font.Ok())
456 {
457 int width, height, width1, height1;
458 GetSize(& width, & height);
459
460 WXFontType fontType =
461 m_font.GetFontType(XtDisplay((Widget) m_mainWidget));
462 WXString fontTag = wxFont::GetFontTag();
463
464 XtVaSetValues ((Widget) m_formWidget, fontTag, fontType, NULL);
465 XtVaSetValues ((Widget) m_buttonWidget, fontTag, fontType, NULL);
466
467 for( size_t i = 0; i < m_noStrings; ++i )
468 XtVaSetValues( (Widget)m_widgetArray[i],
469 fontTag, fontType,
470 NULL );
471
472 GetSize(& width1, & height1);
473 if (keepOriginalSize && (width != width1 || height != height1))
474 {
475 SetSize(-1, -1, width, height);
476 }
477 }
478 }
479
480 void wxChoice::ChangeBackgroundColour()
481 {
482 wxDoChangeBackgroundColour(m_formWidget, m_backgroundColour);
483 wxDoChangeBackgroundColour(m_buttonWidget, m_backgroundColour);
484 wxDoChangeBackgroundColour(m_menuWidget, m_backgroundColour);
485 size_t i;
486 for (i = 0; i < m_noStrings; i++)
487 wxDoChangeBackgroundColour(m_widgetArray[i], m_backgroundColour);
488 }
489
490 void wxChoice::ChangeForegroundColour()
491 {
492 wxDoChangeForegroundColour(m_formWidget, m_foregroundColour);
493 wxDoChangeForegroundColour(m_buttonWidget, m_foregroundColour);
494 wxDoChangeForegroundColour(m_menuWidget, m_foregroundColour);
495 size_t i;
496 for (i = 0; i < m_noStrings; i++)
497 wxDoChangeForegroundColour(m_widgetArray[i], m_foregroundColour);
498 }
499
500 int wxChoice::GetCount() const
501 {
502 return m_noStrings;
503 }
504
505 void wxChoice::DoSetItemClientData(int n, void* clientData)
506 {
507 m_clientDataDict.Set(n, (wxClientData*)clientData, FALSE);
508 }
509
510 void* wxChoice::DoGetItemClientData(int n) const
511 {
512 return (void*)m_clientDataDict.Get(n);
513 }
514
515 void wxChoice::DoSetItemClientObject(int n, wxClientData* clientData)
516 {
517 // don't delete, wxItemContainer does that for us
518 m_clientDataDict.Set(n, clientData, FALSE);
519 }
520
521 wxClientData* wxChoice::DoGetItemClientObject(int n) const
522 {
523 return m_clientDataDict.Get(n);
524 }
525
526 void wxChoice::SetString(int WXUNUSED(n), const wxString& WXUNUSED(s))
527 {
528 wxFAIL_MSG( wxT("wxChoice::SetString not implemented") );
529 }
530
531 wxSize wxChoice::GetItemsSize() const
532 {
533 int x, y, mx = 0, my = 0;
534
535 // get my
536 GetTextExtent( "|", &x, &my );
537
538 wxStringList::compatibility_iterator curr = m_stringList.GetFirst();
539 while( curr )
540 {
541 GetTextExtent( curr->GetData(), &x, &y );
542 mx = wxMax( mx, x );
543 my = wxMax( my, y );
544 curr = curr->GetNext();
545 }
546
547 return wxSize( mx, my );
548 }
549
550 wxSize wxChoice::DoGetBestSize() const
551 {
552 wxSize items = GetItemsSize();
553 // FIXME arbitrary constants
554 return wxSize( ( items.x ? items.x + WIDTH_OVERHEAD : 120 ),
555 items.y + HEIGHT_OVERHEAD );
556 }