listbox.cpp

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

CPP
1,516
字号
{
    switch ( m_updateCount )
    {
        case 0:
            m_updateFrom = from;
            m_updateCount = count;
            break;

        case -1:
            // we refresh everything anyhow
            break;

        default:
            // add these items to the others which we have to refresh
            if ( m_updateFrom < from )
            {
                count += from - m_updateFrom;
                if ( m_updateCount < count )
                    m_updateCount = count;
            }
            else // m_updateFrom >= from
            {
                int updateLast = wxMax(m_updateFrom + m_updateCount,
                                       from + count);
                m_updateFrom = from;
                m_updateCount = updateLast - m_updateFrom;
            }
    }
}

void wxListBox::RefreshItem(int n)
{
    switch ( m_updateCount )
    {
        case 0:
            // refresh this item only
            m_updateFrom = n;
            m_updateCount = 1;
            break;

        case -1:
            // we refresh everything anyhow
            break;

        default:
            // add this item to the others which we have to refresh
            if ( m_updateFrom < n )
            {
                if ( m_updateCount < n - m_updateFrom + 1 )
                    m_updateCount = n - m_updateFrom + 1;
            }
            else // n <= m_updateFrom
            {
                m_updateCount += m_updateFrom - n;
                m_updateFrom = n;
            }
    }
}

void wxListBox::RefreshAll()
{
    m_updateCount = -1;
}

void wxListBox::RefreshHorzScrollbar()
{
    m_maxWidth = 0; // recalculate it
    m_updateScrollbarX = true;
}

void wxListBox::UpdateScrollbars()
{
    wxSize size = GetClientSize();

    // is our height enough to show all items?
    int nLines = GetCount();
    wxCoord lineHeight = GetLineHeight();
    bool showScrollbarY = nLines*lineHeight > size.y;

    // check the width too if required
    wxCoord charWidth, maxWidth;
    bool showScrollbarX;
    if ( HasHorzScrollbar() )
    {
        charWidth = GetCharWidth();
        maxWidth = GetMaxWidth();
        showScrollbarX = maxWidth > size.x;
    }
    else // never show it
    {
        charWidth = maxWidth = 0;
        showScrollbarX = false;
    }

    // what should be the scrollbar range now?
    int scrollRangeX = showScrollbarX
                        ? (maxWidth + charWidth - 1) / charWidth + 2 // FIXME
                        : 0;
    int scrollRangeY = showScrollbarY
                        ? nLines +
                            (size.y % lineHeight + lineHeight - 1) / lineHeight
                        : 0;

    // reset scrollbars if something changed: either the visibility status
    // or the range of a scrollbar which is shown
    if ( (showScrollbarY != m_showScrollbarY) ||
         (showScrollbarX != m_showScrollbarX) ||
         (showScrollbarY && (scrollRangeY != m_scrollRangeY)) ||
         (showScrollbarX && (scrollRangeX != m_scrollRangeX)) )
    {
        int x, y;
        GetViewStart(&x, &y);
        SetScrollbars(charWidth, lineHeight,
                      scrollRangeX, scrollRangeY,
                      x, y);

        m_showScrollbarX = showScrollbarX;
        m_showScrollbarY = showScrollbarY;

        m_scrollRangeX = scrollRangeX;
        m_scrollRangeY = scrollRangeY;
    }
}

void wxListBox::UpdateItems()
{
    // only refresh the items which must be refreshed
    if ( m_updateCount == -1 )
    {
        // refresh all
        wxLogTrace(_T("listbox"), _T("Refreshing all"));

        Refresh();
    }
    else
    {
        wxSize size = GetClientSize();
        wxRect rect;
        rect.width = size.x;
        rect.height = size.y;
        rect.y += m_updateFrom*GetLineHeight();
        rect.height = m_updateCount*GetLineHeight();

        // we don't need to calculate x position as we always refresh the
        // entire line(s)
        CalcScrolledPosition(0, rect.y, NULL, &rect.y);

        wxLogTrace(_T("listbox"), _T("Refreshing items %d..%d (%d-%d)"),
                   m_updateFrom, m_updateFrom + m_updateCount - 1,
                   rect.GetTop(), rect.GetBottom());

        Refresh(true, &rect);
    }
}

