m_owner->OnRenameTimer();
}
+//-----------------------------------------------------------------------------
+// wxListFindTimer (internal)
+//-----------------------------------------------------------------------------
+
+void wxListFindTimer::Notify()
+{
+ m_owner->OnFindTimer();
+}
+
//-----------------------------------------------------------------------------
// wxListTextCtrlWrapper (internal)
//-----------------------------------------------------------------------------
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 =
delete m_highlightBrush;
delete m_highlightUnfocusedBrush;
delete m_renameTimer;
+ delete m_findTimer;
}
void wxListMainWindow::SetReportView(bool inReportView)
return NULL;
}
- // We have to call this here because the label in question might just have
- // been added and no screen update taken place.
if ( m_dirty )
{
- // TODO: use wxTheApp->SafeYieldFor(NULL, wxEVT_CATEGORY_UI) instead
- // so that no pending events may change the item count (see below)
- // IMPORTANT: needs to be tested!
- wxSafeYield();
-
- // Pending events dispatched by wxSafeYield might have changed the item
- // count
- if ( (size_t)item >= GetItemCount() )
- return NULL;
+ // Ensure the display is updated before we start editing.
+ Update();
}
wxTextCtrl * const text = (wxTextCtrl *)textControlClass->CreateObject();
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__
#endif // __WXMAC__
if ( event.LeftDown() )
- SetFocus();
+ {
+ // Ensure we skip the event to let the system set focus to this window.
+ event.Skip();
+ }
// Pretend that the event happened in wxListCtrl itself.
wxMouseEvent me(event);
m_renameTimer->Start(dclick > 0 ? dclick : 250, true);
}
}
+
+ m_lastOnSame = false;
}
- m_lastOnSame = false;
m_lineSelectSingleOnUp = (size_t)-1;
}
else
if (m_current != oldCurrent)
RefreshLine( oldCurrent );
- // forceClick is only set if the previous click was on another item
- m_lastOnSame = !forceClick && (m_current == oldCurrent) && oldWasSelected;
+ // Set the flag telling us whether the next click on this item should
+ // start editing its label. This should happen if we clicked on the
+ // current item and it was already selected, i.e. if this click was not
+ // done to select it.
+ //
+ // It should not happen if this was a double click (forceClick is true)
+ // nor if we hadn't had the focus before as then this click was used to
+ // give focus to the control.
+ m_lastOnSame = (m_current == oldCurrent) && oldWasSelected &&
+ !forceClick && HasFocus();
}
}
event.m_keyCode = WXK_RIGHT;
}
- switch ( event.GetKeyCode() )
+ int keyCode = event.GetKeyCode();
+ switch ( keyCode )
{
case WXK_UP:
if ( m_current > 0 )
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();
+ }
}
}
if ( InReportView() )
{
+ // Account for the header height if it's present.
+ if ( HasHeader() )
+ y -= GetListCtrl()->m_headerWin->GetSize().y + 1;
+
size_t current = y / GetLineHeight();
if ( current < count )
{
*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
// -------------------------------------------------------------------------------------
// the window the next time
m_mainWin->ResetVisibleLinesRange();
- HandleOnScroll( event );
-
if ( event.GetOrientation() == wxHORIZONTAL && HasHeader() )
{
m_headerWin->Refresh();
m_headerWin->Update();
}
+
+ // Let the window be scrolled as usual by the default handler.
+ event.Skip();
}
void wxGenericListCtrl::SetSingleStyle( long style, bool add )
return -1;
}
-wxListItemAttr *
-wxGenericListCtrl::OnGetItemAttr(long WXUNUSED_UNLESS_DEBUG(item)) const
-{
- wxASSERT_MSG( item >= 0 && item < GetItemCount(),
- wxT("invalid item index in OnGetItemAttr()") );
-
- // no attributes by default
- return NULL;
-}
-
void wxGenericListCtrl::SetItemCount(long count)
{
wxASSERT_MSG( IsVirtual(), wxT("this is for virtual controls only") );
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'.