]> git.saurik.com Git - wxWidgets.git/blame - src/motif/choice.cpp
allow calling SetItemLabel() for items not attached to the menu or menu bar yet ...
[wxWidgets.git] / src / motif / choice.cpp
CommitLineData
4bb6408c 1/////////////////////////////////////////////////////////////////////////////
11e62fe6 2// Name: src/motif/choice.cpp
4bb6408c
JS
3// Purpose: wxChoice
4// Author: Julian Smart
5// Modified by:
6// Created: 17/09/98
7// RCS-ID: $Id$
8// Copyright: (c) Julian Smart
312ebad4 9// Licence: wxWindows licence
4bb6408c
JS
10/////////////////////////////////////////////////////////////////////////////
11
1248b41f
MB
12// For compilers that support precompilation, includes "wx.h".
13#include "wx/wxprec.h"
14
8228b893
WS
15#if wxUSE_CHOICE
16
4bb6408c 17#include "wx/choice.h"
de6185e2
WS
18
19#ifndef WX_PRECOMP
20 #include "wx/utils.h"
aaa6d89a 21 #include "wx/arrstr.h"
de6185e2
WS
22#endif
23
338dd992
JJ
24#ifdef __VMS__
25#pragma message disable nosimpint
26#endif
f97c9854
JS
27#include <Xm/Xm.h>
28#include <Xm/PushBG.h>
29#include <Xm/PushB.h>
30#include <Xm/RowColumn.h>
338dd992
JJ
31#ifdef __VMS__
32#pragma message enable nosimpint
33#endif
f97c9854
JS
34
35#include "wx/motif/private.h"
4bb6408c 36
3a73cc52
MB
37#define WIDTH_OVERHEAD 48
38#define WIDTH_OVERHEAD_SUBTRACT 40
39#define HEIGHT_OVERHEAD 15
40
b1294ada 41IMPLEMENT_DYNAMIC_CLASS(wxChoice, wxControlWithItems)
4bb6408c 42
f97c9854 43void wxChoiceCallback (Widget w, XtPointer clientData,
2d120f83 44 XtPointer ptr);
f97c9854
JS
45
46wxChoice::wxChoice()
47{
ec75d791
MB
48 Init();
49}
50
51void wxChoice::Init()
52{
f97c9854
JS
53 m_buttonWidget = (WXWidget) 0;
54 m_menuWidget = (WXWidget) 0;
f97c9854 55 m_formWidget = (WXWidget) 0;
f97c9854
JS
56}
57
4bb6408c 58bool wxChoice::Create(wxWindow *parent, wxWindowID id,
2d120f83
JS
59 const wxPoint& pos,
60 const wxSize& size,
61 int n, const wxString choices[],
62 long style,
63 const wxValidator& validator,
64 const wxString& name)
4bb6408c 65{
ec75d791 66 if ( !CreateControl(parent, id, pos, size, style, validator, name) )
312ebad4 67 return false;
105fbe1f 68 PreCreation();
31528cd3 69
f97c9854 70 Widget parentWidget = (Widget) parent->GetClientWidget();
31528cd3
VZ
71
72 m_formWidget = (WXWidget) XtVaCreateManagedWidget(name.c_str(),
2d120f83
JS
73 xmRowColumnWidgetClass, parentWidget,
74 XmNmarginHeight, 0,
75 XmNmarginWidth, 0,
76 XmNpacking, XmPACK_TIGHT,
77 XmNorientation, XmHORIZONTAL,
3a73cc52
MB
78 XmNresizeWidth, False,
79 XmNresizeHeight, False,
2d120f83 80 NULL);
31528cd3 81
f97c9854 82 XtVaSetValues ((Widget) m_formWidget, XmNspacing, 0, NULL);
31528cd3 83
2d120f83 84 /*
f97c9854
JS
85 * Create the popup menu
86 */
ec75d791 87 m_menuWidget = (WXWidget) XmCreatePulldownMenu ((Widget) m_formWidget,
f1db433a
VZ
88 wxMOTIF_STR("choiceMenu"),
89 NULL, 0);
31528cd3 90
f97c9854
JS
91 if (n > 0)
92 {
93 int i;
94 for (i = 0; i < n; i++)
95 Append (choices[i]);
96 }
31528cd3 97
2d120f83 98 /*
f97c9854
JS
99 * Create button
100 */
101 Arg args[10];
102 Cardinal argcnt = 0;
31528cd3 103
ec75d791
MB
104 XtSetArg (args[argcnt], XmNsubMenuId, (Widget) m_menuWidget); ++argcnt;
105 XtSetArg (args[argcnt], XmNmarginWidth, 0); ++argcnt;
106 XtSetArg (args[argcnt], XmNmarginHeight, 0); ++argcnt;
107 XtSetArg (args[argcnt], XmNpacking, XmPACK_TIGHT); ++argcnt;
108 m_buttonWidget = (WXWidget) XmCreateOptionMenu ((Widget) m_formWidget,
f1db433a 109 wxMOTIF_STR("choiceButton"),
ec75d791 110 args, argcnt);
31528cd3 111
f97c9854 112 m_mainWidget = m_buttonWidget;
31528cd3 113
f97c9854 114 XtManageChild ((Widget) m_buttonWidget);
9838df2c 115
f97c9854
JS
116 // New code from Roland Haenel (roland_haenel@ac.cybercity.de)
117 // Some time ago, I reported a problem with wxChoice-items under
118 // Linux and Motif 2.0 (they caused sporadic GPFs). Now it seems
119 // that I have found the code responsible for this behaviour.
120#if XmVersion >= 1002
121#if XmVersion < 2000
9838df2c
JS
122 // JACS, 24/1/99: this seems to cause a malloc crash later on, e.g.
123 // in controls sample.
dfe1eee3
VZ
124 //
125 // Widget optionLabel = XmOptionLabelGadget ((Widget) m_buttonWidget);
126 // XtUnmanageChild (optionLabel);
f97c9854
JS
127#endif
128#endif
9838df2c 129
a29ee706
MB
130 wxSize bestSize = GetBestSize();
131 if( size.x > 0 ) bestSize.x = size.x;
132 if( size.y > 0 ) bestSize.y = size.y;
133
f97c9854 134 XtVaSetValues((Widget) m_formWidget, XmNresizePolicy, XmRESIZE_NONE, NULL);
31528cd3 135
105fbe1f 136 PostCreation();
ec75d791 137 AttachWidget (parent, m_buttonWidget, m_formWidget,
a29ee706 138 pos.x, pos.y, bestSize.x, bestSize.y);
31528cd3 139
312ebad4 140 return true;
f97c9854
JS
141}
142
584ad2a3
MB
143bool wxChoice::Create(wxWindow *parent, wxWindowID id,
144 const wxPoint& pos,
145 const wxSize& size,
146 const wxArrayString& choices,
147 long style,
148 const wxValidator& validator,
149 const wxString& name)
150{
151 wxCArrayString chs(choices);
152 return Create(parent, id, pos, size, chs.GetCount(), chs.GetStrings(),
153 style, validator, name);
154}
155
f97c9854
JS
156wxChoice::~wxChoice()
157{
2d120f83
JS
158 // For some reason destroying the menuWidget
159 // can cause crashes on some machines. It will
160 // be deleted implicitly by deleting the parent form
161 // anyway.
162 // XtDestroyWidget (menuWidget);
31528cd3 163
8aa04e8b
JS
164 if (GetMainWidget())
165 {
166 DetachWidget(GetMainWidget()); // Removes event handlers
b412f9be 167 DetachWidget(m_formWidget);
31528cd3 168
8aa04e8b
JS
169 XtDestroyWidget((Widget) m_formWidget);
170 m_formWidget = (WXWidget) 0;
31528cd3 171
8aa04e8b
JS
172 // Presumably the other widgets have been deleted now, via the form
173 m_mainWidget = (WXWidget) 0;
174 m_buttonWidget = (WXWidget) 0;
175 }
4bb6408c
JS
176}
177
f94a81c0
MB
178static inline wxChar* MYcopystring(const wxChar* s)
179{
180 wxChar* copy = new wxChar[wxStrlen(s) + 1];
181 return wxStrcpy(copy, s);
182}
183
a236aa20
VZ
184// TODO auto-sorting is not supported by the code
185int wxChoice::DoInsertItems(const wxArrayStringsAdapter& items,
186 unsigned int pos,
187 void **clientData, wxClientDataType type)
4bb6408c 188{
f94a81c0 189#ifndef XmNpositionIndex
aa61d352 190 wxCHECK_MSG( pos == GetCount(), -1, wxT("insert not implemented"));
f94a81c0 191#endif
a236aa20
VZ
192
193 const unsigned int numItems = items.GetCount();
194 AllocClientData(numItems);
195 for( unsigned int i = 0; i < numItems; ++i, ++pos )
196 {
197 Widget w = XtVaCreateManagedWidget (GetLabelText(items[i]),
8c624a14 198#if wxUSE_GADGETS
a236aa20 199 xmPushButtonGadgetClass, (Widget) m_menuWidget,
f97c9854 200#else
a236aa20 201 xmPushButtonWidgetClass, (Widget) m_menuWidget,
f94a81c0
MB
202#endif
203#ifdef XmNpositionIndex
a236aa20 204 XmNpositionIndex, pos,
f97c9854 205#endif
a236aa20 206 NULL);
31528cd3 207
a236aa20 208 wxDoChangeBackgroundColour((WXWidget) w, m_backgroundColour);
31528cd3 209
a236aa20
VZ
210 if( m_font.Ok() )
211 wxDoChangeFont( w, m_font );
31528cd3 212
a236aa20 213 m_widgetArray.Insert(w, pos);
31528cd3 214
a236aa20
VZ
215 char mnem = wxFindMnemonic (items[i]);
216 if (mnem != 0)
217 XtVaSetValues (w, XmNmnemonic, mnem, NULL);
31528cd3 218
a236aa20
VZ
219 XtAddCallback (w, XmNactivateCallback,
220 (XtCallbackProc) wxChoiceCallback,
221 (XtPointer) this);
31528cd3 222
58045daa 223 if (m_stringArray.GetCount() == 0 && m_buttonWidget)
a236aa20
VZ
224 {
225 XtVaSetValues ((Widget) m_buttonWidget, XmNmenuHistory, w, NULL);
226 Widget label = XmOptionButtonGadget ((Widget) m_buttonWidget);
227 wxXmString text( items[i] );
228 XtVaSetValues (label,
229 XmNlabelString, text(),
230 NULL);
231 }
58045daa
FM
232
233 m_stringArray.Insert(items[i], pos);
a236aa20
VZ
234
235 InsertNewItemClientData(pos, clientData, i, type);
f6bcfd97 236 }
c33c81c3 237
a236aa20 238 return pos - 1;
4bb6408c
JS
239}
240
a236aa20 241void wxChoice::DoDeleteOneItem(unsigned int n)
4bb6408c 242{
ec75d791
MB
243 Widget w = (Widget)m_widgetArray[n];
244 XtRemoveCallback(w, XmNactivateCallback, (XtCallbackProc)wxChoiceCallback,
245 (XtPointer)this);
58045daa
FM
246
247 m_stringArray.RemoveAt(size_t(n));
ec75d791 248 m_widgetArray.RemoveAt(size_t(n));
a236aa20 249 wxChoiceBase::DoDeleteOneItem(n);
31528cd3 250
ec75d791 251 XtDestroyWidget(w);
4bb6408c
JS
252}
253
a236aa20 254void wxChoice::DoClear()
4bb6408c 255{
58045daa
FM
256 m_stringArray.Clear();
257
aa61d352 258 unsigned int i;
58045daa 259 for (i = 0; i < m_stringArray.GetCount(); i++)
f97c9854 260 {
ec75d791
MB
261 XtRemoveCallback((Widget) m_widgetArray[i],
262 XmNactivateCallback, (XtCallbackProc)wxChoiceCallback,
263 (XtPointer)this);
264 XtUnmanageChild ((Widget) m_widgetArray[i]);
265 XtDestroyWidget ((Widget) m_widgetArray[i]);
f97c9854 266 }
58045daa 267
ec75d791 268 m_widgetArray.Clear();
f97c9854 269 if (m_buttonWidget)
ec75d791
MB
270 XtVaSetValues ((Widget) m_buttonWidget,
271 XmNmenuHistory, (Widget) NULL,
272 NULL);
f6bcfd97 273
a236aa20 274 wxChoiceBase::DoClear();
4bb6408c
JS
275}
276
277int wxChoice::GetSelection() const
278{
2d120f83 279 XmString text;
2d120f83
JS
280 Widget label = XmOptionButtonGadget ((Widget) m_buttonWidget);
281 XtVaGetValues (label,
282 XmNlabelString, &text,
283 NULL);
da494b40
MB
284 wxXmString freeMe(text);
285 wxString s = wxXmStringToString( text );
31528cd3 286
11e62fe6 287 if (!s.empty())
2d120f83 288 {
58045daa
FM
289 for (size_t i=0; i<m_stringArray.GetCount(); i++)
290 if (m_stringArray[i] == s)
2d120f83 291 return i;
31528cd3 292
58045daa 293 return wxNOT_FOUND;
2d120f83 294 }
58045daa
FM
295
296 return wxNOT_FOUND;
4bb6408c
JS
297}
298
299void wxChoice::SetSelection(int n)
300{
312ebad4 301 m_inSetValue = true;
31528cd3 302
ec75d791 303#if 0
58045daa 304 Dimension selectionWidth, selectionHeight;
ec75d791 305#endif
58045daa 306 wxXmString text( m_stringArray[n] );
ec75d791
MB
307// MBN: this seems silly, at best, and causes wxChoices to be clipped:
308// will remove "soon"
309#if 0
58045daa
FM
310 XtVaGetValues ((Widget) m_widgetArray[n],
311 XmNwidth, &selectionWidth,
312 XmNheight, &selectionHeight,
313 NULL);
ec75d791 314#endif
58045daa
FM
315 Widget label = XmOptionButtonGadget ((Widget) m_buttonWidget);
316 XtVaSetValues (label,
317 XmNlabelString, text(),
318 NULL);
ec75d791 319#if 0
58045daa
FM
320 XtVaSetValues ((Widget) m_buttonWidget,
321 XmNwidth, selectionWidth, XmNheight, selectionHeight,
322 XmNmenuHistory, (Widget) m_widgetArray[n], NULL);
ec75d791 323#endif
58045daa 324
312ebad4 325 m_inSetValue = false;
4bb6408c
JS
326}
327
aa61d352 328wxString wxChoice::GetString(unsigned int n) const
4bb6408c 329{
58045daa 330 return m_stringArray[n];
f97c9854
JS
331}
332
333void wxChoice::SetColumns(int n)
334{
2d120f83 335 if (n<1) n = 1 ;
31528cd3 336
55034339 337 short numColumns = (short)n ;
2d120f83 338 Arg args[3];
31528cd3 339
2d120f83
JS
340 XtSetArg(args[0], XmNnumColumns, numColumns);
341 XtSetArg(args[1], XmNpacking, XmPACK_COLUMN);
342 XtSetValues((Widget) m_menuWidget,args,2) ;
f97c9854
JS
343}
344
345int wxChoice::GetColumns(void) const
346{
2d120f83 347 short numColumns ;
31528cd3 348
2d120f83
JS
349 XtVaGetValues((Widget) m_menuWidget,XmNnumColumns,&numColumns,NULL) ;
350 return numColumns ;
f97c9854
JS
351}
352
353void wxChoice::SetFocus()
354{
2d120f83 355 XmProcessTraversal(XtParent((Widget)m_mainWidget), XmTRAVERSE_CURRENT);
4bb6408c
JS
356}
357
bfc6fde4 358void wxChoice::DoSetSize(int x, int y, int width, int height, int sizeFlags)
4bb6408c 359{
f97c9854
JS
360 XtVaSetValues((Widget) m_formWidget, XmNresizePolicy, XmRESIZE_ANY, NULL);
361 bool managed = XtIsManaged((Widget) m_formWidget);
31528cd3 362
f97c9854
JS
363 if (managed)
364 XtUnmanageChild ((Widget) m_formWidget);
31528cd3 365
3a73cc52
MB
366 int actualWidth = width - WIDTH_OVERHEAD_SUBTRACT,
367 actualHeight = height - HEIGHT_OVERHEAD;
31528cd3 368
f97c9854
JS
369 if (width > -1)
370 {
aa61d352 371 unsigned int i;
58045daa 372 for (i = 0; i < m_stringArray.GetCount(); i++)
ec75d791
MB
373 XtVaSetValues ((Widget) m_widgetArray[i],
374 XmNwidth, actualWidth,
375 NULL);
f97c9854 376 XtVaSetValues ((Widget) m_buttonWidget, XmNwidth, actualWidth,
2d120f83 377 NULL);
f97c9854
JS
378 }
379 if (height > -1)
380 {
3a73cc52 381#if 0
aa61d352 382 unsigned int i;
58045daa 383 for (i = 0; i < m_stringArray.GetCount(); i++)
ec75d791
MB
384 XtVaSetValues ((Widget) m_widgetArray[i],
385 XmNheight, actualHeight,
386 NULL);
3a73cc52 387#endif
f97c9854 388 XtVaSetValues ((Widget) m_buttonWidget, XmNheight, actualHeight,
2d120f83 389 NULL);
f97c9854 390 }
31528cd3 391
f97c9854
JS
392 if (managed)
393 XtManageChild ((Widget) m_formWidget);
394 XtVaSetValues((Widget) m_formWidget, XmNresizePolicy, XmRESIZE_NONE, NULL);
31528cd3 395
bfc6fde4 396 wxControl::DoSetSize (x, y, width, height, sizeFlags);
4bb6408c
JS
397}
398
4bb6408c
JS
399void wxChoice::Command(wxCommandEvent & event)
400{
401 SetSelection (event.GetInt());
402 ProcessCommand (event);
403}
404
f9e02ac7 405void wxChoiceCallback (Widget w, XtPointer clientData, XtPointer WXUNUSED(ptr))
f97c9854
JS
406{
407 wxChoice *item = (wxChoice *) clientData;
408 if (item)
409 {
a4294b78 410 if (item->InSetValue())
f97c9854 411 return;
31528cd3 412
ec75d791
MB
413 int n = item->GetWidgets().Index(w);
414 if (n != wxNOT_FOUND)
f97c9854 415 {
ec75d791 416 wxCommandEvent event(wxEVT_COMMAND_CHOICE_SELECTED, item->GetId());
55acd85e 417 event.SetEventObject(item);
687706f5 418 event.SetInt(n);
58045daa 419 event.SetString( item->GetStrings().Item(n) );
ec75d791
MB
420 if ( item->HasClientObjectData() )
421 event.SetClientObject( item->GetClientObject(n) );
422 else if ( item->HasClientUntypedData() )
423 event.SetClientData( item->GetClientData(n) );
f97c9854
JS
424 item->ProcessCommand (event);
425 }
426 }
427}
428
4b5f3fe6 429void wxChoice::ChangeFont(bool keepOriginalSize)
0d57be45 430{
321db4b6
JS
431 // Note that this causes the widget to be resized back
432 // to its original size! We therefore have to set the size
433 // back again. TODO: a better way in Motif?
105fbe1f 434 if (m_mainWidget && m_font.Ok())
321db4b6 435 {
73608949 436 Display* dpy = XtDisplay((Widget) m_mainWidget);
321db4b6
JS
437 int width, height, width1, height1;
438 GetSize(& width, & height);
31528cd3 439
da494b40
MB
440 WXString fontTag = wxFont::GetFontTag();
441
73608949
MB
442 XtVaSetValues ((Widget) m_formWidget,
443 fontTag, m_font.GetFontTypeC(dpy),
444 NULL);
445 XtVaSetValues ((Widget) m_buttonWidget,
446 fontTag, m_font.GetFontTypeC(dpy),
447 NULL);
31528cd3 448
58045daa 449 for( unsigned int i = 0; i < m_stringArray.GetCount(); ++i )
ec75d791 450 XtVaSetValues( (Widget)m_widgetArray[i],
73608949 451 fontTag, m_font.GetFontTypeC(dpy),
ec75d791 452 NULL );
312ebad4 453
321db4b6 454 GetSize(& width1, & height1);
4b5f3fe6 455 if (keepOriginalSize && (width != width1 || height != height1))
321db4b6 456 {
312ebad4 457 SetSize(wxDefaultCoord, wxDefaultCoord, width, height);
321db4b6
JS
458 }
459 }
0d57be45
JS
460}
461
462void wxChoice::ChangeBackgroundColour()
463{
a8680e3e
MB
464 wxDoChangeBackgroundColour(m_formWidget, m_backgroundColour);
465 wxDoChangeBackgroundColour(m_buttonWidget, m_backgroundColour);
466 wxDoChangeBackgroundColour(m_menuWidget, m_backgroundColour);
aa61d352 467 unsigned int i;
58045daa 468 for (i = 0; i < m_stringArray.GetCount(); i++)
a8680e3e 469 wxDoChangeBackgroundColour(m_widgetArray[i], m_backgroundColour);
0d57be45
JS
470}
471
472void wxChoice::ChangeForegroundColour()
473{
a8680e3e
MB
474 wxDoChangeForegroundColour(m_formWidget, m_foregroundColour);
475 wxDoChangeForegroundColour(m_buttonWidget, m_foregroundColour);
476 wxDoChangeForegroundColour(m_menuWidget, m_foregroundColour);
aa61d352 477 unsigned int i;
58045daa 478 for (i = 0; i < m_stringArray.GetCount(); i++)
a8680e3e 479 wxDoChangeForegroundColour(m_widgetArray[i], m_foregroundColour);
0d57be45 480}
6adaedf0 481
aa61d352 482unsigned int wxChoice::GetCount() const
6adaedf0 483{
58045daa 484 return m_stringArray.GetCount();
6adaedf0
JS
485}
486
aa61d352 487void wxChoice::SetString(unsigned int WXUNUSED(n), const wxString& WXUNUSED(s))
6adaedf0 488{
ec75d791 489 wxFAIL_MSG( wxT("wxChoice::SetString not implemented") );
6adaedf0
JS
490}
491
ec75d791 492wxSize wxChoice::GetItemsSize() const
6adaedf0 493{
ec75d791 494 int x, y, mx = 0, my = 0;
f6bcfd97 495
ec75d791
MB
496 // get my
497 GetTextExtent( "|", &x, &my );
f6bcfd97 498
58045daa 499 for (size_t i=0; i<m_stringArray.GetCount(); i++)
ec75d791 500 {
58045daa 501 GetTextExtent( m_stringArray[i], &x, &y );
ec75d791
MB
502 mx = wxMax( mx, x );
503 my = wxMax( my, y );
ec75d791 504 }
6adaedf0 505
ec75d791 506 return wxSize( mx, my );
6adaedf0
JS
507}
508
ec75d791 509wxSize wxChoice::DoGetBestSize() const
6adaedf0 510{
ec75d791
MB
511 wxSize items = GetItemsSize();
512 // FIXME arbitrary constants
3a73cc52
MB
513 return wxSize( ( items.x ? items.x + WIDTH_OVERHEAD : 120 ),
514 items.y + HEIGHT_OVERHEAD );
6adaedf0 515}
312ebad4
WS
516
517#endif // wxUSE_CHOICE