void wxListBox::OnInternalIdle()
{
    if ( m_updateScrollbarY || m_updateScrollbarX )
    {
        UpdateScrollbars();

        m_updateScrollbarX =
        m_updateScrollbarY = false;
    }

    if ( m_currentChanged )
    {
        DoEnsureVisible(m_current);

        m_currentChanged = false;
    }

    if ( m_updateCount )
    {
        UpdateItems();

        m_updateCount = 0;
    }
    wxListBoxBase::OnInternalIdle();
}

// ----------------------------------------------------------------------------
// drawing
// ----------------------------------------------------------------------------

wxBorder wxListBox::GetDefaultBorder() const
{
    return wxBORDER_SUNKEN;
}

void wxListBox::DoDraw(wxControlRenderer *renderer)
{
    // adjust the DC to account for scrolling
    wxDC& dc = renderer->GetDC();
    PrepareDC(dc);
    dc.SetFont(GetFont());

    // get the update rect
    wxRect rectUpdate = GetUpdateClientRect();

    int yTop, yBottom;
    CalcUnscrolledPosition(0, rectUpdate.GetTop(), NULL, &yTop);
    CalcUnscrolledPosition(0, rectUpdate.GetBottom(), NULL, &yBottom);

    // get the items which must be redrawn
    wxCoord lineHeight = GetLineHeight();
    size_t itemFirst = yTop / lineHeight,
           itemLast = (yBottom + lineHeight - 1) / lineHeight,
           itemMax = m_strings->GetCount();

    if ( itemFirst >= itemMax )
        return;

    if ( itemLast > itemMax )
        itemLast = itemMax;

    // do draw them
    wxLogTrace(_T("listbox"), _T("Repainting items %d..%d"),
               itemFirst, itemLast);

    DoDrawRange(renderer, itemFirst, itemLast);
}

void wxListBox::DoDrawRange(wxControlRenderer *renderer,
                            int itemFirst, int itemLast)
{
    renderer->DrawItems(this, itemFirst, itemLast);
}

// ----------------------------------------------------------------------------
// size calculations
// ----------------------------------------------------------------------------

bool wxListBox::SetFont(const wxFont& font)
{
    if ( !wxControl::SetFont(font) )
        return false;

    CalcItemsPerPage();

    RefreshAll();

    return true;
}

void wxListBox::CalcItemsPerPage()
{
    m_lineHeight = GetRenderer()->GetListboxItemHeight(GetCharHeight());
    m_itemsPerPage = GetClientSize().y / m_lineHeight;
}

int wxListBox::GetItemsPerPage() const
{
    if ( !m_itemsPerPage )
    {
        wxConstCast(this, wxListBox)->CalcItemsPerPage();
    }

    return m_itemsPerPage;
}

wxCoord wxListBox::GetLineHeight() const
{
    if ( !m_lineHeight )
    {
        wxConstCast(this, wxListBox)->CalcItemsPerPage();
    }

    return m_lineHeight;
}

wxCoord wxListBox::GetMaxWidth() const
{
    if ( m_maxWidth == 0 )
    {
        wxListBox *self = wxConstCast(this, wxListBox);
        wxCoord width;
        size_t count = m_strings->GetCount();
        for ( size_t n = 0; n < count; n++ )
        {
            GetTextExtent(this->GetString(n), &width, NULL);
            if ( width > m_maxWidth )
            {
                self->m_maxWidth = width;
                self->m_maxWidthItem = n;
            }
        }
    }

    return m_maxWidth;
}

void wxListBox::OnSize(wxSizeEvent& event)
{
    // recalculate the number of items per page
    CalcItemsPerPage();

    // the scrollbars might [dis]appear
    m_updateScrollbarX =
    m_updateScrollbarY = true;

    event.Skip();
}

void wxListBox::DoSetFirstItem(int n)
{
    SetCurrentItem(n);
}

void wxListBox::DoSetSize(int x, int y,
                          int width, int height,
                          int sizeFlags)
{
    if ( GetWindowStyle() & wxLB_INT_HEIGHT )
    {
        // we must round up the height to an entire number of rows

        // the client area must contain an int number of rows, so take borders
        // into account
        wxRect rectBorders = GetRenderer()->GetBorderDimensions(GetBorder());
        wxCoord hBorders = rectBorders.y + rectBorders.height;

        wxCoord hLine = GetLineHeight();
        height = ((height - hBorders + hLine - 1) / hLine)*hLine + hBorders;
    }

    wxListBoxBase::DoSetSize(x, y, width, height, sizeFlags);
}

