]> git.saurik.com Git - wxWidgets.git/blame - src/motif/choice.cpp
Fix docs for Bind
[wxWidgets.git] / src / motif / choice.cpp
CommitLineData
4bb6408c
JS
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
14f355c2 12#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
4bb6408c
JS
13#pragma implementation "choice.h"
14#endif
15
1248b41f
MB
16// For compilers that support precompilation, includes "wx.h".
17#include "wx/wxprec.h"
18
bcd055ae 19#ifdef __VMS
4dff3400
JJ
20#define XtDisplay XTDISPLAY
21#define XtParent XTPARENT
22#endif
23
f6045f99
GD
24#include "wx/defs.h"
25
4bb6408c 26#include "wx/choice.h"
f97c9854 27#include "wx/utils.h"
584ad2a3 28#include "wx/arrstr.h"
f97c9854 29
338dd992
JJ
30#ifdef __VMS__
31#pragma message disable nosimpint
32#endif
f97c9854
JS
33#include <Xm/Xm.h>
34#include <Xm/PushBG.h>
35#include <Xm/PushB.h>
36#include <Xm/RowColumn.h>
338dd992
JJ
37#ifdef __VMS__
38#pragma message enable nosimpint
39#endif
f97c9854
JS
40
41#include "wx/motif/private.h"
4bb6408c 42
3a73cc52
MB
43#define WIDTH_OVERHEAD 48
44#define WIDTH_OVERHEAD_SUBTRACT 40
45#define HEIGHT_OVERHEAD 15
46
4bb6408c 47IMPLEMENT_DYNAMIC_CLASS(wxChoice, wxControl)
4bb6408c 48
f97c9854 49void wxChoiceCallback (Widget w, XtPointer clientData,
2d120f83 50 XtPointer ptr);
f97c9854
JS
51
52wxChoice::wxChoice()
53{
ec75d791
MB
54 Init();
55}
56
57void wxChoice::Init()
58{
f97c9854
JS
59 m_noStrings = 0;
60 m_buttonWidget = (WXWidget) 0;
61 m_menuWidget = (WXWidget) 0;
f97c9854 62 m_formWidget = (WXWidget) 0;
f97c9854
JS
63}
64
4bb6408c 65bool wxChoice::Create(wxWindow *parent, wxWindowID id,
2d120f83
JS
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)
4bb6408c 72{
ec75d791
MB
73 if ( !CreateControl(parent, id, pos, size, style, validator, name) )
74 return FALSE;
31528cd3 75
f97c9854 76 Widget parentWidget = (Widget) parent->GetClientWidget();
31528cd3
VZ
77
78 m_formWidget = (WXWidget) XtVaCreateManagedWidget(name.c_str(),
2d120f83
JS
79 xmRowColumnWidgetClass, parentWidget,
80 XmNmarginHeight, 0,
81 XmNmarginWidth, 0,
82 XmNpacking, XmPACK_TIGHT,
83 XmNorientation, XmHORIZONTAL,
3a73cc52
MB
84 XmNresizeWidth, False,
85 XmNresizeHeight, False,
2d120f83 86 NULL);
31528cd3 87
f97c9854 88 XtVaSetValues ((Widget) m_formWidget, XmNspacing, 0, NULL);
31528cd3 89
2d120f83 90 /*
f97c9854
JS
91 * Create the popup menu
92 */
ec75d791
MB
93 m_menuWidget = (WXWidget) XmCreatePulldownMenu ((Widget) m_formWidget,
94 "choiceMenu", NULL, 0);
31528cd3 95
f97c9854
JS
96 if (n > 0)
97 {
98 int i;
99 for (i = 0; i < n; i++)
100 Append (choices[i]);
101 }
31528cd3 102
2d120f83 103 /*
f97c9854
JS
104 * Create button
105 */
106 Arg args[10];
107 Cardinal argcnt = 0;
31528cd3 108
ec75d791
MB
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);
31528cd3 116
f97c9854 117 m_mainWidget = m_buttonWidget;
31528cd3 118
f97c9854 119 XtManageChild ((Widget) m_buttonWidget);
9838df2c 120
f97c9854
JS
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
9838df2c
JS
127 // JACS, 24/1/99: this seems to cause a malloc crash later on, e.g.
128 // in controls sample.
dfe1eee3
VZ
129 //
130 // Widget optionLabel = XmOptionLabelGadget ((Widget) m_buttonWidget);
131 // XtUnmanageChild (optionLabel);
f97c9854
JS
132#endif
133#endif
9838df2c 134
a29ee706
MB
135 wxSize bestSize = GetBestSize();
136 if( size.x > 0 ) bestSize.x = size.x;
137 if( size.y > 0 ) bestSize.y = size.y;
138
f97c9854 139 XtVaSetValues((Widget) m_formWidget, XmNresizePolicy, XmRESIZE_NONE, NULL);
31528cd3 140
4b5f3fe6 141 ChangeFont(FALSE);
3a73cc52 142 ChangeBackgroundColour();
9838df2c 143
ec75d791 144 AttachWidget (parent, m_buttonWidget, m_formWidget,
a29ee706 145 pos.x, pos.y, bestSize.x, bestSize.y);
31528cd3 146
f97c9854
JS
147 return TRUE;
148}
149
584ad2a3
MB
150bool 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
f97c9854
JS
163wxChoice::~wxChoice()
164{
2d120f83
JS
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);
31528cd3 170
8aa04e8b
JS
171 if (GetMainWidget())
172 {
173 DetachWidget(GetMainWidget()); // Removes event handlers
b412f9be 174 DetachWidget(m_formWidget);
31528cd3 175
8aa04e8b
JS
176 XtDestroyWidget((Widget) m_formWidget);
177 m_formWidget = (WXWidget) 0;
31528cd3 178
8aa04e8b
JS
179 // Presumably the other widgets have been deleted now, via the form
180 m_mainWidget = (WXWidget) 0;
181 m_buttonWidget = (WXWidget) 0;
182 }
ec75d791
MB
183 if ( HasClientObjectData() )
184 m_clientDataDict.DestroyData();
4bb6408c
JS
185}
186
c33c81c3 187int wxChoice::DoAppend(const wxString& item)
4bb6408c 188{
31528cd3 189 Widget w = XtVaCreateManagedWidget (wxStripMenuCodes(item),
f97c9854 190#if USE_GADGETS
2d120f83 191 xmPushButtonGadgetClass, (Widget) m_menuWidget,
f97c9854 192#else
2d120f83 193 xmPushButtonWidgetClass, (Widget) m_menuWidget,
f97c9854 194#endif
2d120f83 195 NULL);
31528cd3 196
a8680e3e 197 wxDoChangeBackgroundColour((WXWidget) w, m_backgroundColour);
31528cd3 198
e1aae528
MB
199 if( m_font.Ok() )
200 wxDoChangeFont( w, m_font );
31528cd3 201
ec75d791 202 m_widgetArray.Add(w);
31528cd3 203
3a73cc52 204 char mnem = wxFindMnemonic (item);
f6bcfd97
BP
205 if (mnem != 0)
206 XtVaSetValues (w, XmNmnemonic, mnem, NULL);
31528cd3 207
ec75d791
MB
208 XtAddCallback (w, XmNactivateCallback,
209 (XtCallbackProc) wxChoiceCallback,
210 (XtPointer) this);
31528cd3 211
f6bcfd97
BP
212 if (m_noStrings == 0 && m_buttonWidget)
213 {
214 XtVaSetValues ((Widget) m_buttonWidget, XmNmenuHistory, w, NULL);
215 Widget label = XmOptionButtonGadget ((Widget) m_buttonWidget);
c13c9657 216 wxXmString text( item );
f6bcfd97 217 XtVaSetValues (label,
c13c9657 218 XmNlabelString, text(),
f6bcfd97 219 NULL);
f6bcfd97 220 }
ec75d791 221 m_stringList.Add(item);
f6bcfd97 222 m_noStrings ++;
c33c81c3 223
ec75d791 224 return GetCount() - 1;
4bb6408c
JS
225}
226
243dbf1a
VZ
227int 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
ec75d791 235void wxChoice::Delete(int n)
4bb6408c 236{
ec75d791
MB
237 Widget w = (Widget)m_widgetArray[n];
238 XtRemoveCallback(w, XmNactivateCallback, (XtCallbackProc)wxChoiceCallback,
239 (XtPointer)this);
ac32ba44 240 m_stringList.Erase(m_stringList.Item(n));
ec75d791
MB
241 m_widgetArray.RemoveAt(size_t(n));
242 m_clientDataDict.Delete(n, HasClientObjectData());
31528cd3 243
ec75d791 244 XtDestroyWidget(w);
4bb6408c
JS
245 m_noStrings --;
246}
247
248void wxChoice::Clear()
249{
f97c9854 250 m_stringList.Clear ();
fd304d98 251 size_t i;
f97c9854
JS
252 for (i = 0; i < m_noStrings; i++)
253 {
ec75d791
MB
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]);
f97c9854 259 }
ec75d791 260 m_widgetArray.Clear();
f97c9854 261 if (m_buttonWidget)
ec75d791
MB
262 XtVaSetValues ((Widget) m_buttonWidget,
263 XmNmenuHistory, (Widget) NULL,
264 NULL);
f6bcfd97
BP
265
266 if ( HasClientObjectData() )
ec75d791 267 m_clientDataDict.DestroyData();
f6bcfd97 268
4bb6408c
JS
269 m_noStrings = 0;
270}
271
272int wxChoice::GetSelection() const
273{
2d120f83 274 XmString text;
2d120f83
JS
275 Widget label = XmOptionButtonGadget ((Widget) m_buttonWidget);
276 XtVaGetValues (label,
277 XmNlabelString, &text,
278 NULL);
da494b40
MB
279 wxXmString freeMe(text);
280 wxString s = wxXmStringToString( text );
31528cd3 281
da494b40 282 if (!s.IsEmpty())
2d120f83
JS
283 {
284 int i = 0;
ac32ba44 285 for (wxStringList::compatibility_iterator node = m_stringList.GetFirst ();
ec75d791 286 node; node = node->GetNext ())
f97c9854 287 {
da494b40 288 if (wxStrcmp(node->GetData(), s.c_str()) == 0)
2d120f83 289 {
2d120f83
JS
290 return i;
291 }
292 else
293 i++;
294 } // for()
31528cd3 295
2d120f83
JS
296 return -1;
297 }
2d120f83 298 return -1;
4bb6408c
JS
299}
300
301void wxChoice::SetSelection(int n)
302{
2d120f83 303 m_inSetValue = TRUE;
31528cd3 304
ac32ba44 305 wxStringList::compatibility_iterator node = m_stringList.Item(n);
2d120f83 306 if (node)
f97c9854 307 {
ec75d791 308#if 0
2d120f83 309 Dimension selectionWidth, selectionHeight;
ec75d791 310#endif
fd304d98 311 wxXmString text( node->GetData() );
ec75d791
MB
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
2d120f83
JS
320 Widget label = XmOptionButtonGadget ((Widget) m_buttonWidget);
321 XtVaSetValues (label,
ec75d791 322 XmNlabelString, text(),
2d120f83 323 NULL);
ec75d791 324#if 0
2d120f83
JS
325 XtVaSetValues ((Widget) m_buttonWidget,
326 XmNwidth, selectionWidth, XmNheight, selectionHeight,
ec75d791
MB
327 XmNmenuHistory, (Widget) m_widgetArray[n], NULL);
328#endif
f97c9854 329 }
2d120f83 330 m_inSetValue = FALSE;
4bb6408c
JS
331}
332
333int wxChoice::FindString(const wxString& s) const
334{
f97c9854 335 int i = 0;
ac32ba44 336 for (wxStringList::compatibility_iterator node = m_stringList.GetFirst();
ec75d791 337 node; node = node->GetNext ())
f97c9854 338 {
ec75d791 339 if (s == node->GetData())
f97c9854 340 return i;
ec75d791
MB
341
342 i++;
f97c9854 343 }
ec75d791 344
f97c9854 345 return -1;
4bb6408c
JS
346}
347
348wxString wxChoice::GetString(int n) const
349{
ac32ba44 350 wxStringList::compatibility_iterator node = m_stringList.Item(n);
2d120f83 351 if (node)
ec75d791 352 return node->GetData();
2d120f83
JS
353 else
354 return wxEmptyString;
f97c9854
JS
355}
356
357void wxChoice::SetColumns(int n)
358{
2d120f83 359 if (n<1) n = 1 ;
31528cd3 360
2d120f83
JS
361 short numColumns = n ;
362 Arg args[3];
31528cd3 363
2d120f83
JS
364 XtSetArg(args[0], XmNnumColumns, numColumns);
365 XtSetArg(args[1], XmNpacking, XmPACK_COLUMN);
366 XtSetValues((Widget) m_menuWidget,args,2) ;
f97c9854
JS
367}
368
369int wxChoice::GetColumns(void) const
370{
2d120f83 371 short numColumns ;
31528cd3 372
2d120f83
JS
373 XtVaGetValues((Widget) m_menuWidget,XmNnumColumns,&numColumns,NULL) ;
374 return numColumns ;
f97c9854
JS
375}
376
377void wxChoice::SetFocus()
378{
2d120f83 379 XmProcessTraversal(XtParent((Widget)m_mainWidget), XmTRAVERSE_CURRENT);
4bb6408c
JS
380}
381
bfc6fde4 382void wxChoice::DoSetSize(int x, int y, int width, int height, int sizeFlags)
4bb6408c 383{
f97c9854
JS
384 XtVaSetValues((Widget) m_formWidget, XmNresizePolicy, XmRESIZE_ANY, NULL);
385 bool managed = XtIsManaged((Widget) m_formWidget);
31528cd3 386
f97c9854
JS
387 if (managed)
388 XtUnmanageChild ((Widget) m_formWidget);
31528cd3 389
3a73cc52
MB
390 int actualWidth = width - WIDTH_OVERHEAD_SUBTRACT,
391 actualHeight = height - HEIGHT_OVERHEAD;
31528cd3 392
f97c9854
JS
393 if (width > -1)
394 {
fd304d98 395 size_t i;
f97c9854 396 for (i = 0; i < m_noStrings; i++)
ec75d791
MB
397 XtVaSetValues ((Widget) m_widgetArray[i],
398 XmNwidth, actualWidth,
399 NULL);
f97c9854 400 XtVaSetValues ((Widget) m_buttonWidget, XmNwidth, actualWidth,
2d120f83 401 NULL);
f97c9854
JS
402 }
403 if (height > -1)
404 {
3a73cc52 405#if 0
fd304d98 406 size_t i;
f97c9854 407 for (i = 0; i < m_noStrings; i++)
ec75d791
MB
408 XtVaSetValues ((Widget) m_widgetArray[i],
409 XmNheight, actualHeight,
410 NULL);
3a73cc52 411#endif
f97c9854 412 XtVaSetValues ((Widget) m_buttonWidget, XmNheight, actualHeight,
2d120f83 413 NULL);
f97c9854 414 }
31528cd3 415
f97c9854
JS
416 if (managed)
417 XtManageChild ((Widget) m_formWidget);
418 XtVaSetValues((Widget) m_formWidget, XmNresizePolicy, XmRESIZE_NONE, NULL);
31528cd3 419
bfc6fde4 420 wxControl::DoSetSize (x, y, width, height, sizeFlags);
4bb6408c
JS
421}
422
4bb6408c
JS
423void wxChoice::Command(wxCommandEvent & event)
424{
425 SetSelection (event.GetInt());
426 ProcessCommand (event);
427}
428
f9e02ac7 429void wxChoiceCallback (Widget w, XtPointer clientData, XtPointer WXUNUSED(ptr))
f97c9854
JS
430{
431 wxChoice *item = (wxChoice *) clientData;
432 if (item)
433 {
a4294b78 434 if (item->InSetValue())
f97c9854 435 return;
31528cd3 436
ec75d791
MB
437 int n = item->GetWidgets().Index(w);
438 if (n != wxNOT_FOUND)
f97c9854 439 {
ec75d791 440 wxCommandEvent event(wxEVT_COMMAND_CHOICE_SELECTED, item->GetId());
55acd85e 441 event.SetEventObject(item);
ec75d791
MB
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) );
f97c9854
JS
448 item->ProcessCommand (event);
449 }
450 }
451}
452
4b5f3fe6 453void wxChoice::ChangeFont(bool keepOriginalSize)
0d57be45 454{
321db4b6
JS
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?
da175b2c 458 if (m_font.Ok())
321db4b6
JS
459 {
460 int width, height, width1, height1;
461 GetSize(& width, & height);
31528cd3 462
da494b40
MB
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);
31528cd3 469
ec75d791
MB
470 for( size_t i = 0; i < m_noStrings; ++i )
471 XtVaSetValues( (Widget)m_widgetArray[i],
da494b40 472 fontTag, fontType,
ec75d791
MB
473 NULL );
474
321db4b6 475 GetSize(& width1, & height1);
4b5f3fe6 476 if (keepOriginalSize && (width != width1 || height != height1))
321db4b6
JS
477 {
478 SetSize(-1, -1, width, height);
479 }
480 }
0d57be45
JS
481}
482
483void wxChoice::ChangeBackgroundColour()
484{
a8680e3e
MB
485 wxDoChangeBackgroundColour(m_formWidget, m_backgroundColour);
486 wxDoChangeBackgroundColour(m_buttonWidget, m_backgroundColour);
487 wxDoChangeBackgroundColour(m_menuWidget, m_backgroundColour);
fd304d98 488 size_t i;
321db4b6 489 for (i = 0; i < m_noStrings; i++)
a8680e3e 490 wxDoChangeBackgroundColour(m_widgetArray[i], m_backgroundColour);
0d57be45
JS
491}
492
493void wxChoice::ChangeForegroundColour()
494{
a8680e3e
MB
495 wxDoChangeForegroundColour(m_formWidget, m_foregroundColour);
496 wxDoChangeForegroundColour(m_buttonWidget, m_foregroundColour);
497 wxDoChangeForegroundColour(m_menuWidget, m_foregroundColour);
fd304d98 498 size_t i;
321db4b6 499 for (i = 0; i < m_noStrings; i++)
a8680e3e 500 wxDoChangeForegroundColour(m_widgetArray[i], m_foregroundColour);
0d57be45 501}
6adaedf0 502
6adaedf0
JS
503int wxChoice::GetCount() const
504{
ec75d791 505 return m_noStrings;
6adaedf0
JS
506}
507
ec75d791 508void wxChoice::DoSetItemClientData(int n, void* clientData)
6adaedf0 509{
ec75d791 510 m_clientDataDict.Set(n, (wxClientData*)clientData, FALSE);
6adaedf0
JS
511}
512
ec75d791 513void* wxChoice::DoGetItemClientData(int n) const
6adaedf0 514{
ec75d791 515 return (void*)m_clientDataDict.Get(n);
6adaedf0
JS
516}
517
ec75d791 518void wxChoice::DoSetItemClientObject(int n, wxClientData* clientData)
6adaedf0 519{
ec75d791
MB
520 // don't delete, wxItemContainer does that for us
521 m_clientDataDict.Set(n, clientData, FALSE);
6adaedf0
JS
522}
523
ec75d791 524wxClientData* wxChoice::DoGetItemClientObject(int n) const
6adaedf0 525{
ec75d791 526 return m_clientDataDict.Get(n);
6adaedf0
JS
527}
528
ec75d791 529void wxChoice::SetString(int WXUNUSED(n), const wxString& WXUNUSED(s))
6adaedf0 530{
ec75d791 531 wxFAIL_MSG( wxT("wxChoice::SetString not implemented") );
6adaedf0
JS
532}
533
ec75d791 534wxSize wxChoice::GetItemsSize() const
6adaedf0 535{
ec75d791 536 int x, y, mx = 0, my = 0;
f6bcfd97 537
ec75d791
MB
538 // get my
539 GetTextExtent( "|", &x, &my );
f6bcfd97 540
ac32ba44 541 wxStringList::compatibility_iterator curr = m_stringList.GetFirst();
ec75d791
MB
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 }
6adaedf0 549
ec75d791 550 return wxSize( mx, my );
6adaedf0
JS
551}
552
ec75d791 553wxSize wxChoice::DoGetBestSize() const
6adaedf0 554{
ec75d791
MB
555 wxSize items = GetItemsSize();
556 // FIXME arbitrary constants
3a73cc52
MB
557 return wxSize( ( items.x ? items.x + WIDTH_OVERHEAD : 120 ),
558 items.y + HEIGHT_OVERHEAD );
6adaedf0 559}