listbox.cpp

来自「A*算法 A*算法 A*算法 A*算法A*算法A*算法」· C++ 代码 · 共 1,516 行 · 第 1/3 页

CPP
1,516
字号
/////////////////////////////////////////////////////////////////////////////
// Name:        univ/listbox.cpp
// Purpose:     wxListBox implementation
// Author:      Vadim Zeitlin
// Modified by:
// Created:     30.08.00
// RCS-ID:      $Id: listbox.cpp,v 1.31 2005/02/13 17:08:27 VZ Exp $
// Copyright:   (c) 2000 SciTech Software, Inc. (www.scitechsoft.com)
// Licence:     wxWindows licence
/////////////////////////////////////////////////////////////////////////////

// ============================================================================
// declarations
// ============================================================================

// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------

#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
    #pragma implementation "univlistbox.h"
#endif

#include "wx/wxprec.h"

#ifdef __BORLANDC__
    #pragma hdrstop
#endif

#if wxUSE_LISTBOX

#ifndef WX_PRECOMP
    #include "wx/log.h"

    #include "wx/dcclient.h"
    #include "wx/listbox.h"
    #include "wx/validate.h"
#endif

#include "wx/univ/renderer.h"
#include "wx/univ/inphand.h"
#include "wx/univ/theme.h"

// ============================================================================
// implementation of wxListBox
// ============================================================================

IMPLEMENT_DYNAMIC_CLASS(wxListBox, wxControl)

BEGIN_EVENT_TABLE(wxListBox, wxListBoxBase)
    EVT_SIZE(wxListBox::OnSize)
END_EVENT_TABLE()

// ----------------------------------------------------------------------------
// construction
// ----------------------------------------------------------------------------

void wxListBox::Init()
{
    // will be calculated later when needed
    m_lineHeight = 0;
    m_itemsPerPage = 0;
    m_maxWidth = 0;
    m_scrollRangeY = 0;
    m_maxWidthItem = -1;
    m_strings = NULL;

    // no items hence no current item
    m_current = -1;
    m_selAnchor = -1;
    m_currentChanged = false;

    // no need to update anything initially
    m_updateCount = 0;

    // no scrollbars to show nor update
    m_updateScrollbarX =
    m_showScrollbarX =
    m_updateScrollbarY =
    m_showScrollbarY = false;
}

wxListBox::wxListBox(wxWindow *parent,
                     wxWindowID id,
                     const wxPoint &pos,
                     const wxSize &size,
                     const wxArrayString& choices,
                     long style,
                     const wxValidator& validator,
                     const wxString &name)
{
    Init();

    Create(parent, id, pos, size, choices, style, validator, name);
}

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);
}

bool wxListBox::Create(wxWindow *parent,
                       wxWindowID id,
                       const wxPoint &pos,
                       const wxSize &size,
                       int n,
                       const wxString choices[],
                       long style,
                       const wxValidator& validator,
                       const wxString &name)
{
    // for compatibility accept both the new and old styles - they mean the
    // same thing for us
    if ( style & wxLB_ALWAYS_SB )
        style |= wxALWAYS_SHOW_SB;

    // if we don't have neither multiple nor extended flag, we must have the
    // single selection listbox
    if ( !(style & (wxLB_MULTIPLE | wxLB_EXTENDED)) )
        style |= wxLB_SINGLE;

#if wxUSE_TWO_WINDOWS
    style |=  wxVSCROLL|wxHSCROLL;
    if ((style & wxBORDER_MASK) == 0)
        style |= wxBORDER_SUNKEN;
#endif

    if ( !wxControl::Create(parent, id, pos, size, style,
                            validator, name) )
        return false;

    SetWindow(this);

    m_strings = new wxArrayString;

    Set(n, choices);

    SetBestSize(size);

    CreateInputHandler(wxINP_HANDLER_LISTBOX);

    return true;
}

wxListBox::~wxListBox()
{
    // call this just to free the client data -- and avoid leaking memory
    DoClear();

    delete m_strings;

    m_strings = NULL;
}

// ----------------------------------------------------------------------------
// adding/inserting strings
// ----------------------------------------------------------------------------