wxSize wxListBox::DoGetBestClientSize() const
{
    wxCoord width = 0,
            height = 0;

    size_t count = m_strings->GetCount();
    for ( size_t n = 0; n < count; n++ )
    {
        wxCoord w,h;
        GetTextExtent(this->GetString(n), &w, &h);

        if ( w > width )
            width = w;
        if ( h > height )
            height = h;
    }

    // if the listbox is empty, still give it some non zero (even if
    // arbitrary) size - otherwise, leave small margin around the strings
    if ( !width )
        width = 100;
    else
        width += 3*GetCharWidth();

    if ( !height )
        height = GetCharHeight();

    // we need the height of the entire listbox, not just of one line
    height *= wxMax(count, 7);

    return wxSize(width, height);
}

// ----------------------------------------------------------------------------
// listbox actions
// ----------------------------------------------------------------------------

bool wxListBox::SendEvent(wxEventType type, int item)
{
    wxCommandEvent event(type, m_windowId);
    event.SetEventObject(this);

    // use the current item by default
    if ( item == -1 )
    {
        item = m_current;
    }

    // client data and string parameters only make sense if we have an item
    if ( item != -1 )
    {
        if ( HasClientObjectData() )
            event.SetClientObject(GetClientObject(item));
        else if ( HasClientUntypedData() )
            event.SetClientData(GetClientData(item));

        event.SetString(GetString(item));
    }

    event.SetInt(item);

    return GetEventHandler()->ProcessEvent(event);
}

void wxListBox::SetCurrentItem(int n)
{
    if ( n != m_current )
    {
        if ( m_current != -1 )
            RefreshItem(m_current);

        m_current = n;

        if ( m_current != -1 )
        {
            m_currentChanged = true;

            RefreshItem(m_current);
        }
    }
    //else: nothing to do
}

bool wxListBox::FindItem(const wxString& prefix, bool strictlyAfter)
{
    int count = GetCount();
    if ( !count )
    {
        // empty listbox, we can't find anything in it
        return false;
    }

    // start either from the current item or from the next one if strictlyAfter
    // is true
    int first;
    if ( strictlyAfter )
    {
        // the following line will set first correctly to 0 if there is no
        // selection (m_current == -1)
        first = m_current == count - 1 ? 0 : m_current + 1;
    }
    else // start with the current
    {
        first = m_current == -1 ? 0 : m_current;
    }

    int last = first == 0 ? count - 1 : first - 1;

    // if this is not true we'd never exit from the loop below!
    wxASSERT_MSG( first < count && last < count, _T("logic error") );

    // precompute it outside the loop
    size_t len = prefix.length();

    // loop over all items in the listbox
    for ( int item = first; item != last; item < count - 1 ? item++ : item = 0 )
    {
        if ( wxStrnicmp(this->GetString(item).c_str(), prefix, len) == 0 )
        {
            SetCurrentItem(item);

            if ( !(GetWindowStyle() & wxLB_MULTIPLE) )
            {
                DeselectAll(item);
                SelectAndNotify(item);

                if ( GetWindowStyle() & wxLB_EXTENDED )
                    AnchorSelection(item);
            }

            return true;
        }
    }

    // nothing found
    return false;
}

void wxListBox::EnsureVisible(int n)
{
    if ( m_updateScrollbarY )
    {
        UpdateScrollbars();

        m_updateScrollbarX =
        m_updateScrollbarY = false;
    }

    DoEnsureVisible(n);
}

void wxListBox::DoEnsureVisible(int n)
{
    if ( !m_showScrollbarY )
    {
        // nothing to do - everything is shown anyhow
        return;
    }

    int first;
    GetViewStart(0, &first);
    if ( first > n )
    {
        // we need to scroll upwards, so make the current item appear on top
        // of the shown range
        Scroll(0, n);
    }
    else
    {
        int last = first + GetClientSize().y / GetLineHeight() - 1;
        if ( last < n )
        {
            // scroll down: the current item appears at the bottom of the
            // range
            Scroll(0, n - (last - first));
        }
    }

⌨️ 快捷键说明

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