Rework wxMotif font/color inheritance so it works
[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 #include "wx/arrstr.h"
27 #endif
28
29 #ifdef __VMS__
30 #pragma message disable nosimpint
31 #endif
32 #include <Xm/Xm.h>
33 #include <Xm/PushBG.h>
34 #include <Xm/PushB.h>
35 #include <Xm/RowColumn.h>
36 #ifdef __VMS__
37 #pragma message enable nosimpint
38 #endif
39
40 #include "wx/motif/private.h"
41
42 #define WIDTH_OVERHEAD 48
43 #define WIDTH_OVERHEAD_SUBTRACT 40
44 #define HEIGHT_OVERHEAD 15
45
46 IMPLEMENT_DYNAMIC_CLASS(wxChoice, wxControl)
47
48 void wxChoiceCallback (Widget w, XtPointer clientData,
49 XtPointer ptr);
50
51 wxChoice::wxChoice()
52 {
53 Init();
54 }
55
56 void wxChoice::Init()
57 {
58 m_noStrings = 0;
59 m_buttonWidget = (WXWidget) 0;
60 m_menuWidget = (WXWidget) 0;
61 m_formWidget = (WXWidget) 0;
62 }
63
64 bool wxChoice::Create(wxWindow *parent, wxWindowID id,
65 const wxPoint& pos,
66 const wxSize& size,
67 int n, const wxString choices[],
68 long style,
69 const wxValidator& validator,
70 const wxString& name)
71 {
72 if ( !CreateControl(parent, id, pos, size, style, validator, name) )
73 return false;
74 PreCreation();
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 PostCreation();
143 AttachWidget (parent, m_buttonWidget, m_formWidget,
144 pos.x, pos.y, bestSize.x, bestSize.y);
145
146 return true;
147 }
148
149 bool wxChoice::Create(wxWindow *parent, wxWindowID id,
150 const wxPoint& pos,
151 const wxSize& size,
152 const wxArrayString& choices,
153 long style,
154 const wxValidator& validator,
155 const wxString& name)
156 {
157 wxCArrayString chs(choices);
158 return Create(parent, id, pos, size, chs.GetCount(), chs.GetStrings(),
159 style, validator, name);
160 }
161
162 wxChoice::~wxChoice()
163 {
164 // For some reason destroying the menuWidget
165 // can cause crashes on some machines. It will
166 // be deleted implicitly by deleting the parent form
167 // anyway.
168 // XtDestroyWidget (menuWidget);
169
170 if (GetMainWidget())
171 {
172 DetachWidget(GetMainWidget()); // Removes event handlers
173 DetachWidget(m_formWidget);
174
175 XtDestroyWidget((Widget) m_formWidget);
176 m_formWidget = (WXWidget) 0;
177
178 // Presumably the other widgets have been deleted now, via the form
179 m_mainWidget = (WXWidget) 0;
180 m_buttonWidget = (WXWidget) 0;
181 }
182 if ( HasClientObjectData() )
183 m_clientDataDict.DestroyData();
184 }
185
186 static inline wxChar* MYcopystring(const wxChar* s)
187 {
188 wxChar* copy = new wxChar[wxStrlen(s) + 1];
189 return wxStrcpy(copy, s);
190 }
191
192 int wxChoice::DoInsert(const wxString& item, unsigned int pos)
193 {
194 #ifndef XmNpositionIndex
195 wxCHECK_MSG( pos == GetCount(), -1, wxT("insert not implemented"));
196 #endif
197 Widget w = XtVaCreateManagedWidget (GetLabelText(item),
198 #if wxUSE_GADGETS
199 xmPushButtonGadgetClass, (Widget) m_menuWidget,
200 #else
201 xmPushButtonWidgetClass, (Widget) m_menuWidget,
202 #endif
203 #ifdef XmNpositionIndex
204 XmNpositionIndex, pos,
205 #endif
206 NULL);
207
208 wxDoChangeBackgroundColour((WXWidget) w, m_backgroundColour);
209
210 if( m_font.Ok() )
211 wxDoChangeFont( w, m_font );
212
213 m_widgetArray.Insert(w, pos);
214
215 char mnem = wxFindMnemonic (item);
216 if (mnem != 0)
217 XtVaSetValues (w, XmNmnemonic, mnem, NULL);
218
219 XtAddCallback (w, XmNactivateCallback,
220 (XtCallbackProc) wxChoiceCallback,
221 (XtPointer) this);
222
223 if (m_noStrings == 0 && m_buttonWidget)
224 {
225 XtVaSetValues ((Widget) m_buttonWidget, XmNmenuHistory, w, NULL);
226 Widget label = XmOptionButtonGadget ((Widget) m_buttonWidget);
227 wxXmString text( item );
228 XtVaSetValues (label,
229 XmNlabelString, text(),
230 NULL);
231 }
232 // need to ditch wxStringList for wxArrayString
233 m_stringList.Insert(pos, MYcopystring(item));
234 m_noStrings ++;
235
236 return pos;
237 }
238
239 int wxChoice::DoAppend(const wxString& item)
240 {
241 return DoInsert(item, GetCount());
242 }
243
244 void wxChoice::Delete(unsigned int n)
245 {
246 Widget w = (Widget)m_widgetArray[n];
247 XtRemoveCallback(w, XmNactivateCallback, (XtCallbackProc)wxChoiceCallback,
248 (XtPointer)this);
249 m_stringList.Erase(m_stringList.Item(n));
250 m_widgetArray.RemoveAt(size_t(n));
251 m_clientDataDict.Delete(n, HasClientObjectData());
252
253 XtDestroyWidget(w);
254 m_noStrings--;
255 }
256
257 void wxChoice::Clear()
258 {
259 m_stringList.Clear ();
260 unsigned int i;
261 for (i = 0; i < m_noStrings; i++)
262 {
263 XtRemoveCallback((Widget) m_widgetArray[i],
264 XmNactivateCallback, (XtCallbackProc)wxChoiceCallback,
265 (XtPointer)this);
266 XtUnmanageChild ((Widget) m_widgetArray[i]);
267 XtDestroyWidget ((Widget) m_widgetArray[i]);
268 }
269 m_widgetArray.Clear();
270 if (m_buttonWidget)
271 XtVaSetValues ((Widget) m_buttonWidget,
272 XmNmenuHistory, (Widget) NULL,
273 NULL);
274
275 if ( HasClientObjectData() )
276 m_clientDataDict.DestroyData();
277
278 m_noStrings = 0;
279 }
280
281 int wxChoice::GetSelection() const
282 {
283 XmString text;
284 Widget label = XmOptionButtonGadget ((Widget) m_buttonWidget);
285 XtVaGetValues (label,
286 XmNlabelString, &text,
287 NULL);
288 wxXmString freeMe(text);
289 wxString s = wxXmStringToString( text );
290
291 if (!s.empty())
292 {
293 int i = 0;
294 for (wxStringList::compatibility_iterator node = m_stringList.GetFirst ();
295 node; node = node->GetNext ())
296 {
297 if (wxStrcmp(node->GetData(), s.c_str()) == 0)
298 {
299 return i;
300 }
301 else
302 i++;
303 } // for()
304
305 return -1;
306 }
307 return -1;
308 }
309
310 void wxChoice::SetSelection(int n)
311 {
312 m_inSetValue = true;
313
314 wxStringList::compatibility_iterator node = m_stringList.Item(n);
315 if (node)
316 {
317 #if 0
318 Dimension selectionWidth, selectionHeight;
319 #endif
320 wxXmString text( node->GetData() );
321 // MBN: this seems silly, at best, and causes wxChoices to be clipped:
322 // will remove "soon"
323 #if 0
324 XtVaGetValues ((Widget) m_widgetArray[n],
325 XmNwidth, &selectionWidth,
326 XmNheight, &selectionHeight,
327 NULL);
328 #endif
329 Widget label = XmOptionButtonGadget ((Widget) m_buttonWidget);
330 XtVaSetValues (label,
331 XmNlabelString, text(),
332 NULL);
333 #if 0
334 XtVaSetValues ((Widget) m_buttonWidget,
335 XmNwidth, selectionWidth, XmNheight, selectionHeight,
336 XmNmenuHistory, (Widget) m_widgetArray[n], NULL);
337 #endif
338 }
339 m_inSetValue = false;
340 }
341
342 wxString wxChoice::GetString(unsigned int n) const
343 {
344 wxStringList::compatibility_iterator node = m_stringList.Item(n);
345 if (node)
346 return node->GetData();
347 else
348 return wxEmptyString;
349 }
350
351 void wxChoice::SetColumns(int n)
352 {
353 if (n<1) n = 1 ;
354
355 short numColumns = (short)n ;
356 Arg args[3];
357
358 XtSetArg(args[0], XmNnumColumns, numColumns);
359 XtSetArg(args[1], XmNpacking, XmPACK_COLUMN);
360 XtSetValues((Widget) m_menuWidget,args,2) ;
361 }
362
363 int wxChoice::GetColumns(void) const
364 {
365 short numColumns ;
366
367 XtVaGetValues((Widget) m_menuWidget,XmNnumColumns,&numColumns,NULL) ;
368 return numColumns ;
369 }
370
371 void wxChoice::SetFocus()
372 {
373 XmProcessTraversal(XtParent((Widget)m_mainWidget), XmTRAVERSE_CURRENT);
374 }
375
376 void wxChoice::DoSetSize(int x, int y, int width, int height, int sizeFlags)
377 {
378 XtVaSetValues((Widget) m_formWidget, XmNresizePolicy, XmRESIZE_ANY, NULL);
379 bool managed = XtIsManaged((Widget) m_formWidget);
380
381 if (managed)
382 XtUnmanageChild ((Widget) m_formWidget);
383
384 int actualWidth = width - WIDTH_OVERHEAD_SUBTRACT,
385 actualHeight = height - HEIGHT_OVERHEAD;
386
387 if (width > -1)
388 {
389 unsigned int i;
390 for (i = 0; i < m_noStrings; i++)
391 XtVaSetValues ((Widget) m_widgetArray[i],
392 XmNwidth, actualWidth,
393 NULL);
394 XtVaSetValues ((Widget) m_buttonWidget, XmNwidth, actualWidth,
395 NULL);
396 }
397 if (height > -1)
398 {
399 #if 0
400 unsigned int i;
401 for (i = 0; i < m_noStrings; i++)
402 XtVaSetValues ((Widget) m_widgetArray[i],
403 XmNheight, actualHeight,
404 NULL);
405 #endif
406 XtVaSetValues ((Widget) m_buttonWidget, XmNheight, actualHeight,
407 NULL);
408 }
409
410 if (managed)
411 XtManageChild ((Widget) m_formWidget);
412 XtVaSetValues((Widget) m_formWidget, XmNresizePolicy, XmRESIZE_NONE, NULL);
413
414 wxControl::DoSetSize (x, y, width, height, sizeFlags);
415 }
416
417 void wxChoice::Command(wxCommandEvent & event)
418 {
419 SetSelection (event.GetInt());
420 ProcessCommand (event);
421 }
422
423 void wxChoiceCallback (Widget w, XtPointer clientData, XtPointer WXUNUSED(ptr))
424 {
425 wxChoice *item = (wxChoice *) clientData;
426 if (item)
427 {
428 if (item->InSetValue())
429 return;
430
431 int n = item->GetWidgets().Index(w);
432 if (n != wxNOT_FOUND)
433 {
434 wxCommandEvent event(wxEVT_COMMAND_CHOICE_SELECTED, item->GetId());
435 event.SetEventObject(item);
436 event.SetInt(n);
437 event.SetString( item->GetStrings().Item(n)->GetData() );
438 if ( item->HasClientObjectData() )
439 event.SetClientObject( item->GetClientObject(n) );
440 else if ( item->HasClientUntypedData() )
441 event.SetClientData( item->GetClientData(n) );
442 item->ProcessCommand (event);
443 }
444 }
445 }
446
447 void wxChoice::ChangeFont(bool keepOriginalSize)
448 {
449 // Note that this causes the widget to be resized back
450 // to its original size! We therefore have to set the size
451 // back again. TODO: a better way in Motif?
452 if (m_mainWidget && m_font.Ok())
453 {
454 Display* dpy = XtDisplay((Widget) m_mainWidget);
455 int width, height, width1, height1;
456 GetSize(& width, & height);
457
458 WXString fontTag = wxFont::GetFontTag();
459
460 XtVaSetValues ((Widget) m_formWidget,
461 fontTag, m_font.GetFontTypeC(dpy),
462 NULL);
463 XtVaSetValues ((Widget) m_buttonWidget,
464 fontTag, m_font.GetFontTypeC(dpy),
465 NULL);
466
467 for( unsigned int i = 0; i < m_noStrings; ++i )
468 XtVaSetValues( (Widget)m_widgetArray[i],
469 fontTag, m_font.GetFontTypeC(dpy),
470 NULL );
471
472 GetSize(& width1, & height1);
473 if (keepOriginalSize && (width != width1 || height != height1))
474 {
475 SetSize(wxDefaultCoord, wxDefaultCoord, 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 unsigned int 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 unsigned int i;
496 for (i = 0; i < m_noStrings; i++)
497 wxDoChangeForegroundColour(m_widgetArray[i], m_foregroundColour);
498 }
499
500 unsigned int wxChoice::GetCount() const
501 {
502 return m_noStrings;
503 }
504
505 void wxChoice::DoSetItemClientData(unsigned int n, void* clientData)
506 {
507 m_clientDataDict.Set(n, (wxClientData*)clientData, false);
508 }
509
510 void* wxChoice::DoGetItemClientData(unsigned int n) const
511 {
512 return (void*)m_clientDataDict.Get(n);
513 }
514
515 void wxChoice::DoSetItemClientObject(unsigned 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(unsigned int n) const
522 {
523 return m_clientDataDict.Get(n);
524 }
525
526 void wxChoice::SetString(unsigned 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 }
557
558 #endif // wxUSE_CHOICE