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