///////////////////////////////////////////////////////////////////////////// // Name: src/motif/choice.cpp // Purpose: wxChoice // Author: Julian Smart // Modified by: // Created: 17/09/98 // RCS-ID: $Id: choice.cpp 50982 2008-01-01 20:38:33Z VZ $ // Copyright: (c) Julian Smart // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// // For compilers that support precompilation, includes "wx.h". #include "wx/wxprec.h" #if wxUSE_CHOICE #include "wx/choice.h" #ifndef WX_PRECOMP #include "wx/utils.h" #include "wx/arrstr.h" #endif #ifdef __VMS__ #pragma message disable nosimpint #endif #include #include #include #include #ifdef __VMS__ #pragma message enable nosimpint #endif #include "wx/motif/private.h" #define WIDTH_OVERHEAD 48 #define WIDTH_OVERHEAD_SUBTRACT 40 #define HEIGHT_OVERHEAD 15 IMPLEMENT_DYNAMIC_CLASS(wxChoice, wxControl) void wxChoiceCallback (Widget w, XtPointer clientData, XtPointer ptr); wxChoice::wxChoice() { Init(); } void wxChoice::Init() { m_noStrings = 0; m_buttonWidget = (WXWidget) 0; m_menuWidget = (WXWidget) 0; m_formWidget = (WXWidget) 0; } bool wxChoice::Create(wxWindow *parent, wxWindowID id, const wxPoint& pos, const wxSize& size, int n, const wxString choices[], long style, const wxValidator& validator, const wxString& name) { if ( !CreateControl(parent, id, pos, size, style, validator, name) ) return false; Widget parentWidget = (Widget) parent->GetClientWidget(); m_formWidget = (WXWidget) XtVaCreateManagedWidget(name.c_str(), xmRowColumnWidgetClass, parentWidget, XmNmarginHeight, 0, XmNmarginWidth, 0, XmNpacking, XmPACK_TIGHT, XmNorientation, XmHORIZONTAL, XmNresizeWidth, False, XmNresizeHeight, False, NULL); XtVaSetValues ((Widget) m_formWidget, XmNspacing, 0, NULL); /* * Create the popup menu */ m_menuWidget = (WXWidget) XmCreatePulldownMenu ((Widget) m_formWidget, wxMOTIF_STR("choiceMenu"), NULL, 0); if (n > 0) { int i; for (i = 0; i < n; i++) Append (choices[i]); } /* * Create button */ Arg args[10]; Cardinal argcnt = 0; XtSetArg (args[argcnt], XmNsubMenuId, (Widget) m_menuWidget); ++argcnt; XtSetArg (args[argcnt], XmNmarginWidth, 0); ++argcnt; XtSetArg (args[argcnt], XmNmarginHeight, 0); ++argcnt; XtSetArg (args[argcnt], XmNpacking, XmPACK_TIGHT); ++argcnt; m_buttonWidget = (WXWidget) XmCreateOptionMenu ((Widget) m_formWidget, wxMOTIF_STR("choiceButton"), args, argcnt); m_mainWidget = m_buttonWidget; XtManageChild ((Widget) m_buttonWidget); // New code from Roland Haenel (roland_haenel@ac.cybercity.de) // Some time ago, I reported a problem with wxChoice-items under // Linux and Motif 2.0 (they caused sporadic GPFs). Now it seems // that I have found the code responsible for this behaviour. #if XmVersion >= 1002 #if XmVersion < 2000 // JACS, 24/1/99: this seems to cause a malloc crash later on, e.g. // in controls sample. // // Widget optionLabel = XmOptionLabelGadget ((Widget) m_buttonWidget); // XtUnmanageChild (optionLabel); #endif #endif wxSize bestSize = GetBestSize(); if( size.x > 0 ) bestSize.x = size.x; if( size.y > 0 ) bestSize.y = size.y; XtVaSetValues((Widget) m_formWidget, XmNresizePolicy, XmRESIZE_NONE, NULL); ChangeFont(false); ChangeBackgroundColour(); AttachWidget (parent, m_buttonWidget, m_formWidget, pos.x, pos.y, bestSize.x, bestSize.y); return true; } bool wxChoice::Create(wxWindow *parent, wxWindowID id, const wxPoint& pos, const wxSize& size, const wxArrayString& choices, long style, const wxValidator& validator, const wxString& name) { wxCArrayString chs(choices); return Create(parent, id, pos, size, chs.GetCount(), chs.GetStrings(), style, validator, name); } wxChoice::~wxChoice() { // For some reason destroying the menuWidget // can cause crashes on some machines. It will // be deleted implicitly by deleting the parent form // anyway. // XtDestroyWidget (menuWidget); if (GetMainWidget()) { DetachWidget(GetMainWidget()); // Removes event handlers DetachWidget(m_formWidget); XtDestroyWidget((Widget) m_formWidget); m_formWidget = (WXWidget) 0; // Presumably the other widgets have been deleted now, via the form m_mainWidget = (WXWidget) 0; m_buttonWidget = (WXWidget) 0; } if ( HasClientObjectData() ) m_clientDataDict.DestroyData(); } static inline wxChar* MYcopystring(const wxChar* s) { wxChar* copy = new wxChar[wxStrlen(s) + 1]; return wxStrcpy(copy, s); } int wxChoice::DoInsert(const wxString& item, unsigned int pos) { #ifndef XmNpositionIndex wxCHECK_MSG( pos == GetCount(), -1, wxT("insert not implemented")); #endif Widget w = XtVaCreateManagedWidget (GetLabelText(item), #if wxUSE_GADGETS xmPushButtonGadgetClass, (Widget) m_menuWidget, #else xmPushButtonWidgetClass, (Widget) m_menuWidget, #endif #ifdef XmNpositionIndex XmNpositionIndex, pos, #endif NULL); wxDoChangeBackgroundColour((WXWidget) w, m_backgroundColour); if( m_font.Ok() ) wxDoChangeFont( w, m_font ); m_widgetArray.Insert(w, pos); char mnem = wxFindMnemonic (item); if (mnem != 0) XtVaSetValues (w, XmNmnemonic, mnem, NULL); XtAddCallback (w, XmNactivateCallback, (XtCallbackProc) wxChoiceCallback, (XtPointer) this); if (m_noStrings == 0 && m_buttonWidget) { XtVaSetValues ((Widget) m_buttonWidget, XmNmenuHistory, w, NULL); Widget label = XmOptionButtonGadget ((Widget) m_buttonWidget); wxXmString text( item ); XtVaSetValues (label, XmNlabelString, text(), NULL); } // need to ditch wxStringList for wxArrayString m_stringList.Insert(pos, MYcopystring(item)); m_noStrings ++; return pos; } int wxChoice::DoAppend(const wxString& item) { return DoInsert(item, GetCount()); } void wxChoice::Delete(unsigned int n) { Widget w = (Widget)m_widgetArray[n]; XtRemoveCallback(w, XmNactivateCallback, (XtCallbackProc)wxChoiceCallback, (XtPointer)this); m_stringList.Erase(m_stringList.Item(n)); m_widgetArray.RemoveAt(size_t(n)); m_clientDataDict.Delete(n, HasClientObjectData()); XtDestroyWidget(w); m_noStrings--; } void wxChoice::Clear() { m_stringList.Clear (); unsigned int i; for (i = 0; i < m_noStrings; i++) { XtRemoveCallback((Widget) m_widgetArray[i], XmNactivateCallback, (XtCallbackProc)wxChoiceCallback, (XtPointer)this); XtUnmanageChild ((Widget) m_widgetArray[i]); XtDestroyWidget ((Widget) m_widgetArray[i]); } m_widgetArray.Clear(); if (m_buttonWidget) XtVaSetValues ((Widget) m_buttonWidget, XmNmenuHistory, (Widget) NULL, NULL); if ( HasClientObjectData() ) m_clientDataDict.DestroyData(); m_noStrings = 0; } int wxChoice::GetSelection() const { XmString text; Widget label = XmOptionButtonGadget ((Widget) m_buttonWidget); XtVaGetValues (label, XmNlabelString, &text, NULL); wxXmString freeMe(text); wxString s = wxXmStringToString( text ); if (!s.empty()) { int i = 0; for (wxStringList::compatibility_iterator node = m_stringList.GetFirst (); node; node = node->GetNext ()) { if (wxStrcmp(node->GetData(), s.c_str()) == 0) { return i; } else i++; } // for() return -1; } return -1; } void wxChoice::SetSelection(int n) { m_inSetValue = true; wxStringList::compatibility_iterator node = m_stringList.Item(n); if (node) { #if 0 Dimension selectionWidth, selectionHeight; #endif wxXmString text( node->GetData() ); // MBN: this seems silly, at best, and causes wxChoices to be clipped: // will remove "soon" #if 0 XtVaGetValues ((Widget) m_widgetArray[n], XmNwidth, &selectionWidth, XmNheight, &selectionHeight, NULL); #endif Widget label = XmOptionButtonGadget ((Widget) m_buttonWidget); XtVaSetValues (label, XmNlabelString, text(), NULL); #if 0 XtVaSetValues ((Widget) m_buttonWidget, XmNwidth, selectionWidth, XmNheight, selectionHeight, XmNmenuHistory, (Widget) m_widgetArray[n], NULL); #endif } m_inSetValue = false; } wxString wxChoice::GetString(unsigned int n) const { wxStringList::compatibility_iterator node = m_stringList.Item(n); if (node) return node->GetData(); else return wxEmptyString; } void wxChoice::SetColumns(int n) { if (n<1) n = 1 ; short numColumns = (short)n ; Arg args[3]; XtSetArg(args[0], XmNnumColumns, numColumns); XtSetArg(args[1], XmNpacking, XmPACK_COLUMN); XtSetValues((Widget) m_menuWidget,args,2) ; } int wxChoice::GetColumns(void) const { short numColumns ; XtVaGetValues((Widget) m_menuWidget,XmNnumColumns,&numColumns,NULL) ; return numColumns ; } void wxChoice::SetFocus() { XmProcessTraversal(XtParent((Widget)m_mainWidget), XmTRAVERSE_CURRENT); } void wxChoice::DoSetSize(int x, int y, int width, int height, int sizeFlags) { XtVaSetValues((Widget) m_formWidget, XmNresizePolicy, XmRESIZE_ANY, NULL); bool managed = XtIsManaged((Widget) m_formWidget); if (managed) XtUnmanageChild ((Widget) m_formWidget); int actualWidth = width - WIDTH_OVERHEAD_SUBTRACT, actualHeight = height - HEIGHT_OVERHEAD; if (width > -1) { unsigned int i; for (i = 0; i < m_noStrings; i++) XtVaSetValues ((Widget) m_widgetArray[i], XmNwidth, actualWidth, NULL); XtVaSetValues ((Widget) m_buttonWidget, XmNwidth, actualWidth, NULL); } if (height > -1) { #if 0 unsigned int i; for (i = 0; i < m_noStrings; i++) XtVaSetValues ((Widget) m_widgetArray[i], XmNheight, actualHeight, NULL); #endif XtVaSetValues ((Widget) m_buttonWidget, XmNheight, actualHeight, NULL); } if (managed) XtManageChild ((Widget) m_formWidget); XtVaSetValues((Widget) m_formWidget, XmNresizePolicy, XmRESIZE_NONE, NULL); wxControl::DoSetSize (x, y, width, height, sizeFlags); } void wxChoice::Command(wxCommandEvent & event) { SetSelection (event.GetInt()); ProcessCommand (event); } void wxChoiceCallback (Widget w, XtPointer clientData, XtPointer WXUNUSED(ptr)) { wxChoice *item = (wxChoice *) clientData; if (item) { if (item->InSetValue()) return; int n = item->GetWidgets().Index(w); if (n != wxNOT_FOUND) { wxCommandEvent event(wxEVT_COMMAND_CHOICE_SELECTED, item->GetId()); event.SetEventObject(item); event.SetInt(n); event.SetString( item->GetStrings().Item(n)->GetData() ); if ( item->HasClientObjectData() ) event.SetClientObject( item->GetClientObject(n) ); else if ( item->HasClientUntypedData() ) event.SetClientData( item->GetClientData(n) ); item->ProcessCommand (event); } } } void wxChoice::ChangeFont(bool keepOriginalSize) { // Note that this causes the widget to be resized back // to its original size! We therefore have to set the size // back again. TODO: a better way in Motif? if (m_font.Ok()) { Display* dpy = XtDisplay((Widget) m_mainWidget); int width, height, width1, height1; GetSize(& width, & height); WXString fontTag = wxFont::GetFontTag(); XtVaSetValues ((Widget) m_formWidget, fontTag, m_font.GetFontTypeC(dpy), NULL); XtVaSetValues ((Widget) m_buttonWidget, fontTag, m_font.GetFontTypeC(dpy), NULL); for( unsigned int i = 0; i < m_noStrings; ++i ) XtVaSetValues( (Widget)m_widgetArray[i], fontTag, m_font.GetFontTypeC(dpy), NULL ); GetSize(& width1, & height1); if (keepOriginalSize && (width != width1 || height != height1)) { SetSize(wxDefaultCoord, wxDefaultCoord, width, height); } } } void wxChoice::ChangeBackgroundColour() { wxDoChangeBackgroundColour(m_formWidget, m_backgroundColour); wxDoChangeBackgroundColour(m_buttonWidget, m_backgroundColour); wxDoChangeBackgroundColour(m_menuWidget, m_backgroundColour); unsigned int i; for (i = 0; i < m_noStrings; i++) wxDoChangeBackgroundColour(m_widgetArray[i], m_backgroundColour); } void wxChoice::ChangeForegroundColour() { wxDoChangeForegroundColour(m_formWidget, m_foregroundColour); wxDoChangeForegroundColour(m_buttonWidget, m_foregroundColour); wxDoChangeForegroundColour(m_menuWidget, m_foregroundColour); unsigned int i; for (i = 0; i < m_noStrings; i++) wxDoChangeForegroundColour(m_widgetArray[i], m_foregroundColour); } unsigned int wxChoice::GetCount() const { return m_noStrings; } void wxChoice::DoSetItemClientData(unsigned int n, void* clientData) { m_clientDataDict.Set(n, (wxClientData*)clientData, false); } void* wxChoice::DoGetItemClientData(unsigned int n) const { return (void*)m_clientDataDict.Get(n); } void wxChoice::DoSetItemClientObject(unsigned int n, wxClientData* clientData) { // don't delete, wxItemContainer does that for us m_clientDataDict.Set(n, clientData, false); } wxClientData* wxChoice::DoGetItemClientObject(unsigned int n) const { return m_clientDataDict.Get(n); } void wxChoice::SetString(unsigned int WXUNUSED(n), const wxString& WXUNUSED(s)) { wxFAIL_MSG( wxT("wxChoice::SetString not implemented") ); } wxSize wxChoice::GetItemsSize() const { int x, y, mx = 0, my = 0; // get my GetTextExtent( "|", &x, &my ); wxStringList::compatibility_iterator curr = m_stringList.GetFirst(); while( curr ) { GetTextExtent( curr->GetData(), &x, &y ); mx = wxMax( mx, x ); my = wxMax( my, y ); curr = curr->GetNext(); } return wxSize( mx, my ); } wxSize wxChoice::DoGetBestSize() const { wxSize items = GetItemsSize(); // FIXME arbitrary constants return wxSize( ( items.x ? items.x + WIDTH_OVERHEAD : 120 ), items.y + HEIGHT_OVERHEAD ); } #endif // wxUSE_CHOICE