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