X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/4bb6408c2631988fab9925014c6619358bf867de..bbd55ff9564dcaf1a5d2ee93d789c4d656baabef:/src/motif/listbox.cpp diff --git a/src/motif/listbox.cpp b/src/motif/listbox.cpp index 9352c32bc2..4d59337023 100644 --- a/src/motif/listbox.cpp +++ b/src/motif/listbox.cpp @@ -1,5 +1,5 @@ /////////////////////////////////////////////////////////////////////////////// -// Name: listbox.cpp +// Name: src/motif/listbox.cpp // Purpose: wxListBox // Author: Julian Smart // Modified by: @@ -9,18 +9,63 @@ // Licence: wxWindows licence /////////////////////////////////////////////////////////////////////////////// -#ifdef __GNUG__ -#pragma implementation "listbox.h" -#endif +// For compilers that support precompilation, includes "wx.h". +#include "wx/wxprec.h" + +#if wxUSE_LISTBOX #include "wx/listbox.h" -#include "wx/settings.h" -#include "wx/dynarray.h" -#include "wx/log.h" -#if !USE_SHARED_LIBRARY - IMPLEMENT_DYNAMIC_CLASS(wxListBox, wxControl) +#ifndef WX_PRECOMP + #include "wx/dynarray.h" + #include "wx/log.h" + #include "wx/utils.h" + #include "wx/settings.h" + #include "wx/arrstr.h" +#endif + +#ifdef __VMS__ +#pragma message disable nosimpint #endif +#include +#ifdef __VMS__ +#pragma message enable nosimpint +#endif +#include "wx/motif/private.h" + +IMPLEMENT_DYNAMIC_CLASS(wxListBox, wxControlWithItems) + +static void wxListBoxCallback(Widget w, + XtPointer clientData, + XmListCallbackStruct * cbs); + +// ---------------------------------------------------------------------------- +// wxSizeKeeper +// ---------------------------------------------------------------------------- + +// helper class to reduce code duplication +class wxSizeKeeper +{ + int m_x, m_y; + int m_w, m_h; + wxWindow* m_wnd; +public: + wxSizeKeeper( wxWindow* w ) + : m_wnd( w ) + { + m_wnd->GetSize( &m_w, &m_h ); + m_wnd->GetPosition( &m_x, &m_y ); + } + + void Restore() + { + int x, y; + + m_wnd->GetSize( &x, &y ); + if( x != m_x || y != m_y ) + m_wnd->SetSize( m_x, m_y, m_w, m_h ); + } +}; // ============================================================================ // list box control implementation @@ -29,8 +74,7 @@ // Listbox item wxListBox::wxListBox() { - m_noItems = 0; - m_selected = 0; + m_noItems = 0; } bool wxListBox::Create(wxWindow *parent, wxWindowID id, @@ -41,195 +85,528 @@ bool wxListBox::Create(wxWindow *parent, wxWindowID id, const wxValidator& validator, const wxString& name) { - m_noItems = n; - m_selected = 0; + if( !wxControl::CreateControl( parent, id, pos, size, style, + validator, name ) ) + return false; + PreCreation(); + + m_noItems = (unsigned int)n; + + Widget parentWidget = (Widget) parent->GetClientWidget(); + Display* dpy = XtDisplay(parentWidget); + + Arg args[4]; + int count = 0; + XtSetArg( args[count], XmNlistSizePolicy, XmCONSTANT ); ++count; + XtSetArg( args[count], XmNselectionPolicy, + ( m_windowStyle & wxLB_MULTIPLE ) ? XmMULTIPLE_SELECT : + ( m_windowStyle & wxLB_EXTENDED ) ? XmEXTENDED_SELECT : + XmBROWSE_SELECT ); + ++count; + if( m_font.Ok() ) + { + XtSetArg( args[count], + (String)wxFont::GetFontTag(), m_font.GetFontTypeC(dpy) ); + ++count; + } + if( m_windowStyle & wxLB_ALWAYS_SB ) + { + XtSetArg( args[count], XmNscrollBarDisplayPolicy, XmSTATIC ); + ++count; + } - SetName(name); - SetValidator(validator); + Widget listWidget = + XmCreateScrolledList(parentWidget, + name.char_str(), args, count); + + m_mainWidget = (WXWidget) listWidget; + + Set(n, choices); + + XtManageChild (listWidget); + + wxSize best = GetBestSize(); + if( size.x != -1 ) best.x = size.x; + if( size.y != -1 ) best.y = size.y; + + XtAddCallback (listWidget, + XmNbrowseSelectionCallback, + (XtCallbackProc) wxListBoxCallback, + (XtPointer) this); + XtAddCallback (listWidget, + XmNextendedSelectionCallback, + (XtCallbackProc) wxListBoxCallback, + (XtPointer) this); + XtAddCallback (listWidget, + XmNmultipleSelectionCallback, + (XtCallbackProc) wxListBoxCallback, + (XtPointer) this); + XtAddCallback (listWidget, + XmNdefaultActionCallback, + (XtCallbackProc) wxListBoxCallback, + (XtPointer) this); + + PostCreation(); + AttachWidget (parent, m_mainWidget, (WXWidget) NULL, + pos.x, pos.y, best.x, best.y); + + return true; +} - if (parent) parent->AddChild(this); +bool wxListBox::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); +} - wxSystemSettings settings; - SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_WINDOW)); +void wxListBox::SetSelectionPolicy() +{ + Widget listBox = (Widget)m_mainWidget; + Arg args[3]; - m_windowId = ( id == -1 ) ? (int)NewControlId() : id; + XtSetArg( args[0], XmNlistSizePolicy, XmCONSTANT ); - // TODO create listbox + XtSetArg( args[1], XmNselectionPolicy, + ( m_windowStyle & wxLB_MULTIPLE ) ? XmMULTIPLE_SELECT : + ( m_windowStyle & wxLB_EXTENDED ) ? XmEXTENDED_SELECT : + XmBROWSE_SELECT ); - return FALSE; + XtSetValues( listBox, args, 2 ); } -wxListBox::~wxListBox() +void wxListBox::DoSetFirstItem( int N ) { + int count, length; + + if (!IsValid(N)) + return; + + XtVaGetValues ((Widget) m_mainWidget, + XmNvisibleItemCount, &count, + XmNitemCount, &length, + NULL); + if ((N + count) >= length) + N = length - count; + XmListSetPos ((Widget) m_mainWidget, N + 1); } -void wxListBox::SetFirstItem(int N) +void wxListBox::DoDeleteOneItem(unsigned int n) { - // TODO -} + Widget listBox = (Widget) m_mainWidget; -void wxListBox::SetFirstItem(const wxString& s) -{ - // TODO -} + XmListDeletePos (listBox, n + 1); -void wxListBox::Delete(int N) -{ - m_noItems --; - // TODO + wxListBoxBase::DoDeleteOneItem(n); + m_noItems --; } -void wxListBox::Append(const wxString& item) +int wxDoFindStringInList(Widget w, const wxString& s) { - m_noItems ++; + wxXmString str( s ); + int *positions = NULL; + int no_positions = 0; + bool success = XmListGetMatchPos (w, str(), + &positions, &no_positions); - // TODO + if (success && positions) + { + int pos = positions[0]; + XtFree ((char *) positions); + return pos - 1; + } + else + return -1; } -void wxListBox::Append(const wxString& item, char *Client_data) +int wxListBox::FindString(const wxString& s, bool WXUNUSED(bCase)) const { - m_noItems ++; + // FIXME: back to base class for not supported value of bCase - // TODO + return wxDoFindStringInList( (Widget)m_mainWidget, s ); } -void wxListBox::Set(int n, const wxString *choices, char** clientData) +void wxListBox::DoClear() { - m_noItems = n; + if (!m_noItems) + return; + + wxSizeKeeper sk( this ); + Widget listBox = (Widget) m_mainWidget; - // TODO + XmListDeleteAllItems (listBox); + + sk.Restore(); + + wxListBoxBase::DoClear(); + m_noItems = 0; } -int wxListBox::FindString(const wxString& s) const +void wxListBox::DoSetSelection(int N, bool select) { - // TODO - return -1; + m_inSetValue = true; + if (select) + { +#if 0 + if (m_windowStyle & wxLB_MULTIPLE) + { + int *selections = NULL; + int n = GetSelections (&selections); + + // This hack is supposed to work, to make it possible + // to select more than one item, but it DOESN'T under Motif 1.1. + + XtVaSetValues ((Widget) m_mainWidget, + XmNselectionPolicy, XmMULTIPLE_SELECT, + NULL); + + int i; + for (i = 0; i < n; i++) + XmListSelectPos ((Widget) m_mainWidget, + selections[i] + 1, False); + + XmListSelectPos ((Widget) m_mainWidget, N + 1, False); + + XtVaSetValues ((Widget) m_mainWidget, + XmNselectionPolicy, XmEXTENDED_SELECT, + NULL); + } + else +#endif // 0 + XmListSelectPos ((Widget) m_mainWidget, N + 1, False); + + } + else + XmListDeselectPos ((Widget) m_mainWidget, N + 1); + + m_inSetValue = false; } -void wxListBox::Clear() +bool wxListBox::IsSelected(int N) const { - m_noItems = 0; - // TODO + // In Motif, no simple way to determine if the item is selected. + wxArrayInt theSelections; + int count = GetSelections (theSelections); + if (count == 0) + return false; + else + { + int j; + for (j = 0; j < count; j++) + if (theSelections[j] == N) + return true; + } + return false; } -void wxListBox::SetSelection(int N, bool select) +// Return number of selections and an array of selected integers +int wxListBox::GetSelections(wxArrayInt& aSelections) const { - // TODO + aSelections.Empty(); + + Widget listBox = (Widget) m_mainWidget; + int *posList = NULL; + int posCnt = 0; + bool flag = XmListGetSelectedPos (listBox, &posList, &posCnt); + if (flag) + { + if (posCnt > 0) + { + aSelections.Alloc(posCnt); + + int i; + for (i = 0; i < posCnt; i++) + aSelections.Add(posList[i] - 1); + + XtFree ((char *) posList); + return posCnt; + } + else + return 0; + } + else + return 0; } -bool wxListBox::Selected(int N) const +// Get single selection, for single choice list items +int wxDoGetSelectionInList(Widget listBox) { - // TODO - return FALSE; + int *posList = NULL; + int posCnt = 0; + bool flag = XmListGetSelectedPos (listBox, &posList, &posCnt); + if (flag) + { + int id = -1; + if (posCnt > 0) + id = posList[0] - 1; + XtFree ((char *) posList); + return id; + } + else + return -1; } -void wxListBox::Deselect(int N) +int wxListBox::GetSelection() const { - // TODO + return wxDoGetSelectionInList((Widget) m_mainWidget); } -char *wxListBox::GetClientData(int N) const +// Find string for position +wxString wxDoGetStringInList( Widget listBox, int n ) { - // TODO - return (char *)NULL; + XmString *strlist; + int count; + XtVaGetValues( listBox, + XmNitemCount, &count, + XmNitems, &strlist, + NULL ); + if( n < count && n >= 0 ) + return wxXmStringToString( strlist[n] ); + else + return wxEmptyString; } -void wxListBox::SetClientData(int N, char *Client_data) +wxString wxListBox::GetString(unsigned int n) const { - // TODO + return wxDoGetStringInList( (Widget)m_mainWidget, n ); } -// Return number of selections and an array of selected integers -int wxListBox::GetSelections(wxArrayInt& aSelections) const +int wxListBox::DoInsertItems(const wxArrayStringsAdapter & items, + unsigned int pos, + void **clientData, wxClientDataType type) { - aSelections.Empty(); + Widget listBox = (Widget) m_mainWidget; -/* TODO - if ((m_windowStyle & wxLB_MULTIPLE) || (m_windowStyle & wxLB_EXTENDED)) - { - int no_sel = ?? - for ( int n = 0; n < no_sel; n++ ) - aSelections.Add(??); + const unsigned int numItems = items.GetCount(); - return no_sel; - } - else // single-selection listbox + XmString *text = new XmString[numItems]; + unsigned int i; +#if XmVersion > 1001 + for (i = 0; i < numItems; i++) { - aSelections.Add(??); + text[i] = wxStringToXmString(items[i]); + } + XmListAddItemsUnselected(listBox, text, numItems, GetMotifPosition(pos)); + InsertNewItemsClientData(pos, numItems, clientData, type); +#else + AllocClientData(numItems); - return 1; + unsigned int idx = pos; + for ( i = 0; i < numItems; i++, idx++ ) + { + text[i] = wxStringToXmString(items[i]); + XmListAddItemUnselected(listBox, text[i], GetMotifPosition(idx)); + InsertNewItemClientData(idx, clientData, i, type); } -*/ - return 0; +#endif + for (i = 0; i < numItems; i++) + XmStringFree(text[i]); + delete[] text; + + m_noItems += numItems; + + SetSelectionPolicy(); + + return pos + numItems - 1; } -// Get single selection, for single choice list items -int wxListBox::GetSelection() const +void wxListBox::SetString(unsigned int n, const wxString& s) { - // TODO - return -1; + wxSizeKeeper sk( this ); + Widget listBox = (Widget) m_mainWidget; + + wxXmString text( s ); + + // delete the item and add it again. + // FIXME isn't there a way to change it in place? + XmListDeletePos (listBox, n+1); + XmListAddItem (listBox, text(), n+1); + + sk.Restore(); } -// Find string for position -wxString wxListBox::GetString(int N) const +void wxListBox::Command (wxCommandEvent & event) { - // TODO - return wxString(""); + if (event.GetExtraLong()) + SetSelection (event.GetInt()); + else + { + Deselect (event.GetInt()); + return; + } + ProcessCommand (event); } -void wxListBox::SetSize(int x, int y, int width, int height, int sizeFlags) +void wxListBoxCallback (Widget WXUNUSED(w), XtPointer clientData, + XmListCallbackStruct * cbs) { - // TODO + wxListBox *item = (wxListBox *) clientData; + + if (item->InSetValue()) + return; + + wxEventType evtType; + + if( cbs->reason == XmCR_DEFAULT_ACTION ) + evtType = wxEVT_COMMAND_LISTBOX_DOUBLECLICKED; + else + evtType = wxEVT_COMMAND_LISTBOX_SELECTED; + + int n = cbs->item_position - 1; + wxCommandEvent event (evtType, item->GetId()); + if ( item->HasClientObjectData() ) + event.SetClientObject( item->GetClientObject(n) ); + else if ( item->HasClientUntypedData() ) + event.SetClientData( item->GetClientData(n) ); + event.SetInt(n); + event.SetExtraLong(true); + event.SetEventObject(item); + event.SetString( item->GetString( n ) ); + + int x = -1; + if( NULL != cbs->event && cbs->event->type == ButtonRelease ) + { + XButtonEvent* evt = (XButtonEvent*)cbs->event; + + x = evt->x; + } + + switch (cbs->reason) + { + case XmCR_MULTIPLE_SELECT: + case XmCR_BROWSE_SELECT: +#if wxUSE_CHECKLISTBOX + item->DoToggleItem( n, x ); +#endif + case XmCR_DEFAULT_ACTION: + item->HandleWindowEvent(event); + break; + case XmCR_EXTENDED_SELECT: + switch (cbs->selection_type) + { + case XmINITIAL: + case XmADDITION: + case XmMODIFICATION: + item->DoToggleItem( n, x ); + item->HandleWindowEvent(event); + break; + } + break; + } } -void wxListBox::InsertItems(int nItems, const wxString items[], int pos) +WXWidget wxListBox::GetTopWidget() const { - m_noItems += nItems; - - // TODO + return (WXWidget) XtParent( (Widget) m_mainWidget ); } -void wxListBox::SetString(int N, const wxString& s) +void wxListBox::ChangeBackgroundColour() { - // TODO + wxWindow::ChangeBackgroundColour(); + + Widget parent = XtParent ((Widget) m_mainWidget); + Widget hsb, vsb; + + XtVaGetValues (parent, + XmNhorizontalScrollBar, &hsb, + XmNverticalScrollBar, &vsb, + NULL); + + /* TODO: should scrollbars be affected? Should probably have separate + * function to change them (by default, taken from wxSystemSettings) + */ + wxColour backgroundColour = wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE); + wxDoChangeBackgroundColour((WXWidget) hsb, backgroundColour, true); + wxDoChangeBackgroundColour((WXWidget) vsb, backgroundColour, true); + + XtVaSetValues (hsb, + XmNtroughColor, backgroundColour.AllocColour(XtDisplay(hsb)), + NULL); + XtVaSetValues (vsb, + XmNtroughColor, backgroundColour.AllocColour(XtDisplay(vsb)), + NULL); + + // MBN: why change parent's background? It looks really ugly. + // wxDoChangeBackgroundColour((WXWidget) parent, m_backgroundColour, true); } -int wxListBox::Number () const +void wxListBox::ChangeForegroundColour() { - return m_noItems; + wxWindow::ChangeForegroundColour(); + + Widget parent = XtParent ((Widget) m_mainWidget); + Widget hsb, vsb; + + XtVaGetValues(parent, + XmNhorizontalScrollBar, &hsb, + XmNverticalScrollBar, &vsb, + NULL); + + /* TODO: should scrollbars be affected? Should probably have separate + function to change them (by default, taken from wxSystemSettings) + + wxDoChangeForegroundColour((WXWidget) hsb, m_foregroundColour); + wxDoChangeForegroundColour((WXWidget) vsb, m_foregroundColour); + wxDoChangeForegroundColour((WXWidget) parent, m_foregroundColour); + */ } -// For single selection items only -wxString wxListBox::GetStringSelection () const +unsigned int wxListBox::GetCount() const { - int sel = GetSelection (); - if (sel > -1) - return this->GetString (sel); - else - return wxString(""); + return m_noItems; } -bool wxListBox::SetStringSelection (const wxString& s, bool flag) +#define LIST_SCROLL_SPACING 6 + +wxSize wxDoGetListBoxBestSize( Widget listWidget, const wxWindow* window ) { - int sel = FindString (s); - if (sel > -1) + int max; + Dimension spacing, highlight, xmargin, ymargin, shadow; + int width = 0; + int x, y; + + XtVaGetValues( listWidget, + XmNitemCount, &max, + XmNlistSpacing, &spacing, + XmNhighlightThickness, &highlight, + XmNlistMarginWidth, &xmargin, + XmNlistMarginHeight, &ymargin, + XmNshadowThickness, &shadow, + NULL ); + + for( size_t i = 0; i < (size_t)max; ++i ) { - SetSelection (sel, flag); - return TRUE; + window->GetTextExtent( wxDoGetStringInList( listWidget, i ), &x, &y ); + width = wxMax( width, x ); } - else - return FALSE; + + // use some arbitrary value if there are no strings + if( width == 0 ) + width = 100; + + // get my + window->GetTextExtent( "v", &x, &y ); + + // make it a little larger than widest string, plus the scrollbar + width += wxSystemSettings::GetMetric( wxSYS_VSCROLL_X ) + + 2 * highlight + LIST_SCROLL_SPACING + 2 * xmargin + 2 * shadow; + + // at least 3 items, at most 10 + int height = wxMax( 3, wxMin( 10, max ) ) * + ( y + spacing + 2 * highlight ) + 2 * ymargin + 2 * shadow; + + return wxSize( width, height ); } -void wxListBox::Command (wxCommandEvent & event) +wxSize wxListBox::DoGetBestSize() const { - if (event.m_extraLong) - SetSelection (event.m_commandInt); - else - { - Deselect (event.m_commandInt); - return; - } - ProcessCommand (event); + return wxDoGetListBoxBestSize( (Widget)m_mainWidget, this ); } +#endif // wxUSE_LISTBOX