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