git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@42081 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
1572 lines
42 KiB
C++
1572 lines
42 KiB
C++
/////////////////////////////////////////////////////////////////////////////
|
|
// Name: src/univ/listbox.cpp
|
|
// Purpose: wxListBox implementation
|
|
// Author: Vadim Zeitlin
|
|
// Modified by:
|
|
// Created: 30.08.00
|
|
// RCS-ID: $Id$
|
|
// Copyright: (c) 2000 SciTech Software, Inc. (www.scitechsoft.com)
|
|
// Licence: wxWindows licence
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
// ============================================================================
|
|
// declarations
|
|
// ============================================================================
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// headers
|
|
// ----------------------------------------------------------------------------
|
|
|
|
#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"
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxStdListboxInputHandler: handles mouse and kbd in a single or multi
|
|
// selection listbox
|
|
// ----------------------------------------------------------------------------
|
|
|
|
class WXDLLEXPORT wxStdListboxInputHandler : public wxStdInputHandler
|
|
{
|
|
public:
|
|
// if pressing the mouse button in a multiselection listbox should toggle
|
|
// the item under mouse immediately, then specify true as the second
|
|
// parameter (this is the standard behaviour, under GTK the item is toggled
|
|
// only when the mouse is released in the multi selection listbox)
|
|
wxStdListboxInputHandler(wxInputHandler *inphand,
|
|
bool toggleOnPressAlways = true);
|
|
|
|
// base class methods
|
|
virtual bool HandleKey(wxInputConsumer *consumer,
|
|
const wxKeyEvent& event,
|
|
bool pressed);
|
|
virtual bool HandleMouse(wxInputConsumer *consumer,
|
|
const wxMouseEvent& event);
|
|
virtual bool HandleMouseMove(wxInputConsumer *consumer,
|
|
const wxMouseEvent& event);
|
|
|
|
protected:
|
|
// return the item under mouse, 0 if the mouse is above the listbox or
|
|
// GetCount() if it is below it
|
|
int HitTest(const wxListBox *listbox, const wxMouseEvent& event);
|
|
|
|
// parts of HitTest(): first finds the pseudo (because not in range) index
|
|
// of the item and the second one adjusts it if necessary - that is if the
|
|
// third one returns false
|
|
int HitTestUnsafe(const wxListBox *listbox, const wxMouseEvent& event);
|
|
int FixItemIndex(const wxListBox *listbox, int item);
|
|
bool IsValidIndex(const wxListBox *listbox, int item);
|
|
|
|
// init m_btnCapture and m_actionMouse
|
|
wxControlAction SetupCapture(wxListBox *lbox,
|
|
const wxMouseEvent& event,
|
|
int item);
|
|
|
|
wxRenderer *m_renderer;
|
|
|
|
// the button which initiated the mouse capture (currently 0 or 1)
|
|
int m_btnCapture;
|
|
|
|
// the action to perform when the mouse moves while we capture it
|
|
wxControlAction m_actionMouse;
|
|
|
|
// the ctor parameter toggleOnPressAlways (see comments near it)
|
|
bool m_toggleOnPressAlways;
|
|
|
|
// do we track the mouse outside the window when it is captured?
|
|
bool m_trackMouseOutside;
|
|
};
|
|
|
|
// ============================================================================
|
|
// 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)
|
|
:wxScrollHelper(this)
|
|
{
|
|
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;
|
|
|
|
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)
|
|
{
|
|
unsigned int 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, unsigned 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") );
|
|
|
|
unsigned int count = items.GetCount();
|
|
for ( unsigned int 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();
|
|
|
|
unsigned int count = items.GetCount();
|
|
if ( !count )
|
|
return;
|
|
|
|
m_strings->Alloc(count);
|
|
|
|
m_itemsClientData.Alloc(count);
|
|
for ( unsigned int n = 0; n < count; n++ )
|
|
{
|
|
unsigned int index = DoAppendOnly(items[n]);
|
|
|
|
m_itemsClientData.Insert(clientData ? clientData[n] : NULL, index);
|
|
}
|
|
|
|
m_updateScrollbarY = true;
|
|
|
|
RefreshAll();
|
|
}
|
|
|
|
void wxListBox::SetString(unsigned 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 == (unsigned int)m_maxWidthItem )
|
|
{
|
|
RefreshHorzScrollbar();
|
|
}
|
|
}
|
|
|
|
RefreshItem(n);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// removing strings
|
|
// ----------------------------------------------------------------------------
|
|
|
|
void wxListBox::DoClear()
|
|
{
|
|
m_strings->Clear();
|
|
|
|
if ( HasClientObjectData() )
|
|
{
|
|
unsigned int count = m_itemsClientData.GetCount();
|
|
for ( unsigned int 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(unsigned int n)
|
|
{
|
|
wxCHECK_RET( IsValid(n),
|
|
_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 ( (int)n == m_current )
|
|
{
|
|
m_current = -1;
|
|
}
|
|
else if ( (int)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;
|
|
unsigned int count = m_selections.GetCount();
|
|
for ( unsigned int item = 0; item < count; item++ )
|
|
{
|
|
if ( m_selections[item] == (int)n )
|
|
{
|
|
// remember to delete it later
|
|
index = item;
|
|
}
|
|
else if ( m_selections[item] > (int)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 ( (int)n == m_maxWidthItem )
|
|
{
|
|
RefreshHorzScrollbar();
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// client data handling
|
|
// ----------------------------------------------------------------------------
|
|
|
|
void wxListBox::DoSetItemClientData(unsigned int n, void* clientData)
|
|
{
|
|
m_itemsClientData[n] = clientData;
|
|
}
|
|
|
|
void *wxListBox::DoGetItemClientData(unsigned int n) const
|
|
{
|
|
return m_itemsClientData[n];
|
|
}
|
|
|
|
void wxListBox::DoSetItemClientObject(unsigned int n, wxClientData* clientData)
|
|
{
|
|
m_itemsClientData[n] = clientData;
|
|
}
|
|
|
|
wxClientData* wxListBox::DoGetItemClientObject(unsigned 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();
|
|
return;
|
|
}
|
|
|
|
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(), wxNOT_FOUND,
|
|
_T("use wxListBox::GetSelections for ths listbox") );
|
|
|
|
return m_selections.IsEmpty() ? wxNOT_FOUND : 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;
|
|
unsigned int 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)
|
|
{
|
|
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?
|
|
unsigned int nLines = GetCount();
|
|
wxCoord lineHeight = GetLineHeight();
|
|
bool showScrollbarY = (int)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();
|
|
unsigned int 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;
|
|
unsigned int count = m_strings->GetCount();
|
|
for ( unsigned int 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;
|
|
|
|
unsigned int count = m_strings->GetCount();
|
|
for ( unsigned int 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)
|
|
{
|
|
unsigned int count = GetCount();
|
|
if ( count==0 )
|
|
{
|
|
// 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 == (int)(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 < (int)count && last < (int)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 != (int)last; item < (int)(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));
|
|
}
|
|
}
|
|
}
|
|
|
|
void wxListBox::ChangeCurrent(int diff)
|
|
{
|
|
int current = m_current == -1 ? 0 : m_current;
|
|
|
|
current += diff;
|
|
|
|
int last = GetCount() - 1;
|
|
if ( current < 0 )
|
|
current = 0;
|
|
else if ( current > last )
|
|
current = last;
|
|
|
|
SetCurrentItem(current);
|
|
}
|
|
|
|
void wxListBox::ExtendSelection(int itemTo)
|
|
{
|
|
// if we don't have the explicit values for selection start/end, make them
|
|
// up
|
|
if ( m_selAnchor == -1 )
|
|
m_selAnchor = m_current;
|
|
|
|
if ( itemTo == -1 )
|
|
itemTo = m_current;
|
|
|
|
// swap the start/end of selection range if necessary
|
|
int itemFrom = m_selAnchor;
|
|
if ( itemFrom > itemTo )
|
|
{
|
|
int itemTmp = itemFrom;
|
|
itemFrom = itemTo;
|
|
itemTo = itemTmp;
|
|
}
|
|
|
|
// the selection should now include all items in the range between the
|
|
// anchor and the specified item and only them
|
|
|
|
int n;
|
|
for ( n = 0; n < itemFrom; n++ )
|
|
{
|
|
Deselect(n);
|
|
}
|
|
|
|
for ( ; n <= itemTo; n++ )
|
|
{
|
|
SetSelection(n);
|
|
}
|
|
|
|
unsigned int count = GetCount();
|
|
for ( ; n < (int)count; n++ )
|
|
{
|
|
Deselect(n);
|
|
}
|
|
}
|
|
|
|
void wxListBox::DoSelect(int item, bool sel)
|
|
{
|
|
if ( item != -1 )
|
|
{
|
|
// go to this item first
|
|
SetCurrentItem(item);
|
|
}
|
|
|
|
// the current item is the one we want to change: either it was just
|
|
// changed above to be the same as item or item == -1 in which we case we
|
|
// are supposed to use the current one anyhow
|
|
if ( m_current != -1 )
|
|
{
|
|
// [de]select it
|
|
SetSelection(m_current, sel);
|
|
}
|
|
}
|
|
|
|
void wxListBox::SelectAndNotify(int item)
|
|
{
|
|
DoSelect(item);
|
|
|
|
SendEvent(wxEVT_COMMAND_LISTBOX_SELECTED);
|
|
}
|
|
|
|
void wxListBox::Activate(int item)
|
|
{
|
|
if ( item != -1 )
|
|
SetCurrentItem(item);
|
|
else
|
|
item = m_current;
|
|
|
|
if ( !(GetWindowStyle() & wxLB_MULTIPLE) )
|
|
{
|
|
DeselectAll(item);
|
|
}
|
|
|
|
if ( item != -1 )
|
|
{
|
|
DoSelect(item);
|
|
|
|
SendEvent(wxEVT_COMMAND_LISTBOX_DOUBLECLICKED);
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// input handling
|
|
// ----------------------------------------------------------------------------
|
|
|
|
/*
|
|
The numArg here is the listbox item index while the strArg is used
|
|
differently for the different actions:
|
|
|
|
a) for wxACTION_LISTBOX_FIND it has the natural meaning: this is the string
|
|
to find
|
|
|
|
b) for wxACTION_LISTBOX_SELECT and wxACTION_LISTBOX_EXTENDSEL it is used
|
|
to decide if the listbox should send the notification event (it is empty)
|
|
or not (it is not): this allows us to reuse the same action for when the
|
|
user is dragging the mouse when it has been released although in the
|
|
first case no notification is sent while in the second it is sent.
|
|
*/
|
|
bool wxListBox::PerformAction(const wxControlAction& action,
|
|
long numArg,
|
|
const wxString& strArg)
|
|
{
|
|
int item = (int)numArg;
|
|
|
|
if ( action == wxACTION_LISTBOX_SETFOCUS )
|
|
{
|
|
SetCurrentItem(item);
|
|
}
|
|
else if ( action == wxACTION_LISTBOX_ACTIVATE )
|
|
{
|
|
Activate(item);
|
|
}
|
|
else if ( action == wxACTION_LISTBOX_TOGGLE )
|
|
{
|
|
if ( item == -1 )
|
|
item = m_current;
|
|
|
|
if ( IsSelected(item) )
|
|
DoUnselect(item);
|
|
else
|
|
SelectAndNotify(item);
|
|
}
|
|
else if ( action == wxACTION_LISTBOX_SELECT )
|
|
{
|
|
DeselectAll(item);
|
|
|
|
if ( strArg.empty() )
|
|
SelectAndNotify(item);
|
|
else
|
|
DoSelect(item);
|
|
}
|
|
else if ( action == wxACTION_LISTBOX_SELECTADD )
|
|
DoSelect(item);
|
|
else if ( action == wxACTION_LISTBOX_UNSELECT )
|
|
DoUnselect(item);
|
|
else if ( action == wxACTION_LISTBOX_MOVEDOWN )
|
|
ChangeCurrent(1);
|
|
else if ( action == wxACTION_LISTBOX_MOVEUP )
|
|
ChangeCurrent(-1);
|
|
else if ( action == wxACTION_LISTBOX_PAGEDOWN )
|
|
ChangeCurrent(GetItemsPerPage());
|
|
else if ( action == wxACTION_LISTBOX_PAGEUP )
|
|
ChangeCurrent(-GetItemsPerPage());
|
|
else if ( action == wxACTION_LISTBOX_START )
|
|
SetCurrentItem(0);
|
|
else if ( action == wxACTION_LISTBOX_END )
|
|
SetCurrentItem(GetCount() - 1);
|
|
else if ( action == wxACTION_LISTBOX_UNSELECTALL )
|
|
DeselectAll(item);
|
|
else if ( action == wxACTION_LISTBOX_EXTENDSEL )
|
|
ExtendSelection(item);
|
|
else if ( action == wxACTION_LISTBOX_FIND )
|
|
FindNextItem(strArg);
|
|
else if ( action == wxACTION_LISTBOX_ANCHOR )
|
|
AnchorSelection(item == -1 ? m_current : item);
|
|
else if ( action == wxACTION_LISTBOX_SELECTALL ||
|
|
action == wxACTION_LISTBOX_SELTOGGLE )
|
|
wxFAIL_MSG(_T("unimplemented yet"));
|
|
else
|
|
return wxControl::PerformAction(action, numArg, strArg);
|
|
|
|
return true;
|
|
}
|
|
|
|
/* static */
|
|
wxInputHandler *wxListBox::GetStdInputHandler(wxInputHandler *handlerDef)
|
|
{
|
|
static wxStdListboxInputHandler s_handler(handlerDef);
|
|
|
|
return &s_handler;
|
|
}
|
|
|
|
// ============================================================================
|
|
// implementation of wxStdListboxInputHandler
|
|
// ============================================================================
|
|
|
|
wxStdListboxInputHandler::wxStdListboxInputHandler(wxInputHandler *handler,
|
|
bool toggleOnPressAlways)
|
|
: wxStdInputHandler(handler)
|
|
{
|
|
m_btnCapture = 0;
|
|
m_toggleOnPressAlways = toggleOnPressAlways;
|
|
m_actionMouse = wxACTION_NONE;
|
|
m_trackMouseOutside = true;
|
|
}
|
|
|
|
int wxStdListboxInputHandler::HitTest(const wxListBox *lbox,
|
|
const wxMouseEvent& event)
|
|
{
|
|
int item = HitTestUnsafe(lbox, event);
|
|
|
|
return FixItemIndex(lbox, item);
|
|
}
|
|
|
|
int wxStdListboxInputHandler::HitTestUnsafe(const wxListBox *lbox,
|
|
const wxMouseEvent& event)
|
|
{
|
|
wxPoint pt = event.GetPosition();
|
|
pt -= lbox->GetClientAreaOrigin();
|
|
int y;
|
|
lbox->CalcUnscrolledPosition(0, pt.y, NULL, &y);
|
|
return y / lbox->GetLineHeight();
|
|
}
|
|
|
|
int wxStdListboxInputHandler::FixItemIndex(const wxListBox *lbox,
|
|
int item)
|
|
{
|
|
if ( item < 0 )
|
|
{
|
|
// mouse is above the first item
|
|
item = 0;
|
|
}
|
|
else if ( (unsigned int)item >= lbox->GetCount() )
|
|
{
|
|
// mouse is below the last item
|
|
item = lbox->GetCount() - 1;
|
|
}
|
|
|
|
return item;
|
|
}
|
|
|
|
bool wxStdListboxInputHandler::IsValidIndex(const wxListBox *lbox, int item)
|
|
{
|
|
return item >= 0 && (unsigned int)item < lbox->GetCount();
|
|
}
|
|
|
|
wxControlAction
|
|
wxStdListboxInputHandler::SetupCapture(wxListBox *lbox,
|
|
const wxMouseEvent& event,
|
|
int item)
|
|
{
|
|
// we currently only allow selecting with the left mouse button, if we
|
|
// do need to allow using other buttons too we might use the code
|
|
// inside #if 0
|
|
#if 0
|
|
m_btnCapture = event.LeftDown()
|
|
? 1
|
|
: event.RightDown()
|
|
? 3
|
|
: 2;
|
|
#else
|
|
m_btnCapture = 1;
|
|
#endif // 0/1
|
|
|
|
wxControlAction action;
|
|
if ( lbox->HasMultipleSelection() )
|
|
{
|
|
if ( lbox->GetWindowStyle() & wxLB_MULTIPLE )
|
|
{
|
|
if ( m_toggleOnPressAlways )
|
|
{
|
|
// toggle the item right now
|
|
action = wxACTION_LISTBOX_TOGGLE;
|
|
}
|
|
//else: later
|
|
|
|
m_actionMouse = wxACTION_LISTBOX_SETFOCUS;
|
|
}
|
|
else // wxLB_EXTENDED listbox
|
|
{
|
|
// simple click in an extended sel listbox clears the old
|
|
// selection and adds the clicked item to it then, ctrl-click
|
|
// toggles an item to it and shift-click adds a range between
|
|
// the old selection anchor and the clicked item
|
|
if ( event.ControlDown() )
|
|
{
|
|
lbox->PerformAction(wxACTION_LISTBOX_ANCHOR, item);
|
|
|
|
action = wxACTION_LISTBOX_TOGGLE;
|
|
}
|
|
else if ( event.ShiftDown() )
|
|
{
|
|
action = wxACTION_LISTBOX_EXTENDSEL;
|
|
}
|
|
else // simple click
|
|
{
|
|
lbox->PerformAction(wxACTION_LISTBOX_ANCHOR, item);
|
|
|
|
action = wxACTION_LISTBOX_SELECT;
|
|
}
|
|
|
|
m_actionMouse = wxACTION_LISTBOX_EXTENDSEL;
|
|
}
|
|
}
|
|
else // single selection
|
|
{
|
|
m_actionMouse =
|
|
action = wxACTION_LISTBOX_SELECT;
|
|
}
|
|
|
|
// by default we always do track it
|
|
m_trackMouseOutside = true;
|
|
|
|
return action;
|
|
}
|
|
|
|
bool wxStdListboxInputHandler::HandleKey(wxInputConsumer *consumer,
|
|
const wxKeyEvent& event,
|
|
bool pressed)
|
|
{
|
|
// we're only interested in the key press events
|
|
if ( pressed && !event.AltDown() )
|
|
{
|
|
bool isMoveCmd = true;
|
|
int style = consumer->GetInputWindow()->GetWindowStyle();
|
|
|
|
wxControlAction action;
|
|
wxString strArg;
|
|
|
|
int keycode = event.GetKeyCode();
|
|
switch ( keycode )
|
|
{
|
|
// movement
|
|
case WXK_UP:
|
|
action = wxACTION_LISTBOX_MOVEUP;
|
|
break;
|
|
|
|
case WXK_DOWN:
|
|
action = wxACTION_LISTBOX_MOVEDOWN;
|
|
break;
|
|
|
|
case WXK_PAGEUP:
|
|
action = wxACTION_LISTBOX_PAGEUP;
|
|
break;
|
|
|
|
case WXK_PAGEDOWN:
|
|
action = wxACTION_LISTBOX_PAGEDOWN;
|
|
break;
|
|
|
|
case WXK_HOME:
|
|
action = wxACTION_LISTBOX_START;
|
|
break;
|
|
|
|
case WXK_END:
|
|
action = wxACTION_LISTBOX_END;
|
|
break;
|
|
|
|
// selection
|
|
case WXK_SPACE:
|
|
if ( style & wxLB_MULTIPLE )
|
|
{
|
|
action = wxACTION_LISTBOX_TOGGLE;
|
|
isMoveCmd = false;
|
|
}
|
|
break;
|
|
|
|
case WXK_RETURN:
|
|
action = wxACTION_LISTBOX_ACTIVATE;
|
|
isMoveCmd = false;
|
|
break;
|
|
|
|
default:
|
|
if ( (keycode < 255) && wxIsalnum((wxChar)keycode) )
|
|
{
|
|
action = wxACTION_LISTBOX_FIND;
|
|
strArg = (wxChar)keycode;
|
|
}
|
|
}
|
|
|
|
if ( !action.IsEmpty() )
|
|
{
|
|
consumer->PerformAction(action, -1, strArg);
|
|
|
|
if ( isMoveCmd )
|
|
{
|
|
if ( style & wxLB_SINGLE )
|
|
{
|
|
// the current item is always the one selected
|
|
consumer->PerformAction(wxACTION_LISTBOX_SELECT);
|
|
}
|
|
else if ( style & wxLB_EXTENDED )
|
|
{
|
|
if ( event.ShiftDown() )
|
|
consumer->PerformAction(wxACTION_LISTBOX_EXTENDSEL);
|
|
else
|
|
{
|
|
// select the item and make it the new selection anchor
|
|
consumer->PerformAction(wxACTION_LISTBOX_SELECT);
|
|
consumer->PerformAction(wxACTION_LISTBOX_ANCHOR);
|
|
}
|
|
}
|
|
//else: nothing to do for multiple selection listboxes
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return wxStdInputHandler::HandleKey(consumer, event, pressed);
|
|
}
|
|
|
|
bool wxStdListboxInputHandler::HandleMouse(wxInputConsumer *consumer,
|
|
const wxMouseEvent& event)
|
|
{
|
|
wxListBox *lbox = wxStaticCast(consumer->GetInputWindow(), wxListBox);
|
|
int item = HitTest(lbox, event);
|
|
wxControlAction action;
|
|
|
|
// when the left mouse button is pressed, capture the mouse and track the
|
|
// item under mouse (if the mouse leaves the window, we will still be
|
|
// getting the mouse move messages generated by wxScrollWindow)
|
|
if ( event.LeftDown() )
|
|
{
|
|
// capture the mouse to track the selected item
|
|
lbox->CaptureMouse();
|
|
|
|
action = SetupCapture(lbox, event, item);
|
|
}
|
|
else if ( m_btnCapture && event.ButtonUp(m_btnCapture) )
|
|
{
|
|
// when the left mouse button is released, release the mouse too
|
|
wxWindow *winCapture = wxWindow::GetCapture();
|
|
if ( winCapture )
|
|
{
|
|
winCapture->ReleaseMouse();
|
|
m_btnCapture = 0;
|
|
|
|
action = m_actionMouse;
|
|
}
|
|
//else: the mouse wasn't presed over the listbox, only released here
|
|
}
|
|
else if ( event.LeftDClick() )
|
|
{
|
|
action = wxACTION_LISTBOX_ACTIVATE;
|
|
}
|
|
|
|
if ( !action.IsEmpty() )
|
|
{
|
|
lbox->PerformAction(action, item);
|
|
|
|
return true;
|
|
}
|
|
|
|
return wxStdInputHandler::HandleMouse(consumer, event);
|
|
}
|
|
|
|
bool wxStdListboxInputHandler::HandleMouseMove(wxInputConsumer *consumer,
|
|
const wxMouseEvent& event)
|
|
{
|
|
wxWindow *winCapture = wxWindow::GetCapture();
|
|
if ( winCapture && (event.GetEventObject() == winCapture) )
|
|
{
|
|
wxListBox *lbox = wxStaticCast(consumer->GetInputWindow(), wxListBox);
|
|
|
|
if ( !m_btnCapture || !m_trackMouseOutside )
|
|
{
|
|
// someone captured the mouse for us (we always set m_btnCapture
|
|
// when we do it ourselves): in this case we only react to
|
|
// the mouse messages when they happen inside the listbox
|
|
if ( lbox->HitTest(event.GetPosition()) != wxHT_WINDOW_INSIDE )
|
|
return false;
|
|
}
|
|
|
|
int item = HitTest(lbox, event);
|
|
if ( !m_btnCapture )
|
|
{
|
|
// now that we have the mouse inside the listbox, do capture it
|
|
// normally - but ensure that we will still ignore the outside
|
|
// events
|
|
SetupCapture(lbox, event, item);
|
|
|
|
m_trackMouseOutside = false;
|
|
}
|
|
|
|
if ( IsValidIndex(lbox, item) )
|
|
{
|
|
// pass something into strArg to tell the listbox that it shouldn't
|
|
// send the notification message: see PerformAction() above
|
|
lbox->PerformAction(m_actionMouse, item, _T("no"));
|
|
}
|
|
// else: don't pass invalid index to the listbox
|
|
}
|
|
else // we don't have capture any more
|
|
{
|
|
if ( m_btnCapture )
|
|
{
|
|
// if we lost capture unexpectedly (someone else took the capture
|
|
// from us), return to a consistent state
|
|
m_btnCapture = 0;
|
|
}
|
|
}
|
|
|
|
return wxStdInputHandler::HandleMouseMove(consumer, event);
|
|
}
|
|
|
|
#endif // wxUSE_LISTBOX
|