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