Implement incremental search in wxGenericListCtrl.

Mostly copy wxGenericTreeCtrl incremental search implementation to
wxGenericListCtrl (unfortunately there is no simple way to reuse this code
currently), including the recently added EnableBellOnNoMatch() method.

Update the sample to test it, the key event handling in it had to be modified
to allow it.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@72639 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Vadim Zeitlin 2012-10-07 22:42:27 +00:00
parent 27bc919446
commit d34d31f6d6
8 changed files with 237 additions and 22 deletions

View File

@ -566,6 +566,7 @@ All (GUI):
- Add "inherit" to <font> XRC tag (Steffen Olszewski, Gero Meßsysteme GmbH).
- Add support for wxALWAYS_SHOW_SB style to wxScrolled<> (Catalin Raceanu).
- Add wxTreeCtrl::EnableBellOnNoMatch() (Jonathan Dagresta).
- Implement incremental search in wxGenericListCtrl (Jonathan Dagresta).
wxGTK:

View File

@ -144,6 +144,8 @@ public:
void RefreshItem(long item);
void RefreshItems(long itemFrom, long itemTo);
virtual void EnableBellOnNoMatch(bool on);
#if WXWIN_COMPATIBILITY_2_6
// obsolete, don't use
wxDEPRECATED( int GetItemSpacing( bool isSmall ) const );

View File