int wxCMPFUNC_CONV wxListBoxSortNoCase(wxString* s1, wxString* s2)
{
    return  s1->CmpNoCase(*s2);
}

int wxListBox::DoAppendOnly(const wxString& item)
{
    size_t index;

    if ( IsSorted() )
    {
        m_strings->Add(item);
        m_strings->Sort(wxListBoxSortNoCase);
        index = m_strings->Index(item);
    }
    else
    {
        index = m_strings->GetCount();
        m_strings->Add(item);
    }

    return index;
}

int wxListBox::DoAppend(const wxString& item)
{
    size_t index = DoAppendOnly( item );

    m_itemsClientData.Insert(NULL, index);

    m_updateScrollbarY = true;

    if ( HasHorzScrollbar() )
    {
        // has the max width increased?
        wxCoord width;
        GetTextExtent(item, &width, NULL);
        if ( width > m_maxWidth )
        {
            m_maxWidth = width;
            m_maxWidthItem = index;
            m_updateScrollbarX = true;
        }
    }

    RefreshFromItemToEnd(index);

    return index;
}

void wxListBox::DoInsertItems(const wxArrayString& items, int pos)
{
    // the position of the item being added to a sorted listbox can't be
    // specified
    wxCHECK_RET( !IsSorted(), _T("can't insert items into sorted listbox") );

    size_t count = items.GetCount();
    for ( size_t n = 0; n < count; n++ )
    {
        m_strings->Insert(items[n], pos + n);
        m_itemsClientData.Insert(NULL, pos + n);
    }

    // the number of items has changed so we might have to show the scrollbar
    m_updateScrollbarY = true;

    // the max width also might have changed - just recalculate it instead of
    // keeping track of it here, this is probably more efficient for a typical
    // use pattern
    RefreshHorzScrollbar();

    // note that we have to refresh all the items after the ones we inserted,
    // not just these items
    RefreshFromItemToEnd(pos);
}

void wxListBox::DoSetItems(const wxArrayString& items, void **clientData)
{
    DoClear();

    size_t count = items.GetCount();
    if ( !count )
        return;

    m_strings->Alloc(count);

    m_itemsClientData.Alloc(count);
    for ( size_t n = 0; n < count; n++ )
    {
        size_t index = DoAppendOnly(items[n]);

        m_itemsClientData.Insert(clientData ? clientData[n] : NULL, index);
    }

    m_updateScrollbarY = true;

    RefreshAll();
}

void wxListBox::SetString(int n, const wxString& s)
{
    wxCHECK_RET( !IsSorted(), _T("can't set string in sorted listbox") );

    (*m_strings)[n] = s;

    if ( HasHorzScrollbar() )
    {
        // we need to update m_maxWidth as changing the string may cause the
        // horz scrollbar [dis]appear
        wxCoord width;

        GetTextExtent(s, &width, NULL);

        // it might have increased if the new string is long
        if ( width > m_maxWidth )
        {
            m_maxWidth = width;
            m_maxWidthItem = n;
            m_updateScrollbarX = true;
        }
        // or also decreased if the old string was the longest one
        else if ( n == m_maxWidthItem )
        {
            RefreshHorzScrollbar();
        }
    }

    RefreshItem(n);
}

// ----------------------------------------------------------------------------
// removing strings
// ----------------------------------------------------------------------------

void wxListBox::DoClear()
{
    m_strings->Clear();

    if ( HasClientObjectData() )
    {
        size_t count = m_itemsClientData.GetCount();
        for ( size_t n = 0; n < count; n++ )
        {
            delete (wxClientData *) m_itemsClientData[n];
        }
    }

    m_itemsClientData.Clear();
    m_selections.Clear();

    m_current = -1;
}

void wxListBox::Clear()
{
    DoClear();

    m_updateScrollbarY = true;

    RefreshHorzScrollbar();

    RefreshAll();
}

