]> git.saurik.com Git - wxWidgets.git/blame - src/motif/choice.cpp
build fix
[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
bcd055ae 17#ifdef __VMS
4dff3400
JJ
18#define XtDisplay XTDISPLAY
19#define XtParent XTPARENT
20#endif
21
4bb6408c 22#include "wx/choice.h"
de6185e2
WS
23
24#ifndef WX_PRECOMP
25 #include "wx/utils.h"
26#endif
27
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 73 if ( !CreateControl(parent, id, pos, size, style, validator, name) )
312ebad4 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 93 m_menuWidget = (WXWidget) XmCreatePulldownMenu ((Widget) m_formWidget,
f1db433a
VZ
94 wxMOTIF_STR("choiceMenu"),
95 NULL, 0);
31528cd3 96
f97c9854
JS
97 if (n > 0)
98 {
99 int i;
100 for (i = 0; i < n; i++)
101 Append (choices[i]);
102 }
31528cd3 103
2d120f83 104 /*
f97c9854
JS
105 * Create button
106 */
107 Arg args[10];
108 Cardinal argcnt = 0;
31528cd3 109
ec75d791
MB
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,
f1db433a 115 wxMOTIF_STR("choiceButton"),
ec75d791 116 args, argcnt);
31528cd3 117
f97c9854 118 m_mainWidget = m_buttonWidget;
31528cd3 119
f97c9854 120 XtManageChild ((Widget) m_buttonWidget);
9838df2c 121
f97c9854
JS
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
9838df2c
JS
128 // JACS, 24/1/99: this seems to cause a malloc crash later on, e.g.
129 // in controls sample.
dfe1eee3
VZ
130 //
131 // Widget optionLabel = XmOptionLabelGadget ((Widget) m_buttonWidget);
132 // XtUnmanageChild (optionLabel);
f97c9854
JS
133#endif
134#endif
9838df2c 135
a29ee706
MB
136 wxSize bestSize = GetBestSize();
137 if( size.x > 0 ) bestSize.x = size.x;
138 if( size.y > 0 ) bestSize.y = size.y;
139
f97c9854 140 XtVaSetValues((Widget) m_formWidget, XmNresizePolicy, XmRESIZE_NONE, NULL);
31528cd3 141
312ebad4 142 ChangeFont(false);
3a73cc52 143 ChangeBackgroundColour();
9838df2c 144
ec75d791 145 AttachWidget (parent, m_buttonWidget, m_formWidget,
a29ee706 146 pos.x, pos.y, bestSize.x, bestSize.y);
31528cd3 147
312ebad4 148 return true;
f97c9854
JS
149}
150
584ad2a3
MB
151bool wxChoice::Create(wxWindow *parent, wxWindowID id,
152 const wxPoint& pos,
153 const wxSize& size,
154 const wxArrayString& choices,
155 long style,
156 const wxValidator& validator,
157 const wxString& name)
158{
159 wxCArrayString chs(choices);
160 return Create(parent, id, pos, size, chs.GetCount(), chs.GetStrings(),
161 style, validator, name);
162}
163
f97c9854
JS
164wxChoice::~wxChoice()
165{
2d120f83
JS
166 // For some reason destroying the menuWidget
167 // can cause crashes on some machines. It will
168 // be deleted implicitly by deleting the parent form
169 // anyway.
170 // XtDestroyWidget (menuWidget);
31528cd3 171
8aa04e8b
JS
172 if (GetMainWidget())
173 {
174 DetachWidget(GetMainWidget()); // Removes event handlers
b412f9be 175 DetachWidget(m_formWidget);
31528cd3 176
8aa04e8b
JS
177 XtDestroyWidget((Widget) m_formWidget);
178 m_formWidget = (WXWidget) 0;
31528cd3 179
8aa04e8b
JS
180 // Presumably the other widgets have been deleted now, via the form
181 m_mainWidget = (WXWidget) 0;
182 m_buttonWidget = (WXWidget) 0;
183 }
ec75d791
MB
184 if ( HasClientObjectData() )
185 m_clientDataDict.DestroyData();
4bb6408c
JS
186}
187
f94a81c0
MB
188static inline wxChar* MYcopystring(const wxChar* s)
189{
190 wxChar* copy = new wxChar[wxStrlen(s) + 1];
191 return wxStrcpy(copy, s);
192}
193
aa61d352 194int wxChoice::DoInsert(const wxString& item, unsigned int pos)
4bb6408c 195{
f94a81c0 196#ifndef XmNpositionIndex
aa61d352 197 wxCHECK_MSG( pos == GetCount(), -1, wxT("insert not implemented"));
f94a81c0 198#endif
31528cd3 199 Widget w = XtVaCreateManagedWidget (wxStripMenuCodes(item),
8c624a14 200#if wxUSE_GADGETS
2d120f83 201 xmPushButtonGadgetClass, (Widget) m_menuWidget,
f97c9854 202#else
2d120f83 203 xmPushButtonWidgetClass, (Widget) m_menuWidget,
f94a81c0
MB
204#endif
205#ifdef XmNpositionIndex
206 XmNpositionIndex, pos,
f97c9854 207#endif
2d120f83 208 NULL);
31528cd3 209
a8680e3e 210 wxDoChangeBackgroundColour((WXWidget) w, m_backgroundColour);
31528cd3 211
e1aae528
MB
212 if( m_font.Ok() )
213 wxDoChangeFont( w, m_font );
31528cd3 214
f94a81c0 215 m_widgetArray.Insert(w, pos);
31528cd3 216
3a73cc52 217 char mnem = wxFindMnemonic (item);
f6bcfd97
BP
218 if (mnem != 0)
219 XtVaSetValues (w, XmNmnemonic, mnem, NULL);
31528cd3 220
ec75d791
MB
221 XtAddCallback (w, XmNactivateCallback,
222 (XtCallbackProc) wxChoiceCallback,
223 (XtPointer) this);
31528cd3 224
f6bcfd97
BP
225 if (m_noStrings == 0 && m_buttonWidget)
226 {
227 XtVaSetValues ((Widget) m_buttonWidget, XmNmenuHistory, w, NULL);
228 Widget label = XmOptionButtonGadget ((Widget) m_buttonWidget);
c13c9657 229 wxXmString text( item );
f6bcfd97 230 XtVaSetValues (label,
c13c9657 231 XmNlabelString, text(),
f6bcfd97 232 NULL);
f6bcfd97 233 }
f94a81c0
MB
234 // need to ditch wxStringList for wxArrayString
235 m_stringList.Insert(pos, MYcopystring(item));
f6bcfd97 236 m_noStrings ++;
c33c81c3 237
f94a81c0 238 return pos;
4bb6408c
JS
239}
240
f94a81c0 241int wxChoice::DoAppend(const wxString& item)
243dbf1a 242{
aa61d352 243 return DoInsert(item, GetCount());
243dbf1a
VZ
244}
245
aa61d352 246void wxChoice::Delete(unsigned int n)
4bb6408c 247{
ec75d791
MB
248 Widget w = (Widget)m_widgetArray[n];
249 XtRemoveCallback(w, XmNactivateCallback, (XtCallbackProc)wxChoiceCallback,
250 (XtPointer)this);
ac32ba44 251 m_stringList.Erase(m_stringList.Item(n));
ec75d791
MB
252 m_widgetArray.RemoveAt(size_t(n));
253 m_clientDataDict.Delete(n, HasClientObjectData());
31528cd3 254
ec75d791 255 XtDestroyWidget(w);
aa61d352 256 m_noStrings--;
4bb6408c
JS
257}
258
259void wxChoice::Clear()
260{
f97c9854 261 m_stringList.Clear ();
aa61d352 262 unsigned int i;
f97c9854
JS
263 for (i = 0; i < m_noStrings; i++)
264 {
ec75d791
MB
265 XtRemoveCallback((Widget) m_widgetArray[i],
266 XmNactivateCallback, (XtCallbackProc)wxChoiceCallback,
267 (XtPointer)this);
268 XtUnmanageChild ((Widget) m_widgetArray[i]);
269 XtDestroyWidget ((Widget) m_widgetArray[i]);
f97c9854 270 }
ec75d791 271 m_widgetArray.Clear();
f97c9854 272 if (m_buttonWidget)
ec75d791
MB
273 XtVaSetValues ((Widget) m_buttonWidget,
274 XmNmenuHistory, (Widget) NULL,
275 NULL);
f6bcfd97
BP
276
277 if ( HasClientObjectData() )
ec75d791 278 m_clientDataDict.DestroyData();
f6bcfd97 279
4bb6408c
JS
280 m_noStrings = 0;
281}
282
283int wxChoice::GetSelection() const
284{
2d120f83 285 XmString text;
2d120f83
JS
286 Widget label = XmOptionButtonGadget ((Widget) m_buttonWidget);
287 XtVaGetValues (label,
288 XmNlabelString, &text,
289 NULL);
da494b40
MB
290 wxXmString freeMe(text);
291 wxString s = wxXmStringToString( text );
31528cd3 292
11e62fe6 293 if (!s.empty())
2d120f83
JS
294 {
295 int i = 0;
ac32ba44 296 for (wxStringList::compatibility_iterator node = m_stringList.GetFirst ();
ec75d791 297 node; node = node->GetNext ())
f97c9854 298 {
da494b40 299 if (wxStrcmp(node->GetData(), s.c_str()) == 0)
2d120f83 300 {
2d120f83
JS
301 return i;
302 }
303 else
304 i++;
305 } // for()
31528cd3 306
2d120f83
JS
307 return -1;
308 }
2d120f83 309 return -1;
4bb6408c
JS
310}
311
312void wxChoice::SetSelection(int n)
313{
312ebad4 314 m_inSetValue = true;
31528cd3 315
ac32ba44 316 wxStringList::compatibility_iterator node = m_stringList.Item(n);
2d120f83 317 if (node)
f97c9854 318 {
ec75d791 319#if 0
2d120f83 320 Dimension selectionWidth, selectionHeight;
ec75d791 321#endif
fd304d98 322 wxXmString text( node->GetData() );
ec75d791
MB
323// MBN: this seems silly, at best, and causes wxChoices to be clipped:
324// will remove "soon"
325#if 0
326 XtVaGetValues ((Widget) m_widgetArray[n],
327 XmNwidth, &selectionWidth,
328 XmNheight, &selectionHeight,
329 NULL);
330#endif
2d120f83
JS
331 Widget label = XmOptionButtonGadget ((Widget) m_buttonWidget);
332 XtVaSetValues (label,
ec75d791 333 XmNlabelString, text(),
2d120f83 334 NULL);
ec75d791 335#if 0
2d120f83
JS
336 XtVaSetValues ((Widget) m_buttonWidget,
337 XmNwidth, selectionWidth, XmNheight, selectionHeight,
ec75d791
MB
338 XmNmenuHistory, (Widget) m_widgetArray[n], NULL);
339#endif
f97c9854 340 }
312ebad4 341 m_inSetValue = false;
4bb6408c
JS
342}
343
aa61d352 344wxString wxChoice::GetString(unsigned int n) const
4bb6408c 345{
ac32ba44 346 wxStringList::compatibility_iterator node = m_stringList.Item(n);
2d120f83 347 if (node)
ec75d791 348 return node->GetData();
2d120f83
JS
349 else
350 return wxEmptyString;
f97c9854
JS
351}
352
353void wxChoice::SetColumns(int n)
354{
2d120f83 355 if (n<1) n = 1 ;
31528cd3 356
55034339 357 short numColumns = (short)n ;
2d120f83 358 Arg args[3];
31528cd3 359
2d120f83
JS
360 XtSetArg(args[0], XmNnumColumns, numColumns);
361 XtSetArg(args[1], XmNpacking, XmPACK_COLUMN);
362 XtSetValues((Widget) m_menuWidget,args,2) ;
f97c9854
JS
363}
364
365int wxChoice::GetColumns(void) const
366{
2d120f83 367 short numColumns ;
31528cd3 368
2d120f83
JS
369 XtVaGetValues((Widget) m_menuWidget,XmNnumColumns,&numColumns,NULL) ;
370 return numColumns ;
f97c9854
JS
371}
372
373void wxChoice::SetFocus()
374{
2d120f83 375 XmProcessTraversal(XtParent((Widget)m_mainWidget), XmTRAVERSE_CURRENT);
4bb6408c
JS
376}
377
bfc6fde4 378void wxChoice::DoSetSize(int x, int y, int width, int height, int sizeFlags)
4bb6408c 379{
f97c9854
JS
380 XtVaSetValues((Widget) m_formWidget, XmNresizePolicy, XmRESIZE_ANY, NULL);
381 bool managed = XtIsManaged((Widget) m_formWidget);
31528cd3 382
f97c9854
JS
383 if (managed)
384 XtUnmanageChild ((Widget) m_formWidget);
31528cd3 385
3a73cc52
MB
386 int actualWidth = width - WIDTH_OVERHEAD_SUBTRACT,
387 actualHeight = height - HEIGHT_OVERHEAD;
31528cd3 388
f97c9854
JS
389 if (width > -1)
390 {
aa61d352 391 unsigned int i;
f97c9854 392 for (i = 0; i < m_noStrings; i++)
ec75d791
MB
393 XtVaSetValues ((Widget) m_widgetArray[i],
394 XmNwidth, actualWidth,
395 NULL);
f97c9854 396 XtVaSetValues ((Widget) m_buttonWidget, XmNwidth, actualWidth,
2d120f83 397 NULL);
f97c9854
JS
398 }
399 if (height > -1)
400 {
3a73cc52 401#if 0
aa61d352 402 unsigned int i;
f97c9854 403 for (i = 0; i < m_noStrings; i++)
ec75d791
MB
404 XtVaSetValues ((Widget) m_widgetArray[i],
405 XmNheight, actualHeight,
406 NULL);
3a73cc52 407#endif
f97c9854 408 XtVaSetValues ((Widget) m_buttonWidget, XmNheight, actualHeight,
2d120f83 409 NULL);
f97c9854 410 }
31528cd3 411
f97c9854
JS
412 if (managed)
413 XtManageChild ((Widget) m_formWidget);
414 XtVaSetValues((Widget) m_formWidget, XmNresizePolicy, XmRESIZE_NONE, NULL);
31528cd3 415
bfc6fde4 416 wxControl::DoSetSize (x, y, width, height, sizeFlags);
4bb6408c
JS
417}
418
4bb6408c
JS
419void wxChoice::Command(wxCommandEvent & event)
420{
421 SetSelection (event.GetInt());
422 ProcessCommand (event);
423}
424
f9e02ac7 425void wxChoiceCallback (Widget w, XtPointer clientData, XtPointer WXUNUSED(ptr))
f97c9854
JS
426{
427 wxChoice *item = (wxChoice *) clientData;
428 if (item)
429 {
a4294b78 430 if (item->InSetValue())
f97c9854 431 return;
31528cd3 432
ec75d791
MB
433 int n = item->GetWidgets().Index(w);
434 if (n != wxNOT_FOUND)
f97c9854 435 {
ec75d791 436 wxCommandEvent event(wxEVT_COMMAND_CHOICE_SELECTED, item->GetId());
55acd85e 437 event.SetEventObject(item);
687706f5
KH
438 event.SetInt(n);
439 event.SetString( item->GetStrings().Item(n)->GetData() );
ec75d791
MB
440 if ( item->HasClientObjectData() )
441 event.SetClientObject( item->GetClientObject(n) );
442 else if ( item->HasClientUntypedData() )
443 event.SetClientData( item->GetClientData(n) );
f97c9854
JS
444 item->ProcessCommand (event);
445 }
446 }
447}
448
4b5f3fe6 449void wxChoice::ChangeFont(bool keepOriginalSize)
0d57be45 450{
321db4b6
JS
451 // Note that this causes the widget to be resized back
452 // to its original size! We therefore have to set the size
453 // back again. TODO: a better way in Motif?
da175b2c 454 if (m_font.Ok())
321db4b6 455 {
73608949 456 Display* dpy = XtDisplay((Widget) m_mainWidget);
321db4b6
JS
457 int width, height, width1, height1;
458 GetSize(& width, & height);
31528cd3 459
da494b40
MB
460 WXString fontTag = wxFont::GetFontTag();
461
73608949
MB
462 XtVaSetValues ((Widget) m_formWidget,
463 fontTag, m_font.GetFontTypeC(dpy),
464 NULL);
465 XtVaSetValues ((Widget) m_buttonWidget,
466 fontTag, m_font.GetFontTypeC(dpy),
467 NULL);
31528cd3 468
aa61d352 469 for( unsigned int i = 0; i < m_noStrings; ++i )
ec75d791 470 XtVaSetValues( (Widget)m_widgetArray[i],
73608949 471 fontTag, m_font.GetFontTypeC(dpy),
ec75d791 472 NULL );
312ebad4 473
321db4b6 474 GetSize(& width1, & height1);
4b5f3fe6 475 if (keepOriginalSize && (width != width1 || height != height1))
321db4b6 476 {
312ebad4 477 SetSize(wxDefaultCoord, wxDefaultCoord, width, height);
321db4b6
JS
478 }
479 }
0d57be45
JS
480}
481
482void wxChoice::ChangeBackgroundColour()
483{
a8680e3e
MB
484 wxDoChangeBackgroundColour(m_formWidget, m_backgroundColour);
485 wxDoChangeBackgroundColour(m_buttonWidget, m_backgroundColour);
486 wxDoChangeBackgroundColour(m_menuWidget, m_backgroundColour);
aa61d352 487 unsigned int i;
321db4b6 488 for (i = 0; i < m_noStrings; i++)
a8680e3e 489 wxDoChangeBackgroundColour(m_widgetArray[i], m_backgroundColour);
0d57be45
JS
490}
491
492void wxChoice::ChangeForegroundColour()
493{
a8680e3e
MB
494 wxDoChangeForegroundColour(m_formWidget, m_foregroundColour);
495 wxDoChangeForegroundColour(m_buttonWidget, m_foregroundColour);
496 wxDoChangeForegroundColour(m_menuWidget, m_foregroundColour);
aa61d352 497 unsigned int i;
321db4b6 498 for (i = 0; i < m_noStrings; i++)
a8680e3e 499 wxDoChangeForegroundColour(m_widgetArray[i], m_foregroundColour);
0d57be45 500}
6adaedf0 501
aa61d352 502unsigned int wxChoice::GetCount() const
6adaedf0 503{
ec75d791 504 return m_noStrings;
6adaedf0
JS
505}
506
aa61d352 507void wxChoice::DoSetItemClientData(unsigned int n, void* clientData)
6adaedf0 508{
312ebad4 509 m_clientDataDict.Set(n, (wxClientData*)clientData, false);
6adaedf0
JS
510}
511
aa61d352 512void* wxChoice::DoGetItemClientData(unsigned int n) const
6adaedf0 513{
ec75d791 514 return (void*)m_clientDataDict.Get(n);
6adaedf0
JS
515}
516
aa61d352 517void wxChoice::DoSetItemClientObject(unsigned int n, wxClientData* clientData)
6adaedf0 518{
ec75d791 519 // don't delete, wxItemContainer does that for us
312ebad4 520 m_clientDataDict.Set(n, clientData, false);
6adaedf0
JS
521}
522
aa61d352 523wxClientData* wxChoice::DoGetItemClientObject(unsigned int n) const
6adaedf0 524{
ec75d791 525 return m_clientDataDict.Get(n);
6adaedf0
JS
526}
527
aa61d352 528void wxChoice::SetString(unsigned int WXUNUSED(n), const wxString& WXUNUSED(s))
6adaedf0 529{
ec75d791 530 wxFAIL_MSG( wxT("wxChoice::SetString not implemented") );
6adaedf0
JS
531}
532
ec75d791 533wxSize wxChoice::GetItemsSize() const
6adaedf0 534{
ec75d791 535 int x, y, mx = 0, my = 0;
f6bcfd97 536
ec75d791
MB
537 // get my
538 GetTextExtent( "|", &x, &my );
f6bcfd97 539
ac32ba44 540 wxStringList::compatibility_iterator curr = m_stringList.GetFirst();
ec75d791
MB
541 while( curr )
542 {
543 GetTextExtent( curr->GetData(), &x, &y );
544 mx = wxMax( mx, x );
545 my = wxMax( my, y );
546 curr = curr->GetNext();
547 }
6adaedf0 548
ec75d791 549 return wxSize( mx, my );
6adaedf0
JS
550}
551
ec75d791 552wxSize wxChoice::DoGetBestSize() const
6adaedf0 553{
ec75d791
MB
554 wxSize items = GetItemsSize();
555 // FIXME arbitrary constants
3a73cc52
MB
556 return wxSize( ( items.x ? items.x + WIDTH_OVERHEAD : 120 ),
557 items.y + HEIGHT_OVERHEAD );
6adaedf0 558}
312ebad4
WS
559
560#endif // wxUSE_CHOICE