X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/22930be86cddbf792227bb471852c64b40f483f0..07aaf32633ecf18ec3edfbb41793a112914792d0:/src/generic/listctrl.cpp diff --git a/src/generic/listctrl.cpp b/src/generic/listctrl.cpp index 834eac9ac3..b311e031ef 100644 --- a/src/generic/listctrl.cpp +++ b/src/generic/listctrl.cpp @@ -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 = @@ -1582,10 +1593,9 @@ wxListMainWindow::wxListMainWindow() wxListMainWindow::wxListMainWindow( wxWindow *parent, wxWindowID id, const wxPoint& pos, - const wxSize& size, - long style, - const wxString &name ) - : wxWindow( parent, id, pos, size, style, name ) + const wxSize& size ) + : wxWindow( parent, id, pos, size, + wxWANTS_CHARS | wxBORDER_NONE ) { Init(); @@ -1626,6 +1636,7 @@ wxListMainWindow::~wxListMainWindow() delete m_highlightBrush; delete m_highlightUnfocusedBrush; delete m_renameTimer; + delete m_findTimer; } void wxListMainWindow::SetReportView(bool inReportView) @@ -2208,7 +2219,7 @@ wxTextCtrl *wxListMainWindow::EditLabel(long item, wxClassInfo* textControlClass wxCHECK_MSG( (item >= 0) && ((size_t)item < GetItemCount()), NULL, wxT("wrong index in wxGenericListCtrl::EditLabel()") ); - wxASSERT_MSG( textControlClass->IsKindOf(CLASSINFO(wxTextCtrl)), + wxASSERT_MSG( textControlClass->IsKindOf(wxCLASSINFO(wxTextCtrl)), wxT("EditLabel() needs a text control") ); size_t itemEdit = (size_t)item; @@ -2227,19 +2238,10 @@ wxTextCtrl *wxListMainWindow::EditLabel(long item, wxClassInfo* textControlClass 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(); @@ -2289,6 +2291,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__ @@ -2301,10 +2315,16 @@ void wxListMainWindow::OnMouse( wxMouseEvent &event ) #endif // __WXMAC__ if ( event.LeftDown() ) - SetFocus(); + { + // Ensure we skip the event to let the system set focus to this window. + event.Skip(); + } - event.SetEventObject( GetParent() ); - if ( GetParent()->GetEventHandler()->ProcessEvent( event) ) + // Pretend that the event happened in wxListCtrl itself. + wxMouseEvent me(event); + me.SetEventObject( GetParent() ); + me.SetId(GetParent()->GetId()); + if ( GetParent()->GetEventHandler()->ProcessEvent( me )) return; if (event.GetEventType() == wxEVT_MOUSEWHEEL) @@ -2465,9 +2485,10 @@ void wxListMainWindow::OnMouse( wxMouseEvent &event ) m_renameTimer->Start(dclick > 0 ? dclick : 250, true); } } + + m_lastOnSame = false; } - m_lastOnSame = false; m_lineSelectSingleOnUp = (size_t)-1; } else @@ -2559,8 +2580,16 @@ void wxListMainWindow::OnMouse( wxMouseEvent &event ) 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(); } } @@ -2706,6 +2735,7 @@ void wxListMainWindow::OnKeyDown( wxKeyEvent &event ) // propagate the key event upwards wxKeyEvent ke(event); ke.SetEventObject( parent ); + ke.SetId(GetParent()->GetId()); if (parent->GetEventHandler()->ProcessEvent( ke )) return; @@ -2729,6 +2759,8 @@ void wxListMainWindow::OnKeyUp( wxKeyEvent &event ) // propagate the key event upwards wxKeyEvent ke(event); + ke.SetEventObject( parent ); + ke.SetId(GetParent()->GetId()); if (parent->GetEventHandler()->ProcessEvent( ke )) return; @@ -2758,6 +2790,7 @@ void wxListMainWindow::OnChar( wxKeyEvent &event ) // propagate the char event upwards wxKeyEvent ke(event); ke.SetEventObject( parent ); + ke.SetId(GetParent()->GetId()); if (parent->GetEventHandler()->ProcessEvent( ke )) return; @@ -2783,7 +2816,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 ) @@ -2881,7 +2915,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(); + } } } @@ -4126,8 +4232,10 @@ void wxListMainWindow::InsertItem( wxListItem &item ) RefreshLines(id, GetItemCount() - 1); } -void wxListMainWindow::InsertColumn( long col, const wxListItem &item ) +long wxListMainWindow::InsertColumn( long col, const wxListItem &item ) { + long idx = -1; + m_dirty = true; if ( InReportView() ) { @@ -4144,9 +4252,11 @@ void wxListMainWindow::InsertColumn( long col, const wxListItem &item ) node = m_columns.Item( col ); m_columns.Insert( node, column ); m_aColWidths.Insert( colWidthInfo, col ); + idx = col; } else { + idx = m_aColWidths.GetCount(); m_columns.Append( column ); m_aColWidths.Add( colWidthInfo ); } @@ -4168,6 +4278,7 @@ void wxListMainWindow::InsertColumn( long col, const wxListItem &item ) // invalidate it as it has to be recalculated m_headerWidth = 0; } + return idx; } int wxListMainWindow::GetItemWidthWithImage(wxListItem * item) @@ -4298,6 +4409,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 // ------------------------------------------------------------------------------------- @@ -4389,12 +4555,7 @@ bool wxGenericListCtrl::Create(wxWindow *parent, validator, name ) ) return false; -#ifdef __WXGTK__ - style &= ~wxBORDER_MASK; - style |= wxBORDER_THEME; -#endif - - m_mainWin = new wxListMainWindow( this, wxID_ANY, wxPoint(0, 0), size, style ); + m_mainWin = new wxListMainWindow(this, wxID_ANY, wxPoint(0, 0), size); SetTargetWindow( m_mainWin ); @@ -4451,13 +4612,14 @@ void wxGenericListCtrl::OnScroll(wxScrollWinEvent& event) // 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 ) @@ -4953,14 +5115,14 @@ long wxGenericListCtrl::DoInsertColumn( long col, const wxListItem &item ) { wxCHECK_MSG( InReportView(), -1, wxT("can't add column in non report mode") ); - m_mainWin->InsertColumn( col, item ); + long idx = m_mainWin->InsertColumn( col, item ); // NOTE: if wxLC_NO_HEADER was given, then we are in report view mode but // still have m_headerWin==NULL if (m_headerWin) m_headerWin->Refresh(); - return 0; + return idx; } bool wxGenericListCtrl::ScrollList( int dx, int dy ) @@ -5149,8 +5311,8 @@ void wxGenericListCtrl::DoScreenToClient( int *x, int *y ) const wxSize wxGenericListCtrl::DoGetBestClientSize() const { - // Something is better than nothing even if this is completely arbitrary. - wxSize sizeBest(100, 80); + // The base class version can compute the best size in report view only. + wxSize sizeBest = wxListCtrlBase::DoGetBestClientSize(); if ( !InReportView() ) { @@ -5235,16 +5397,6 @@ int wxGenericListCtrl::OnGetItemColumnImage(long item, long column) const 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") ); @@ -5262,6 +5414,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'.