X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/e55e1a5fda0be9114123671b0e595936238b43b4..683df83d51c0f080e2e5210dc8115be0cb30d1ad:/src/generic/listctrl.cpp diff --git a/src/generic/listctrl.cpp b/src/generic/listctrl.cpp index a5aee6314b..6575083534 100644 --- a/src/generic/listctrl.cpp +++ b/src/generic/listctrl.cpp @@ -952,15 +952,17 @@ wxListHeaderWindow::wxListHeaderWindow() m_resizeCursor = NULL; } -wxListHeaderWindow::wxListHeaderWindow( wxWindow *win, - wxWindowID id, - wxListMainWindow *owner, - const wxPoint& pos, - const wxSize& size, - long style, - const wxString &name ) - : wxWindow( win, id, pos, size, style, name ) -{ +bool wxListHeaderWindow::Create( wxWindow *win, + wxWindowID id, + wxListMainWindow *owner, + const wxPoint& pos, + const wxSize& size, + long style, + const wxString &name ) +{ + if ( !wxWindow::Create(win, id, pos, size, style, name) ) + return false; + Init(); m_owner = owner; @@ -978,6 +980,8 @@ wxListHeaderWindow::wxListHeaderWindow( wxWindow *win, if (!m_hasFont) SetOwnFont( wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT )); #endif + + return true; } wxListHeaderWindow::~wxListHeaderWindow() @@ -1214,7 +1218,7 @@ void wxListHeaderWindow::OnMouse( wxMouseEvent &event ) if (m_isDragging) { - SendListEvent(wxEVT_COMMAND_LIST_COL_DRAGGING, event.GetPosition()); + SendListEvent(wxEVT_LIST_COL_DRAGGING, event.GetPosition()); // we don't draw the line beyond our window, but we allow dragging it // there @@ -1233,7 +1237,7 @@ void wxListHeaderWindow::OnMouse( wxMouseEvent &event ) m_isDragging = false; m_dirty = true; m_owner->SetColumnWidth( m_column, m_currentX - m_minX ); - SendListEvent(wxEVT_COMMAND_LIST_COL_END_DRAG, event.GetPosition()); + SendListEvent(wxEVT_LIST_COL_END_DRAG, event.GetPosition()); } else { @@ -1286,7 +1290,7 @@ void wxListHeaderWindow::OnMouse( wxMouseEvent &event ) { if (hit_border && event.LeftDown()) { - if ( SendListEvent(wxEVT_COMMAND_LIST_COL_BEGIN_DRAG, + if ( SendListEvent(wxEVT_LIST_COL_BEGIN_DRAG, event.GetPosition()) ) { m_isDragging = true; @@ -1315,8 +1319,8 @@ void wxListHeaderWindow::OnMouse( wxMouseEvent &event ) } SendListEvent( event.LeftDown() - ? wxEVT_COMMAND_LIST_COL_CLICK - : wxEVT_COMMAND_LIST_COL_RIGHT_CLICK, + ? wxEVT_LIST_COL_CLICK + : wxEVT_LIST_COL_RIGHT_CLICK, event.GetPosition()); } } @@ -1371,6 +1375,15 @@ void wxListRenameTimer::Notify() m_owner->OnRenameTimer(); } +//----------------------------------------------------------------------------- +// wxListFindTimer (internal) +//----------------------------------------------------------------------------- + +void wxListFindTimer::Notify() +{ + m_owner->OnFindTimer(); +} + //----------------------------------------------------------------------------- // wxListTextCtrlWrapper (internal) //----------------------------------------------------------------------------- @@ -1563,6 +1576,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 +1640,7 @@ wxListMainWindow::~wxListMainWindow() delete m_highlightBrush; delete m_highlightUnfocusedBrush; delete m_renameTimer; + delete m_findTimer; } void wxListMainWindow::SetReportView(bool inReportView) @@ -1876,8 +1892,8 @@ bool wxListMainWindow::HighlightLine( size_t line, bool highlight ) if ( changed ) { - SendNotify( line, highlight ? wxEVT_COMMAND_LIST_ITEM_SELECTED - : wxEVT_COMMAND_LIST_ITEM_DESELECTED ); + SendNotify( line, highlight ? wxEVT_LIST_ITEM_SELECTED + : wxEVT_LIST_ITEM_DESELECTED ); } return changed; @@ -2031,7 +2047,7 @@ void wxListMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) ) // tell the caller cache to cache the data if ( IsVirtual() ) { - wxListEvent evCache(wxEVT_COMMAND_LIST_CACHE_HINT, + wxListEvent evCache(wxEVT_LIST_CACHE_HINT, GetParent()->GetId()); evCache.SetEventObject( GetParent() ); evCache.m_oldItemIndex = visibleFrom; @@ -2183,7 +2199,7 @@ void wxListMainWindow::SendNotify( size_t line, { GetLine(line)->GetItem( 0, le.m_item ); } - //else: this happens for wxEVT_COMMAND_LIST_ITEM_FOCUSED event + //else: this happens for wxEVT_LIST_ITEM_FOCUSED event } //else: there may be no more such item @@ -2199,7 +2215,7 @@ void wxListMainWindow::ChangeCurrent(size_t current) if ( m_renameTimer->IsRunning() ) m_renameTimer->Stop(); - SendNotify(current, wxEVT_COMMAND_LIST_ITEM_FOCUSED); + SendNotify(current, wxEVT_LIST_ITEM_FOCUSED); } wxTextCtrl *wxListMainWindow::EditLabel(long item, wxClassInfo* textControlClass) @@ -2212,7 +2228,7 @@ wxTextCtrl *wxListMainWindow::EditLabel(long item, wxClassInfo* textControlClass size_t itemEdit = (size_t)item; - wxListEvent le( wxEVT_COMMAND_LIST_BEGIN_LABEL_EDIT, GetParent()->GetId() ); + wxListEvent le( wxEVT_LIST_BEGIN_LABEL_EDIT, GetParent()->GetId() ); le.SetEventObject( GetParent() ); le.m_item.m_itemId = le.m_itemIndex = item; @@ -2226,19 +2242,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(); @@ -2255,7 +2262,7 @@ void wxListMainWindow::OnRenameTimer() bool wxListMainWindow::OnRenameAccept(size_t itemEdit, const wxString& value) { - wxListEvent le( wxEVT_COMMAND_LIST_END_LABEL_EDIT, GetParent()->GetId() ); + wxListEvent le( wxEVT_LIST_END_LABEL_EDIT, GetParent()->GetId() ); le.SetEventObject( GetParent() ); le.m_item.m_itemId = le.m_itemIndex = itemEdit; @@ -2273,7 +2280,7 @@ bool wxListMainWindow::OnRenameAccept(size_t itemEdit, const wxString& value) void wxListMainWindow::OnRenameCancelled(size_t itemEdit) { // let owner know that the edit was cancelled - wxListEvent le( wxEVT_COMMAND_LIST_END_LABEL_EDIT, GetParent()->GetId() ); + wxListEvent le( wxEVT_LIST_END_LABEL_EDIT, GetParent()->GetId() ); le.SetEditCanceled(true); @@ -2288,6 +2295,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__ @@ -2323,7 +2342,7 @@ void wxListMainWindow::OnMouse( wxMouseEvent &event ) { if (event.RightDown()) { - SendNotify( (size_t)-1, wxEVT_COMMAND_LIST_ITEM_RIGHT_CLICK, event.GetPosition() ); + SendNotify( (size_t)-1, wxEVT_LIST_ITEM_RIGHT_CLICK, event.GetPosition() ); wxContextMenuEvent evtCtx(wxEVT_CONTEXT_MENU, GetParent()->GetId(), @@ -2379,7 +2398,7 @@ void wxListMainWindow::OnMouse( wxMouseEvent &event ) m_dragCount = 0; // The only mouse event that can be generated without any valid item is - // wxEVT_COMMAND_LIST_ITEM_RIGHT_CLICK as it can be useful to have a global + // wxEVT_LIST_ITEM_RIGHT_CLICK as it can be useful to have a global // popup menu for the list control itself which should be shown even when // the user clicks outside of any item. if ( !hitResult ) @@ -2387,7 +2406,7 @@ void wxListMainWindow::OnMouse( wxMouseEvent &event ) // outside of any item if (event.RightDown()) { - SendNotify( (size_t) -1, wxEVT_COMMAND_LIST_ITEM_RIGHT_CLICK, event.GetPosition() ); + SendNotify( (size_t) -1, wxEVT_LIST_ITEM_RIGHT_CLICK, event.GetPosition() ); wxContextMenuEvent evtCtx( wxEVT_CONTEXT_MENU, @@ -2418,8 +2437,8 @@ void wxListMainWindow::OnMouse( wxMouseEvent &event ) if (m_dragCount != 3) return; - int command = event.RightIsDown() ? wxEVT_COMMAND_LIST_BEGIN_RDRAG - : wxEVT_COMMAND_LIST_BEGIN_DRAG; + int command = event.RightIsDown() ? wxEVT_LIST_BEGIN_RDRAG + : wxEVT_LIST_BEGIN_DRAG; SendNotify( m_lineLastClicked, command, m_dragStart ); @@ -2436,7 +2455,7 @@ void wxListMainWindow::OnMouse( wxMouseEvent &event ) if ( current == m_lineLastClicked ) { - SendNotify( current, wxEVT_COMMAND_LIST_ITEM_ACTIVATED ); + SendNotify( current, wxEVT_LIST_ITEM_ACTIVATED ); return; } @@ -2470,9 +2489,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 @@ -2497,14 +2517,14 @@ void wxListMainWindow::OnMouse( wxMouseEvent &event ) ReverseHighlight(m_current); } - SendNotify( current, wxEVT_COMMAND_LIST_ITEM_RIGHT_CLICK, event.GetPosition() ); + SendNotify( current, wxEVT_LIST_ITEM_RIGHT_CLICK, event.GetPosition() ); // Allow generation of context menu event event.Skip(); } else if (event.MiddleDown()) { - SendNotify( current, wxEVT_COMMAND_LIST_ITEM_MIDDLE_CLICK ); + SendNotify( current, wxEVT_LIST_ITEM_MIDDLE_CLICK ); } else if ( event.LeftDown() || forceClick ) { @@ -2564,8 +2584,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(); } } @@ -2716,7 +2744,7 @@ void wxListMainWindow::OnKeyDown( wxKeyEvent &event ) return; // send a list event - wxListEvent le( wxEVT_COMMAND_LIST_KEY_DOWN, parent->GetId() ); + wxListEvent le( wxEVT_LIST_KEY_DOWN, parent->GetId() ); le.m_item.m_itemId = le.m_itemIndex = m_current; if (HasCurrent()) @@ -2792,7 +2820,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 ) @@ -2875,7 +2904,7 @@ void wxListMainWindow::OnChar( wxKeyEvent &event ) } else // normal space press { - SendNotify( m_current, wxEVT_COMMAND_LIST_ITEM_ACTIVATED ); + SendNotify( m_current, wxEVT_LIST_ITEM_ACTIVATED ); } } else // multiple selection @@ -2886,11 +2915,83 @@ void wxListMainWindow::OnChar( wxKeyEvent &event ) case WXK_RETURN: case WXK_EXECUTE: - SendNotify( m_current, wxEVT_COMMAND_LIST_ITEM_ACTIVATED ); + SendNotify( m_current, wxEVT_LIST_ITEM_ACTIVATED ); 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(); + } } } @@ -3841,7 +3942,7 @@ void wxListMainWindow::DeleteItem( long lindex ) ResetVisibleLinesRange(); } - SendNotify( index, wxEVT_COMMAND_LIST_DELETE_ITEM, wxDefaultPosition ); + SendNotify( index, wxEVT_LIST_DELETE_ITEM, wxDefaultPosition ); if ( IsVirtual() ) { @@ -3919,7 +4020,7 @@ void wxListMainWindow::DoDeleteAllItems() // for all of them: this is compatible with wxMSW and documented in // DeleteAllItems() description - wxListEvent event( wxEVT_COMMAND_LIST_DELETE_ALL_ITEMS, GetParent()->GetId() ); + wxListEvent event( wxEVT_LIST_DELETE_ALL_ITEMS, GetParent()->GetId() ); event.SetEventObject( GetParent() ); GetParent()->GetEventHandler()->ProcessEvent( event ); @@ -4130,7 +4231,7 @@ void wxListMainWindow::InsertItem( wxListItem &item ) if ( HasCurrent() && m_current >= id ) m_current++; - SendNotify(id, wxEVT_COMMAND_LIST_INSERT_ITEM); + SendNotify(id, wxEVT_LIST_INSERT_ITEM); RefreshLines(id, GetItemCount() - 1); } @@ -4312,6 +4413,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 // ------------------------------------------------------------------------------------- @@ -4357,7 +4513,13 @@ void wxGenericListCtrl::CreateOrDestroyHeaderWindowAsNeeded() if (needs_header) { - m_headerWin = new wxListHeaderWindow + // Notice that we must initialize m_headerWin first, and create the + // real window only later, so that the test in the beginning of the + // function blocks repeated creation of the header as it could happen + // before via wxNavigationEnabled::AddChild() -> ToggleWindowStyle() -> + // SetWindowStyleFlag(). + m_headerWin = new wxListHeaderWindow(); + m_headerWin->Create ( this, wxID_ANY, m_mainWin, wxPoint(0,0), @@ -4368,7 +4530,7 @@ void wxGenericListCtrl::CreateOrDestroyHeaderWindowAsNeeded() ), wxTAB_TRAVERSAL ); - + #if defined( __WXMAC__ ) static wxFont font( wxOSX_SYSTEM_FONT_SMALL ); m_headerWin->SetFont( font ); @@ -4460,13 +4622,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 ) @@ -5136,26 +5299,6 @@ bool wxGenericListCtrl::DoPopupMenu( wxMenu *menu, int x, int y ) #endif } -void wxGenericListCtrl::DoClientToScreen( int *x, int *y ) const -{ - // It's not clear whether this can be called before m_mainWin is created - // but it seems better to be on the safe side and check. - if ( m_mainWin ) - m_mainWin->DoClientToScreen(x, y); - else - wxListCtrlBase::DoClientToScreen(x, y); -} - -void wxGenericListCtrl::DoScreenToClient( int *x, int *y ) const -{ - // At least in wxGTK/Univ build this method can be called before m_mainWin - // is created so avoid crashes in this case. - if ( m_mainWin ) - m_mainWin->DoScreenToClient(x, y); - else - wxListCtrlBase::DoScreenToClient(x, y); -} - wxSize wxGenericListCtrl::DoGetBestClientSize() const { // The base class version can compute the best size in report view only. @@ -5244,16 +5387,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") ); @@ -5271,6 +5404,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'.