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