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 
wxChoice()43 wxChoice::wxChoice()
44 {
45     Init();
46 }
47 
Init()48 void wxChoice::Init()
49 {
50     m_buttonWidget = (WXWidget) 0;
51     m_menuWidget = (WXWidget) 0;
52     m_formWidget = (WXWidget) 0;
53 }
54 
Create(wxWindow * parent,wxWindowID id,const wxPoint & pos,const wxSize & size,int n,const wxString choices[],long style,const wxValidator & validator,const wxString & name)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 
Create(wxWindow * parent,wxWindowID id,const wxPoint & pos,const wxSize & size,const wxArrayString & choices,long style,const wxValidator & validator,const wxString & name)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 
~wxChoice()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 
MYcopystring(const wxChar * s)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
DoInsertItems(const wxArrayStringsAdapter & items,unsigned int pos,void ** clientData,wxClientDataType type)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 
DoDeleteOneItem(unsigned int n)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 
DoClear()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 
GetSelection() const274 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 
SetSelection(int n)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 
GetString(unsigned int n) const325 wxString wxChoice::GetString(unsigned int n) const
326 {
327     return m_stringArray[n];
328 }
329 
SetColumns(int n)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 
GetColumns(void) const342 int wxChoice::GetColumns(void) const
343 {
344     short numColumns ;
345 
346     XtVaGetValues((Widget) m_menuWidget,XmNnumColumns,&numColumns,NULL) ;
347     return numColumns ;
348 }
349 
SetFocus()350 void wxChoice::SetFocus()
351 {
352     XmProcessTraversal(XtParent((Widget)m_mainWidget), XmTRAVERSE_CURRENT);
353 }
354 
DoSetSize(int x,int y,int width,int height,int sizeFlags)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 
Command(wxCommandEvent & event)396 void wxChoice::Command(wxCommandEvent & event)
397 {
398     SetSelection (event.GetInt());
399     ProcessCommand (event);
400 }
401 
wxChoiceCallback(Widget w,XtPointer clientData,XtPointer WXUNUSED (ptr))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 
ChangeFont(bool keepOriginalSize)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 
ChangeBackgroundColour()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 
ChangeForegroundColour()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 
GetCount() const479 unsigned int wxChoice::GetCount() const
480 {
481     return m_stringArray.GetCount();
482 }
483 
SetString(unsigned int WXUNUSED (n),const wxString & WXUNUSED (s))484 void wxChoice::SetString(unsigned int WXUNUSED(n), const wxString& WXUNUSED(s))
485 {
486     wxFAIL_MSG( wxT("wxChoice::SetString not implemented") );
487 }
488 
GetItemsSize() const489 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 
DoGetBestSize() const506 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
515