void wxListBox::Delete(int n)
{
    wxCHECK_RET( n >= 0 && n < GetCount(),
                 _T("invalid index in wxListBox::Delete") );

    // do it before removing the index as otherwise the last item will not be
    // refreshed (as GetCount() will be decremented)
    RefreshFromItemToEnd(n);

    m_strings->RemoveAt(n);

    if ( HasClientObjectData() )
    {
        delete (wxClientData *)m_itemsClientData[n];
    }

    m_itemsClientData.RemoveAt(n);

    // when the item disappears we must not keep using its index
    if ( n == m_current )
    {
        m_current = -1;
    }
    else if ( n < m_current )
    {
        m_current--;
    }
    //else: current item may stay

    // update the selections array: the indices of all seletected items after
    // the one being deleted must change and the item itselfm ust be removed
    int index = wxNOT_FOUND;
    size_t count = m_selections.GetCount();
    for ( size_t item = 0; item < count; item++ )
    {
        if ( m_selections[item] == n )
        {
            // remember to delete it later
            index = item;
        }
        else if ( m_selections[item] > n )
        {
            // to account for the index shift
            m_selections[item]--;
        }
        //else: nothing changed for this one
    }

    if ( index != wxNOT_FOUND )
    {
        m_selections.RemoveAt(index);
    }

    // the number of items has changed, hence the scrollbar may disappear
    m_updateScrollbarY = true;

    // finally, if the longest item was deleted the scrollbar may disappear
    if ( n == m_maxWidthItem )
    {
        RefreshHorzScrollbar();
    }
}

// ----------------------------------------------------------------------------
// client data handling
// ----------------------------------------------------------------------------

void wxListBox::DoSetItemClientData(int n, void* clientData)
{
    m_itemsClientData[n] = clientData;
}

void *wxListBox::DoGetItemClientData(int n) const
{
    return m_itemsClientData[n];
}

void wxListBox::DoSetItemClientObject(int n, wxClientData* clientData)
{
    m_itemsClientData[n] = clientData;
}

wxClientData* wxListBox::DoGetItemClientObject(int n) const
{
    return (wxClientData *)m_itemsClientData[n];
}

// ----------------------------------------------------------------------------
// selection
// ----------------------------------------------------------------------------

void wxListBox::DoSetSelection(int n, bool select)
{
    if ( select )
    {
        if ( m_selections.Index(n) == wxNOT_FOUND )
        {
            if ( !HasMultipleSelection() )
            {
                // selecting an item in a single selection listbox deselects
                // all the others
                DeselectAll();
            }

            m_selections.Add(n);

            RefreshItem(n);
        }
        //else: already selected
    }
    else // unselect
    {
        int index = m_selections.Index(n);
        if ( index != wxNOT_FOUND )
        {
            m_selections.RemoveAt(index);

            RefreshItem(n);
        }
        //else: not selected
    }

    // sanity check: a single selection listbox can't have more than one item
    // selected
    wxASSERT_MSG( HasMultipleSelection() || (m_selections.GetCount() < 2),
                  _T("multiple selected items in single selection lbox?") );

    if ( select )
    {
        // the newly selected item becomes the current one
        SetCurrentItem(n);
    }
}

int wxListBox::GetSelection() const
{
    wxCHECK_MSG( !HasMultipleSelection(), -1,
                 _T("use wxListBox::GetSelections for ths listbox") );

    return m_selections.IsEmpty() ? -1 : m_selections[0];
}

int wxCMPFUNC_CONV wxCompareInts(int *n, int *m)
{
    return *n - *m;
}

int wxListBox::GetSelections(wxArrayInt& selections) const
{
    // always return sorted array to the user
    selections = m_selections;
    size_t count = m_selections.GetCount();

    // don't call sort on an empty array
    if ( count )
    {
        selections.Sort(wxCompareInts);
    }

    return count;
}

// ----------------------------------------------------------------------------
// refresh logic: we use delayed refreshing which allows to avoid multiple
// refreshes (and hence flicker) in case when several listbox items are
// added/deleted/changed subsequently
// ----------------------------------------------------------------------------

void wxListBox::RefreshFromItemToEnd(int from)
{
    RefreshItems(from, GetCount() - from);
}

void wxListBox::RefreshItems(int from, int count)

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?