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