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