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