]> git.saurik.com Git - wxWidgets.git/blame - src/motif/choice.cpp
Applied patch from Ian Brown to make menus look better in wxMotif (#586347)
[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
12#ifdef __GNUG__
13#pragma implementation "choice.h"
14#endif
15
bcd055ae 16#ifdef __VMS
4dff3400
JJ
17#define XtDisplay XTDISPLAY
18#define XtParent XTPARENT
19#endif
20
f6045f99
GD
21#include "wx/defs.h"
22
4bb6408c 23#include "wx/choice.h"
f97c9854
JS
24#include "wx/utils.h"
25
338dd992
JJ
26#ifdef __VMS__
27#pragma message disable nosimpint
28#endif
f97c9854
JS
29#include <Xm/Xm.h>
30#include <Xm/PushBG.h>
31#include <Xm/PushB.h>
32#include <Xm/RowColumn.h>
338dd992
JJ
33#ifdef __VMS__
34#pragma message enable nosimpint
35#endif
f97c9854
JS
36
37#include "wx/motif/private.h"
4bb6408c 38
4bb6408c 39IMPLEMENT_DYNAMIC_CLASS(wxChoice, wxControl)
4bb6408c 40
f97c9854 41void wxChoiceCallback (Widget w, XtPointer clientData,
2d120f83 42 XtPointer ptr);
f97c9854
JS
43
44wxChoice::wxChoice()
45{
46 m_noStrings = 0;
47 m_buttonWidget = (WXWidget) 0;
48 m_menuWidget = (WXWidget) 0;
49 m_widgetList = (WXWidget*) 0;
50 m_formWidget = (WXWidget) 0;
f97c9854
JS
51}
52
4bb6408c 53bool wxChoice::Create(wxWindow *parent, wxWindowID id,
2d120f83
JS
54 const wxPoint& pos,
55 const wxSize& size,
56 int n, const wxString choices[],
57 long style,
58 const wxValidator& validator,
59 const wxString& name)
4bb6408c
JS
60{
61 SetName(name);
62 SetValidator(validator);
ea57084d 63 m_noStrings = 0; // Starts off with none, incremented in Append
4bb6408c 64 m_windowStyle = style;
f97c9854
JS
65 m_buttonWidget = (WXWidget) 0;
66 m_menuWidget = (WXWidget) 0;
67 m_widgetList = (WXWidget*) 0;
68 m_formWidget = (WXWidget) 0;
31528cd3 69
4bb6408c 70 if (parent) parent->AddChild(this);
31528cd3 71
4bb6408c 72 if ( id == -1 )
2d120f83 73 m_windowId = (int)NewControlId();
4bb6408c 74 else
2d120f83 75 m_windowId = id;
31528cd3 76
0d57be45
JS
77 m_backgroundColour = parent->GetBackgroundColour();
78 m_foregroundColour = parent->GetForegroundColour();
da175b2c 79 m_font = parent->GetFont();
31528cd3 80
f97c9854 81 Widget parentWidget = (Widget) parent->GetClientWidget();
31528cd3
VZ
82
83 m_formWidget = (WXWidget) XtVaCreateManagedWidget(name.c_str(),
2d120f83
JS
84 xmRowColumnWidgetClass, parentWidget,
85 XmNmarginHeight, 0,
86 XmNmarginWidth, 0,
87 XmNpacking, XmPACK_TIGHT,
88 XmNorientation, XmHORIZONTAL,
89 NULL);
31528cd3 90
f97c9854 91 XtVaSetValues ((Widget) m_formWidget, XmNspacing, 0, NULL);
31528cd3 92
2d120f83 93 /*
f97c9854
JS
94 * Create the popup menu
95 */
96 m_menuWidget = (WXWidget) XmCreatePulldownMenu ((Widget) m_formWidget, "choiceMenu", NULL, 0);
31528cd3 97
2d120f83 98 // int i;
f97c9854
JS
99 if (n > 0)
100 {
101 int i;
102 for (i = 0; i < n; i++)
103 Append (choices[i]);
104 }
31528cd3 105
2d120f83 106 /*
f97c9854
JS
107 * Create button
108 */
109 Arg args[10];
110 Cardinal argcnt = 0;
31528cd3 111
f97c9854
JS
112 XtSetArg (args[argcnt], XmNsubMenuId, (Widget) m_menuWidget);
113 argcnt++;
114 XtSetArg (args[argcnt], XmNmarginWidth, 0);
115 argcnt++;
116 XtSetArg (args[argcnt], XmNmarginHeight, 0);
117 argcnt++;
118 XtSetArg (args[argcnt], XmNpacking, XmPACK_TIGHT);
119 argcnt++;
120 m_buttonWidget = (WXWidget) XmCreateOptionMenu ((Widget) m_formWidget, "choiceButton", args, argcnt);
31528cd3 121
f97c9854 122 m_mainWidget = m_buttonWidget;
31528cd3 123
f97c9854 124 XtManageChild ((Widget) m_buttonWidget);
9838df2c 125
f97c9854
JS
126 // New code from Roland Haenel (roland_haenel@ac.cybercity.de)
127 // Some time ago, I reported a problem with wxChoice-items under
128 // Linux and Motif 2.0 (they caused sporadic GPFs). Now it seems
129 // that I have found the code responsible for this behaviour.
130#if XmVersion >= 1002
131#if XmVersion < 2000
9838df2c
JS
132 // JACS, 24/1/99: this seems to cause a malloc crash later on, e.g.
133 // in controls sample.
dfe1eee3
VZ
134 //
135 // Widget optionLabel = XmOptionLabelGadget ((Widget) m_buttonWidget);
136 // XtUnmanageChild (optionLabel);
f97c9854
JS
137#endif
138#endif
9838df2c 139
f97c9854 140 XtVaSetValues((Widget) m_formWidget, XmNresizePolicy, XmRESIZE_NONE, NULL);
31528cd3 141
4b5f3fe6 142 ChangeFont(FALSE);
9838df2c 143
f97c9854 144 AttachWidget (parent, m_buttonWidget, m_formWidget, pos.x, pos.y, size.x, size.y);
31528cd3 145
0d57be45 146 ChangeBackgroundColour();
31528cd3 147
f97c9854
JS
148 return TRUE;
149}
150
151wxChoice::~wxChoice()
152{
2d120f83
JS
153 // For some reason destroying the menuWidget
154 // can cause crashes on some machines. It will
155 // be deleted implicitly by deleting the parent form
156 // anyway.
157 // XtDestroyWidget (menuWidget);
f97c9854
JS
158 if (m_widgetList)
159 delete[] m_widgetList;
31528cd3 160
8aa04e8b
JS
161 if (GetMainWidget())
162 {
163 DetachWidget(GetMainWidget()); // Removes event handlers
b412f9be 164 DetachWidget(m_formWidget);
31528cd3 165
8aa04e8b
JS
166 XtDestroyWidget((Widget) m_formWidget);
167 m_formWidget = (WXWidget) 0;
31528cd3 168
8aa04e8b
JS
169 // Presumably the other widgets have been deleted now, via the form
170 m_mainWidget = (WXWidget) 0;
171 m_buttonWidget = (WXWidget) 0;
172 }
4bb6408c
JS
173}
174
175void wxChoice::Append(const wxString& item)
176{
31528cd3 177 Widget w = XtVaCreateManagedWidget (wxStripMenuCodes(item),
f97c9854 178#if USE_GADGETS
2d120f83 179 xmPushButtonGadgetClass, (Widget) m_menuWidget,
f97c9854 180#else
2d120f83 181 xmPushButtonWidgetClass, (Widget) m_menuWidget,
f97c9854 182#endif
2d120f83 183 NULL);
31528cd3 184
2d120f83 185 DoChangeBackgroundColour((WXWidget) w, m_backgroundColour);
31528cd3 186
da175b2c 187 if (m_font.Ok())
2d120f83 188 XtVaSetValues (w,
da175b2c 189 XmNfontList, (XmFontList) m_font.GetFontList(1.0, XtDisplay((Widget) m_formWidget)),
2d120f83 190 NULL);
31528cd3 191
2d120f83
JS
192 WXWidget *new_widgetList = new WXWidget[m_noStrings + 1];
193 int i;
194 if (m_widgetList)
195 for (i = 0; i < m_noStrings; i++)
196 new_widgetList[i] = m_widgetList[i];
31528cd3 197
f6bcfd97 198 new_widgetList[m_noStrings] = (WXWidget) w;
31528cd3 199
f6bcfd97
BP
200 if (m_widgetList)
201 delete[] m_widgetList;
202 m_widgetList = new_widgetList;
31528cd3 203
f6bcfd97
BP
204 char mnem = wxFindMnemonic ((char*) (const char*) item);
205 if (mnem != 0)
206 XtVaSetValues (w, XmNmnemonic, mnem, NULL);
31528cd3 207
f6bcfd97 208 XtAddCallback (w, XmNactivateCallback, (XtCallbackProc) wxChoiceCallback, (XtPointer) this);
31528cd3 209
f6bcfd97
BP
210 if (m_noStrings == 0 && m_buttonWidget)
211 {
212 XtVaSetValues ((Widget) m_buttonWidget, XmNmenuHistory, w, NULL);
213 Widget label = XmOptionButtonGadget ((Widget) m_buttonWidget);
214 XmString text = XmStringCreateSimple ((char*) (const char*) item);
215 XtVaSetValues (label,
216 XmNlabelString, text,
217 NULL);
218 XmStringFree (text);
219 }
220 wxNode *node = m_stringList.Add (item);
221 XtVaSetValues (w, XmNuserData, node->Data (), NULL);
31528cd3 222
f6bcfd97
BP
223 if (m_noStrings == 0)
224 m_clientList.Append((wxObject*) NULL);
225 else
226 m_clientList.Insert( m_clientList.Item(m_noStrings-1),
227 (wxObject*) NULL );
228 m_noStrings ++;
4bb6408c
JS
229}
230
f9e02ac7 231void wxChoice::Delete(int WXUNUSED(n))
4bb6408c 232{
f97c9854 233 wxFAIL_MSG( "Sorry, wxChoice::Delete isn't implemented yet. Maybe you'd like to volunteer? :-)" );
31528cd3 234
f97c9854
JS
235 // What should we do -- remove the callback for this button widget,
236 // delete the m_stringList entry, delete the button widget, construct a new widget list
237 // (see Append)
31528cd3 238
4bb6408c
JS
239 // TODO
240 m_noStrings --;
241}
242
243void wxChoice::Clear()
244{
f97c9854
JS
245 m_stringList.Clear ();
246 int i;
247 for (i = 0; i < m_noStrings; i++)
248 {
249 XtUnmanageChild ((Widget) m_widgetList[i]);
250 XtDestroyWidget ((Widget) m_widgetList[i]);
251 }
252 if (m_noStrings)
253 delete[] m_widgetList;
254 m_widgetList = (WXWidget*) NULL;
255 if (m_buttonWidget)
256 XtVaSetValues ((Widget) m_buttonWidget, XmNmenuHistory, (Widget) NULL, NULL);
f6bcfd97
BP
257
258 if ( HasClientObjectData() )
259 {
260 // destroy the data (due to Robert's idea of using wxList<wxObject>
261 // and not wxList<wxClientData> we can't just say
262 // m_clientList.DeleteContents(TRUE) - this would crash!
263 wxNode *node = m_clientList.First();
264 while ( node )
265 {
266 delete (wxClientData *)node->Data();
267 node = node->Next();
268 }
269 }
270 m_clientList.Clear();
271
4bb6408c
JS
272 m_noStrings = 0;
273}
274
275int wxChoice::GetSelection() const
276{
2d120f83
JS
277 XmString text;
278 char *s;
279 Widget label = XmOptionButtonGadget ((Widget) m_buttonWidget);
280 XtVaGetValues (label,
281 XmNlabelString, &text,
282 NULL);
31528cd3 283
2d120f83
JS
284 if (XmStringGetLtoR (text, XmSTRING_DEFAULT_CHARSET, &s))
285 {
286 int i = 0;
287 for (wxNode * node = m_stringList.First (); node; node = node->Next ())
f97c9854 288 {
2d120f83
JS
289 char *s1 = (char *) node->Data ();
290 if (s1 == s || strcmp (s1, s) == 0)
291 {
292 XmStringFree(text) ;
293 XtFree (s);
294 return i;
295 }
296 else
297 i++;
298 } // for()
31528cd3 299
2d120f83
JS
300 XmStringFree(text) ;
301 XtFree (s);
302 return -1;
303 }
304 XmStringFree(text) ;
305 return -1;
4bb6408c
JS
306}
307
308void wxChoice::SetSelection(int n)
309{
2d120f83 310 m_inSetValue = TRUE;
31528cd3 311
2d120f83
JS
312 wxNode *node = m_stringList.Nth (n);
313 if (node)
f97c9854 314 {
2d120f83 315 Dimension selectionWidth, selectionHeight;
31528cd3 316
2d120f83
JS
317 char *s = (char *) node->Data ();
318 XmString text = XmStringCreateSimple (s);
319 XtVaGetValues ((Widget) m_widgetList[n], XmNwidth, &selectionWidth, XmNheight, &selectionHeight, NULL);
320 Widget label = XmOptionButtonGadget ((Widget) m_buttonWidget);
321 XtVaSetValues (label,
322 XmNlabelString, text,
323 NULL);
324 XmStringFree (text);
325 XtVaSetValues ((Widget) m_buttonWidget,
326 XmNwidth, selectionWidth, XmNheight, selectionHeight,
327 XmNmenuHistory, (Widget) m_widgetList[n], NULL);
f97c9854 328 }
2d120f83 329 m_inSetValue = FALSE;
4bb6408c
JS
330}
331
332int wxChoice::FindString(const wxString& s) const
333{
f97c9854
JS
334 int i = 0;
335 for (wxNode * node = m_stringList.First (); node; node = node->Next ())
336 {
337 char *s1 = (char *) node->Data ();
338 if (s == s1)
339 {
340 return i;
341 }
342 else
343 i++;
344 }
345 return -1;
4bb6408c
JS
346}
347
348wxString wxChoice::GetString(int n) const
349{
2d120f83
JS
350 wxNode *node = m_stringList.Nth (n);
351 if (node)
352 return wxString((char *) node->Data ());
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
f97c9854 390 int actualWidth = width, actualHeight = height;
31528cd3 391
f97c9854
JS
392 if (width > -1)
393 {
394 int i;
395 for (i = 0; i < m_noStrings; i++)
396 XtVaSetValues ((Widget) m_widgetList[i], XmNwidth, actualWidth, NULL);
397 XtVaSetValues ((Widget) m_buttonWidget, XmNwidth, actualWidth,
2d120f83 398 NULL);
f97c9854
JS
399 }
400 if (height > -1)
401 {
402 int i;
403 for (i = 0; i < m_noStrings; i++)
404 XtVaSetValues ((Widget) m_widgetList[i], XmNheight, actualHeight, NULL);
405 XtVaSetValues ((Widget) m_buttonWidget, XmNheight, actualHeight,
2d120f83 406 NULL);
f97c9854 407 }
31528cd3 408
f97c9854
JS
409 if (managed)
410 XtManageChild ((Widget) m_formWidget);
411 XtVaSetValues((Widget) m_formWidget, XmNresizePolicy, XmRESIZE_NONE, NULL);
31528cd3 412
bfc6fde4 413 wxControl::DoSetSize (x, y, width, height, sizeFlags);
4bb6408c
JS
414}
415
416wxString wxChoice::GetStringSelection () const
417{
418 int sel = GetSelection ();
419 if (sel > -1)
420 return wxString(this->GetString (sel));
421 else
f97c9854 422 return wxEmptyString;
4bb6408c
JS
423}
424
425bool wxChoice::SetStringSelection (const wxString& s)
426{
427 int sel = FindString (s);
428 if (sel > -1)
2d120f83
JS
429 {
430 SetSelection (sel);
431 return TRUE;
432 }
4bb6408c
JS
433 else
434 return FALSE;
435}
436
437void wxChoice::Command(wxCommandEvent & event)
438{
439 SetSelection (event.GetInt());
440 ProcessCommand (event);
441}
442
f9e02ac7 443void wxChoiceCallback (Widget w, XtPointer clientData, XtPointer WXUNUSED(ptr))
f97c9854
JS
444{
445 wxChoice *item = (wxChoice *) clientData;
446 if (item)
447 {
a4294b78 448 if (item->InSetValue())
f97c9854 449 return;
31528cd3 450
f97c9854
JS
451 char *s = NULL;
452 XtVaGetValues (w, XmNuserData, &s, NULL);
453 if (s)
454 {
55acd85e
JS
455 wxCommandEvent event (wxEVT_COMMAND_CHOICE_SELECTED, item->GetId());
456 event.SetEventObject(item);
f97c9854 457 event.m_commandInt = item->FindString (s);
2d120f83 458 // event.m_commandString = s;
f97c9854
JS
459 item->ProcessCommand (event);
460 }
461 }
462}
463
4b5f3fe6 464void wxChoice::ChangeFont(bool keepOriginalSize)
0d57be45 465{
321db4b6
JS
466 // Note that this causes the widget to be resized back
467 // to its original size! We therefore have to set the size
468 // back again. TODO: a better way in Motif?
da175b2c 469 if (m_font.Ok())
321db4b6
JS
470 {
471 int width, height, width1, height1;
472 GetSize(& width, & height);
31528cd3 473
da175b2c 474 XmFontList fontList = (XmFontList) m_font.GetFontList(1.0, XtDisplay((Widget) m_mainWidget));
321db4b6
JS
475 XtVaSetValues ((Widget) m_mainWidget, XmNfontList, fontList, NULL);
476 XtVaSetValues ((Widget) m_buttonWidget, XmNfontList, fontList, NULL);
31528cd3 477
4b5f3fe6 478 /* TODO: why does this cause a crash in XtWidgetToApplicationContext?
321db4b6
JS
479 int i;
480 for (i = 0; i < m_noStrings; i++)
2d120f83 481 XtVaSetValues ((Widget) m_widgetList[i], XmNfontList, fontList, NULL);
4b5f3fe6 482 */
321db4b6 483 GetSize(& width1, & height1);
4b5f3fe6 484 if (keepOriginalSize && (width != width1 || height != height1))
321db4b6
JS
485 {
486 SetSize(-1, -1, width, height);
487 }
488 }
0d57be45
JS
489}
490
491void wxChoice::ChangeBackgroundColour()
492{
321db4b6
JS
493 DoChangeBackgroundColour(m_formWidget, m_backgroundColour);
494 DoChangeBackgroundColour(m_buttonWidget, m_backgroundColour);
495 DoChangeBackgroundColour(m_menuWidget, m_backgroundColour);
496 int i;
497 for (i = 0; i < m_noStrings; i++)
498 DoChangeBackgroundColour(m_widgetList[i], m_backgroundColour);
0d57be45
JS
499}
500
501void wxChoice::ChangeForegroundColour()
502{
321db4b6
JS
503 DoChangeForegroundColour(m_formWidget, m_foregroundColour);
504 DoChangeForegroundColour(m_buttonWidget, m_foregroundColour);
505 DoChangeForegroundColour(m_menuWidget, m_foregroundColour);
506 int i;
507 for (i = 0; i < m_noStrings; i++)
508 DoChangeForegroundColour(m_widgetList[i], m_foregroundColour);
0d57be45 509}
6adaedf0
JS
510
511
512// These implement functions needed by wxControlWithItems.
513// Unfortunately, they're not all implemented yet.
514
515int wxChoice::GetCount() const
516{
517 return Number();
518}
519
520int wxChoice::DoAppend(const wxString& item)
521{
522 Append(item);
523 return GetCount() - 1;
524}
525
526// Just appends, doesn't yet insert
527void wxChoice::DoInsertItems(const wxArrayString& items, int WXUNUSED(pos))
528{
529 size_t nItems = items.GetCount();
530
531 for ( size_t n = 0; n < nItems; n++ )
532 {
533 Append( items[n]);
534 }
535}
536
537void wxChoice::DoSetItems(const wxArrayString& items, void **WXUNUSED(clientData))
538{
539 Clear();
540 size_t nItems = items.GetCount();
541
542 for ( size_t n = 0; n < nItems; n++ )
543 {
544 Append(items[n]);
545 }
546}
547
548void wxChoice::DoSetFirstItem(int WXUNUSED(n))
549{
550 wxFAIL_MSG( wxT("wxChoice::DoSetFirstItem not implemented") );
551}
552
f6bcfd97 553void wxChoice::DoSetItemClientData(int n, void* clientData)
6adaedf0 554{
f6bcfd97
BP
555 wxNode *node = m_clientList.Nth( n );
556 wxCHECK_RET( node, wxT("invalid index in wxChoice::DoSetItemClientData") );
557
558 node->SetData( (wxObject*) clientData );
6adaedf0
JS
559}
560
f6bcfd97 561void* wxChoice::DoGetItemClientData(int n) const
6adaedf0 562{
f6bcfd97
BP
563 wxNode *node = m_clientList.Nth( n );
564 wxCHECK_MSG( node, NULL, wxT("invalid index in wxChoice::DoGetItemClientData") );
565
566 return node->Data();
6adaedf0
JS
567}
568
f6bcfd97 569void wxChoice::DoSetItemClientObject(int n, wxClientData* clientData)
6adaedf0 570{
f6bcfd97
BP
571 wxNode *node = m_clientList.Nth( n );
572 wxCHECK_RET( node, wxT("invalid index in wxChoice::DoSetItemClientObject") );
573
574 wxClientData *cd = (wxClientData*) node->Data();
575 delete cd;
576
577 node->SetData( (wxObject*) clientData );
6adaedf0
JS
578}
579
f6bcfd97 580wxClientData* wxChoice::DoGetItemClientObject(int n) const
6adaedf0 581{
f6bcfd97
BP
582 wxNode *node = m_clientList.Nth( n );
583 wxCHECK_MSG( node, (wxClientData *)NULL,
584 wxT("invalid index in wxChoice::DoGetItemClientObject") );
585
586 return (wxClientData*) node->Data();
6adaedf0
JS
587}
588
589void wxChoice::Select(int n)
590{
591 SetSelection(n);
592}
593
594void wxChoice::SetString(int WXUNUSED(n), const wxString& WXUNUSED(s))
595{
596 wxFAIL_MSG( wxT("wxChoice::SetString not implemented") );
597}