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 + -
显示快捷键?