@ -390,6 +390,27 @@ public:
void Notify();
};
//-----------------------------------------------------------------------------
// wxListFindTimer (internal)
//-----------------------------------------------------------------------------
class wxListFindTimer: public wxTimer
{
public:
// reset the current prefix after half a second of inactivity
enum { DELAY = 500 };
wxListFindTimer( wxListMainWindow *owner )
: m_owner(owner)
{
}
virtual void Notify();
private:
wxListMainWindow *m_owner;
};
//-----------------------------------------------------------------------------
// wxListTextCtrlWrapper: wraps a wxTextCtrl to make it work for inline editing
//-----------------------------------------------------------------------------
@ -556,6 +577,11 @@ public:
bool OnRenameAccept(size_t itemEdit, const wxString& value);
void OnRenameCancelled(size_t itemEdit);
void OnFindTimer();
// set whether or not to ring the find bell
// (does nothing on MSW - bell is always rung)
void EnableBellOnNoMatch( bool on );
void OnMouse( wxMouseEvent &event );
// called to switch the selection from the current item to newCurrent,
@ -729,6 +755,15 @@ protected:
bool m_lastOnSame;
wxTimer *m_renameTimer;
// incremental search data
wxString m_findPrefix;
wxTimer *m_findTimer;
// This flag is set to 0 if the bell is disabled, 1 if it is enabled and -1
// if it is globally enabled but has been temporarily disabled because we
// had already beeped for this particular search.
int m_findBell;
bool m_isCreated;
int m_dragCount;
wxPoint m_dragStart;
@ -779,6 +814,9 @@ protected:
// force us to recalculate the range of visible lines
void ResetVisibleLinesRange() { m_lineFrom = (size_t)-1; }
// find the first item starting with the given prefix after the given item
size_t PrefixFindItem(size_t item, const wxString& prefix) const;
// get the colour to be used for drawing the rules
wxColour GetRuleColour() const
{

View File

@ -454,6 +454,10 @@ public:
bool InReportView() const { return HasFlag(wxLC_REPORT); }
bool IsVirtual() const { return HasFlag(wxLC_VIRTUAL); }
// Enable or disable beep when incremental match doesn't find any item.
// Only implemented in the generic version currently.
virtual void EnableBellOnNoMatch(bool WXUNUSED(on) = true) { }
protected:
// Real implementations methods to which our public forwards.
virtual long DoInsertColumn(long col, const wxListItem& info) = 0;

View File

@ -407,6 +407,18 @@ public:
wxTextCtrl* EditLabel(long item,
wxClassInfo* textControlClass = wxCLASSINFO(wxTextCtrl));
/**
Enable or disable a beep if there is no match for the currently
entered text when searching for the item from keyboard.
The default is to not beep in this case except in wxMSW where the
beep is always generated by the native control and cannot be disabled,
i.e. calls to this function do nothing there.
@since 2.9.5
*/
void EnableBellOnNoMatch(bool on);
/**
Finish editing the label.

View File

@ -149,6 +149,7 @@ BEGIN_EVENT_TABLE(MyFrame, wxFrame)
EVT_MENU(LIST_THAW, MyFrame::OnThaw)
EVT_MENU(LIST_TOGGLE_LINES, MyFrame::OnToggleLines)
EVT_MENU(LIST_TOGGLE_HEADER, MyFrame::OnToggleHeader)
EVT_MENU(LIST_TOGGLE_BELL, MyFrame::OnToggleBell)
#ifdef __WXOSX__
EVT_MENU(LIST_MAC_USE_GENERIC, MyFrame::OnToggleMacUseGeneric)
#endif // __WXOSX__
@ -258,6 +259,7 @@ MyFrame::MyFrame(const wxChar *title)
menuList->Check(LIST_TOGGLE_MULTI_SEL, true);
menuList->AppendCheckItem(LIST_TOGGLE_HEADER, "Toggle &header\tCtrl-H");
menuList->Check(LIST_TOGGLE_HEADER, true);
menuList->AppendCheckItem(LIST_TOGGLE_BELL, "Toggle &bell on no match");
wxMenu *menuCol = new wxMenu;
menuCol->Append(LIST_SET_FG_COL, wxT("&Foreground colour..."));
@ -366,6 +368,11 @@ void MyFrame::OnToggleHeader(wxCommandEvent& event)
m_listCtrl->ToggleWindowStyle(wxLC_NO_HEADER);
}
void MyFrame::OnToggleBell(wxCommandEvent& event)
{
m_listCtrl->EnableBellOnNoMatch(event.IsChecked());
}
#ifdef __WXOSX__
void MyFrame::OnToggleMacUseGeneric(wxCommandEvent& event)
@ -468,6 +475,10 @@ void MyFrame::RecreateList(long flags, bool withText)
default:
wxFAIL_MSG( wxT("unknown listctrl mode") );
}
wxMenuBar* const mb = GetMenuBar();
if ( mb )
m_listCtrl->EnableBellOnNoMatch(mb->IsChecked(LIST_TOGGLE_BELL));
}
DoSize();
@ -1096,6 +1107,13 @@ void MyListCtrl::OnListKeyDown(wxListEvent& event)
{
long item;
if ( !wxGetKeyState(WXK_SHIFT) )
{
LogEvent(event, wxT("OnListKeyDown"));
event.Skip();
return;
}
switch ( event.GetKeyCode() )
{
case 'C': // colorize
@ -1237,26 +1255,7 @@ void MyListCtrl::OnChar(wxKeyEvent& event)
{
wxLogMessage(wxT("Got char event."));
switch ( event.GetKeyCode() )
{
case 'n':
case 'N':
case 'c':
case 'C':
case 'r':
case 'R':
case 'u':
case 'U':
case 'd':
case 'D':
case 'i':
case 'I':
// these are the keys we process ourselves
break;
default:
event.Skip();
}
event.Skip();
}
void MyListCtrl::OnRightClick(wxMouseEvent& event)

View File

@ -147,6 +147,7 @@ protected:
void OnThaw(wxCommandEvent& event);
void OnToggleLines(wxCommandEvent& event);
void OnToggleHeader(wxCommandEvent& event);
void OnToggleBell(wxCommandEvent& event);
#ifdef __WXOSX__
void OnToggleMacUseGeneric(wxCommandEvent& event);
#endif // __WXOSX__
@ -219,6 +220,7 @@ enum
LIST_SET_BG_COL,
LIST_TOGGLE_MULTI_SEL,
LIST_TOGGLE_HEADER,
LIST_TOGGLE_BELL,
LIST_TOGGLE_FIRST,
LIST_SHOW_COL_INFO,
LIST_SHOW_SEL_INFO,

View File

@ -1371,6 +1371,15 @@ void wxListRenameTimer::Notify()
m_owner->OnRenameTimer();
}
//-----------------------------------------------------------------------------
// wxListFindTimer (internal)
//-----------------------------------------------------------------------------
void wxListFindTimer::Notify()
{
m_owner->OnFindTimer();
}
//-----------------------------------------------------------------------------
// wxListTextCtrlWrapper (internal)
//-----------------------------------------------------------------------------
@ -1563,6 +1572,8 @@ void wxListMainWindow::Init()
m_lastOnSame = false;
m_renameTimer = new wxListRenameTimer( this );
m_findTimer = NULL;
m_findBell = 0; // default is to not ring bell at all
m_textctrlWrapper = NULL;
m_current =
@ -1625,6 +1636,7 @@ wxListMainWindow::~wxListMainWindow()
delete m_highlightBrush;
delete m_highlightUnfocusedBrush;
delete m_renameTimer;
delete m_findTimer;
}
void wxListMainWindow::SetReportView(bool inReportView)
@ -2288,6 +2300,18 @@ void wxListMainWindow::OnRenameCancelled(size_t itemEdit)
GetEventHandler()->ProcessEvent( le );
}
void wxListMainWindow::OnFindTimer()
{
m_findPrefix.clear();
if ( m_findBell )
m_findBell = 1;
}
void wxListMainWindow::EnableBellOnNoMatch( bool on )
{
m_findBell = on;
}
void wxListMainWindow::OnMouse( wxMouseEvent &event )
{
#ifdef __WXMAC__
@ -2801,7 +2825,8 @@ void wxListMainWindow::OnChar( wxKeyEvent &event )
event.m_keyCode = WXK_RIGHT;
}
switch ( event.GetKeyCode() )
int keyCode = event.GetKeyCode();
switch ( keyCode )
{
case WXK_UP:
if ( m_current > 0 )
@ -2899,7 +2924,79 @@ void wxListMainWindow::OnChar( wxKeyEvent &event )
break;
default:
event.Skip();
if ( !event.HasModifiers() &&
((keyCode >= '0' && keyCode <= '9') ||
(keyCode >= 'a' && keyCode <= 'z') ||
(keyCode >= 'A' && keyCode <= 'Z') ||
(keyCode == '_') ||
(keyCode == '+') ||
(keyCode == '*') ||
(keyCode == '-')))
{
// find the next item starting with the given prefix
wxChar ch = (wxChar)keyCode;
size_t item;
// if the same character is typed multiple times then go to the
// next entry starting with that character instead of searching
// for an item starting with multiple copies of this character,
// this is more useful and is how it works under Windows.
if ( m_findPrefix.length() == 1 && m_findPrefix[0] == ch )
{
item = PrefixFindItem(m_current, ch);
}
else
{
const wxString newPrefix(m_findPrefix + ch);
item = PrefixFindItem(m_current, newPrefix);
if ( item != (size_t)-1 )
m_findPrefix = newPrefix;
}
// also start the timer to reset the current prefix if the user
// doesn't press any more alnum keys soon -- we wouldn't want
// to use this prefix for a new item search
if ( !m_findTimer )
{
m_findTimer = new wxListFindTimer( this );
}
// Notice that we should start the timer even if we didn't find
// anything to make sure we reset the search state later.
m_findTimer->Start(wxListFindTimer::DELAY, wxTIMER_ONE_SHOT);
// restart timer even when there's no match so bell get's reset
if ( item != (size_t)-1 )
{
// Select the found item and go to it.
HighlightAll(false);
SetItemState(item,
wxLIST_STATE_FOCUSED | wxLIST_STATE_SELECTED,
wxLIST_STATE_FOCUSED | wxLIST_STATE_SELECTED);
// Reset the bell flag if it had been temporarily disabled
// before.
if ( m_findBell )
m_findBell = 1;
}
else // No such item
{
// Signal it with a bell if enabled.
if ( m_findBell == 1 )
{
::wxBell();
// Disable it for the next unsuccessful match, we only
// beep once, this is usually enough and continuing to
// do it would be annoying.
m_findBell = -1;
}
}
}
else
{
event.Skip();
}
}
}
@ -4321,6 +4418,61 @@ void wxListMainWindow::GetVisibleLinesRange(size_t *from, size_t *to)
*to = m_lineTo;
}
size_t
wxListMainWindow::PrefixFindItem(size_t idParent,
const wxString& prefixOrig) const
{
// if no items then just return
if ( idParent == (size_t)-1 )
return idParent;
// match is case insensitive as this is more convenient to the user: having
// to press Shift-letter to go to the item starting with a capital letter
// would be too bothersome
wxString prefix = prefixOrig.Lower();
// determine the starting point: we shouldn't take the current item (this
// allows to switch between two items starting with the same letter just by
// pressing it) but we shouldn't jump to the next one if the user is
// continuing to type as otherwise he might easily skip the item he wanted
size_t itemid = idParent;
if ( prefix.length() == 1 )
{
itemid += 1;
}
// look for the item starting with the given prefix after it
while ( ( itemid < (size_t)GetItemCount() ) &&
!GetLine(itemid)->GetText(0).Lower().StartsWith(prefix) )
{
itemid += 1;
}
// if we haven't found anything...
if ( !( itemid < (size_t)GetItemCount() ) )
{
// ... wrap to the beginning
itemid = 0;
// and try all the items (stop when we get to the one we started from)
while ( ( itemid < (size_t)GetItemCount() ) && itemid != idParent &&
!GetLine(itemid)->GetText(0).Lower().StartsWith(prefix) )
{
itemid += 1;
}
// If we haven't found the item, id will be (size_t)-1, as per
// documentation
if ( !( itemid < (size_t)GetItemCount() ) ||
( ( itemid == idParent ) &&
!GetLine(itemid)->GetText(0).Lower().StartsWith(prefix) ) )
{
itemid = (size_t)-1;
}
}
return itemid;
}
// -------------------------------------------------------------------------------------
// wxGenericListCtrl
// -------------------------------------------------------------------------------------
@ -5280,6 +5432,11 @@ void wxGenericListCtrl::RefreshItems(long itemFrom, long itemTo)
m_mainWin->RefreshLines(itemFrom, itemTo);
}
void wxGenericListCtrl::EnableBellOnNoMatch( bool on )
{
m_mainWin->EnableBellOnNoMatch(on);
}
// Generic wxListCtrl is more or less a container for two other
// windows which drawings are done upon. These are namely
// 'm_headerWin' and 'm_mainWin'.