void CSelection::GetCaretRect( CEditView *pView, int nBuffCol, int nRow, RECT &rcCaret ) { ASSERT( pView ); RECT rcView; pView->GetRect( &rcView ); CBuffer *pBuffer = m_pCtrl->GetBuffer(); int nLastLine = pBuffer->GetLineCount() - 1; int nViewCol = nBuffCol; nRow = min( nLastLine, nRow ); if ( nRow < 0 ) { nRow = 0; } else { nViewCol = pBuffer->ConvertBufferColToViewCol( nRow, nBuffCol ); } rcCaret.left = rcView.left + pView->GetLeftMargin( TRUE, TRUE ) + ( ( nViewCol - pView->GetLeftIndex() ) * pView->GetCharWidth() ); rcCaret.right = rcCaret.left + m_cxCaretIns; rcCaret.top = rcView.top + ( ( nRow - pView->GetTopIndex() ) * pView->GetLineHeight() ); rcCaret.bottom = rcCaret.top + m_cyCaret; }
void CSelection::SetExtendedSelection( int nStartCol, int nStartRow, int nEndCol, int nEndRow, BOOL bEnsureVisible, BOOL bAllowDamage ) { ASSERT( nStartRow >= 0 ); int nOldStartRow = m_nStartRow; int nOldEndRow = m_nEndRow; CBuffer *pBuffer = m_pCtrl->GetBuffer(); int nMaxLine = pBuffer->GetLineCount() - 1; nMaxLine = max( 0, nMaxLine ); m_nStartRow = min( nMaxLine, nStartRow ); m_nEndRow = min( nMaxLine, nEndRow ); m_nStartCol = nStartCol; m_nEndCol = nEndCol; // keep selection over text, if requested to if ( BoundSelection() ) { BOOL bKeepEmpty = ( nStartCol == nEndCol && nStartRow == nEndRow ); EnforceSelBounds(); if ( bKeepEmpty ) { m_nStartCol = m_nEndCol; } } m_nEndViewCol = pBuffer->ConvertBufferColToViewCol( m_nEndRow, m_nEndCol ); m_nStartViewCol = pBuffer->ConvertBufferColToViewCol( m_nStartRow, m_nStartCol ); m_nEndViewColPreferred = m_nEndViewCol; // damage old selection and new selection to clear it if ( bAllowDamage ) { int nStartDamage = min( nOldStartRow, nOldEndRow ); nStartDamage = min( nStartDamage, nStartRow ); nStartDamage = min( nStartDamage, nEndRow ); int nEndDamage = max( nOldStartRow, nOldEndRow ); nEndDamage = max( nEndDamage, nStartRow ); nEndDamage = max( nEndDamage, nEndRow ); m_pView->DamageView( nStartDamage, nEndDamage ); } if ( bEnsureVisible ) { EnsureVisible( TRUE ); } if ( ::IsWindow( m_hWnd ) && GetFocus() == m_hWnd ) { UpdateCaretPosition(); } }
void CSelection::ExtendTo( int nCol, int nRow ) { CBuffer *pBuffer = m_pCtrl->GetBuffer(); int nOldRow = m_nEndRow; int nTemp = pBuffer->GetLineCount() - 1; nTemp = min( nRow, nTemp ); m_nEndRow = max( 0, nTemp ); int nMinRow = min( nOldRow, m_nEndRow ); int nMaxRow = max( nOldRow, m_nEndRow ); int nOldViewCol = m_nEndViewCol; m_nEndCol = max( 0, nCol ); // keep selection over text is requested to if ( BoundSelection() ) { EnforceSelBounds(); } m_nStartViewCol = pBuffer->ConvertBufferColToViewCol( m_nStartRow, m_nStartCol ); m_nEndViewCol = pBuffer->ConvertBufferColToViewCol( m_nEndRow, m_nEndCol ); if ( m_bColumnSel && ( m_nEndViewCol != nOldViewCol ) ) { // column sel width changed nMinRow = min( nMinRow, m_nStartRow ); nMaxRow = max( nMaxRow, m_nStartRow ); } // if user changed lines, notify the control so it can normalize the text case in the // line that was just left. if ( nOldRow != nRow ) { m_pCtrl->OnChangeLineSelection(); } m_pView->DamageView( nMinRow, nMaxRow ); UpdateCaretPosition(); }
BOOL CSelection::EnforceSelBounds() { CBuffer *pBuffer = m_pCtrl->GetBuffer(); BOOL bFixup = FALSE; if ( m_nEndRow < pBuffer->GetLineCount() ) { int nLastChar = pBuffer->GetLineLength( m_nEndRow ); if ( m_nEndCol > nLastChar ) { m_nEndCol = nLastChar; bFixup = TRUE; } } else { m_nEndCol = 0; bFixup = TRUE; } return bFixup; }
BOOL CSelection::EnforceSelBounds() { ASSERT( BoundSelection() ); // don't call this unless it's neccessary CBuffer *pBuffer = m_pCtrl->GetBuffer(); BOOL bFixup = FALSE; if ( m_nEndRow < pBuffer->GetLineCount() ) { int nLastChar = pBuffer->GetLineLength( m_nEndRow ); if ( m_nEndCol > nLastChar ) { m_nEndCol = nLastChar; bFixup = TRUE; } } else { m_nEndCol = 0; bFixup = TRUE; } return bFixup; }
void CSelection::Extend( Direction eDirection, Amount eAmount, BOOL bScrollIfNeccessary, BOOL bDamage, BOOL bAllowPastEndOfLine ) { CBuffer *pBuffer = m_pCtrl->GetBuffer(); int nLineCount = pBuffer->GetLineCount(); BOOL bEnforceSelBounds = BoundSelection(); int nSaveEndRow = m_nEndRow; int nSaveEndCol = m_nEndCol; BOOL bUsePreferredCol = FALSE; if ( nLineCount ) { int nOldEndRow = m_nEndRow; int nOldStartRow = m_nStartRow; LPCTSTR pszEndLineStart = pBuffer->GetLineText( m_nEndRow ); int nEndLineLen = pBuffer->GetLineLength( m_nEndRow ); BOOL bStartRowChanged = FALSE; switch ( eDirection ) { case eUp: { switch ( eAmount ) { case eChar: { m_nEndRow--; bUsePreferredCol = TRUE; break; } case ePage: { m_nEndRow -= ( m_pView->GetBottomIndex( FALSE ) - m_pView->GetTopIndex() ); break; } case eSmartAll: case eAll: { m_nEndRow = 0; break; } } break; } case eDown: { switch ( eAmount ) { case eChar: { m_nEndRow++; bUsePreferredCol = TRUE; break; } case ePage: { int nTemp = m_nEndRow + ( m_pView->GetBottomIndex( FALSE ) - m_pView->GetTopIndex() ); m_nEndRow = min( nLineCount, nTemp ); break; } case eAll: case eSmartAll: { m_nEndRow = nLineCount - 1; break; } } break; } case eLeft: { switch ( eAmount ) { case eChar: { if ( m_nEndCol == 0 || m_nEndCol > nEndLineLen ) m_nEndCol--; else m_nEndCol -= _tclen_prev( pszEndLineStart, pszEndLineStart + m_nEndCol ); if ( m_nEndCol < 0 ) { if ( bAllowPastEndOfLine && m_nEndRow > 0 ) { m_nEndRow--; m_nEndCol = pBuffer->GetLineLength( m_nEndRow ); bEnforceSelBounds = FALSE; // already enforced by previous statement! } else { m_nEndCol = 0; } } break; } case ePage: { m_nEndCol -= ( m_pView->GetRightIndex( FALSE ) - m_pView->GetLeftIndex() ); break; } case eAll: { m_nEndCol = 0; break; } case eSmartAll: { LPCTSTR pszLine = pBuffer->GetLineText( m_nEndRow ); int nFirstNonSpace = 0; while ( *pszLine && ( *pszLine == _T(' ') || *pszLine == _T('\t') ) ) { nFirstNonSpace++; pszLine = _tcsinc( pszLine ); } // jump between absolute left and 'textual' left m_nEndCol = ( m_nEndCol == nFirstNonSpace ? 0 : nFirstNonSpace ); break; } case eWord: { pBuffer->AdvanceToWordStart( m_nEndRow, m_nEndCol, FALSE, TRUE ); break; } case eWordEnd: { pBuffer->AdvanceToWordEnd( m_nEndRow, m_nEndCol, FALSE, TRUE ); break; } case eSentence: { pBuffer->AdvanceToSentenceStart( m_nEndRow, m_nEndCol, FALSE ); break; } } break; } case eRight: { switch ( eAmount ) { case eChar: { if ( m_nEndCol >= nEndLineLen ) m_nEndCol++; else m_nEndCol += _tclen( pszEndLineStart + m_nEndCol ); break; } case ePage: { m_nEndCol += ( m_pView->GetRightIndex( FALSE ) - m_pView->GetLeftIndex() ); break; } case eAll: { m_nEndCol = pBuffer->GetLineLength( m_nEndRow ); break; } case eSmartAll: { LPCTSTR pszStart = pBuffer->GetLineText( m_nEndRow ); int nLastChar = pBuffer->GetLineLength( m_nEndRow ); int nFirstNonSpace = nLastChar; LPCTSTR pszEnd = pszStart + nFirstNonSpace - 1; while ( ( pszEnd >= pszStart ) && ( *pszEnd == _T(' ') || *pszEnd == _T('\t') ) ) { nFirstNonSpace--; pszEnd = _tcsdec( pszStart, pszEnd ); } // jump between absolute right and 'textual' right m_nEndCol = ( m_nEndCol <= nFirstNonSpace ? nLastChar : nFirstNonSpace ); break; } case eWord: { pBuffer->AdvanceToWordStart( m_nEndRow, m_nEndCol, TRUE, TRUE ); break; } case eWordEnd: { pBuffer->AdvanceToWordEnd( m_nEndRow, m_nEndCol, TRUE, TRUE ); break; } case eSentence: { pBuffer->AdvanceToSentenceStart( m_nEndRow, m_nEndCol, TRUE ); break; } } break; } case eOutward: { switch ( eAmount ) { case eWord: { m_nStartCol = m_nEndCol; int nLineLen = pBuffer->GetLineLength( m_nEndRow ); if ( m_nStartCol <= nLineLen ) { if ( m_nStartCol ) { pBuffer->AdvanceToWordStart( m_nEndRow, m_nStartCol, FALSE, FALSE ); } m_nEndCol = m_nStartCol; if ( m_nStartCol < nLineLen ) { pBuffer->AdvanceToWordEnd( m_nEndRow, m_nEndCol, TRUE, FALSE ); } } break; } case eSentence: { m_nStartRow = m_nEndRow; m_nStartCol = 0; pBuffer->AdvanceToSentenceStart( m_nStartRow, m_nStartCol, FALSE ); m_nEndCol = m_nStartCol; m_nEndRow = m_nStartRow; pBuffer->AdvanceToSentenceStart( m_nEndRow, m_nEndCol, TRUE ); bStartRowChanged = ( m_nStartRow != nOldStartRow ); break; } } break; } } int nTemp = nLineCount - 1; m_nEndRow = min( m_nEndRow, nTemp ); m_nEndRow = max( 0, m_nEndRow ); m_nEndCol = ( nLineCount == 0 ) ? 0 : max( 0, m_nEndCol ); BOOL bEndViewColUpToDate = FALSE; // keep cursor within the line's bounds if requested to if ( bEnforceSelBounds ) { // special case: if moving left one char and beyond the end of the line, // do the fixup now or else the one-char move will be nullified by // EnforceSelBounds() if ( nLineCount && eDirection == eLeft && eAmount == eChar ) { int nEndRowLen = pBuffer->GetLineLength( m_nEndRow ); if ( m_nEndCol >= nEndRowLen ) { m_nEndCol = nEndRowLen - 1; m_nEndCol = max( 0, m_nEndCol ); } } if ( bUsePreferredCol && nSaveEndRow != m_nEndRow ) { m_nEndCol = pBuffer->ConvertViewColToBufferCol( m_nEndRow, m_nEndViewColPreferred ); } BOOL bFixup = EnforceSelBounds(); // if we didn't have to fix-up the selection, remember this new col position // as the preferred position. if ( !bFixup ) { if ( bUsePreferredCol && nSaveEndRow != m_nEndRow ) { // moved vertically -- need to translate view col from one row to another int nBuffCol = pBuffer->ConvertViewColToBufferCol( m_nEndRow, m_nEndViewColPreferred ); m_nEndViewCol = pBuffer->ConvertBufferColToViewCol( m_nEndRow, nBuffCol ); m_nEndCol = pBuffer->ConvertViewColToBufferCol( m_nEndRow, m_nEndViewCol ); } else if ( nSaveEndCol != m_nEndCol ) { m_nEndViewCol = pBuffer->ConvertBufferColToViewCol( m_nEndRow, m_nEndCol ); m_nEndViewColPreferred = m_nEndViewCol; } bEndViewColUpToDate = TRUE; } } // since m_nEndCol may have changed, we need to recalc the view position and re-snap m_nEndCol to the current row if ( !bEndViewColUpToDate ) { m_nEndViewCol = pBuffer->ConvertBufferColToViewCol( m_nEndRow, m_nEndCol ); m_nEndCol = pBuffer->ConvertViewColToBufferCol( m_nEndRow, m_nEndViewCol ); } if ( eDirection == eOutward ) { m_nStartViewCol = pBuffer->ConvertBufferColToViewCol( m_nStartRow, m_nStartCol ); } if ( bDamage ) { int nDamageStart = min( nOldEndRow, m_nEndRow ); int nDamageEnd = max( nOldEndRow, m_nEndRow ); if ( m_bColumnSel ) { nDamageStart = min( nDamageStart, nOldStartRow ); nDamageStart = min( nDamageStart, m_nStartRow ); nDamageEnd = max( nDamageEnd, nOldStartRow ); nDamageEnd = max( nDamageEnd, m_nStartRow ); } if ( bStartRowChanged ) { nDamageStart = min( nDamageStart, nOldStartRow ); nDamageStart = min( nDamageStart, m_nStartRow ); nDamageEnd = max( nDamageEnd, nOldEndRow ); nDamageEnd = max( nDamageEnd, m_nEndRow ); } m_pView->DamageView( nDamageStart, nDamageEnd ); } // if user changed lines, notify the control so it can normalize the text case in the // line that was just left. if ( eDirection == eUp || eDirection == eDown ) { m_pCtrl->OnChangeLineSelection(); } } else { m_nEndCol = m_nEndRow = m_nEndViewCol = m_nStartViewCol = m_nStartCol = m_nStartRow = 0; } if ( bScrollIfNeccessary ) { EnsureVisible( TRUE ); } }
void CSelection::SetSelectionFromPoint( CEditView *pView, int x, int y, BOOL bEmpty, BOOL bAllowLineSel ) { if ( m_pView != pView ) { CEditView *pLastView = m_pView; m_pView = pView; if ( !IsEmpty() ) { // switching views -- erase the selection in the other view pLastView->DamageView( min( m_nEndRow, m_nStartRow ), max( m_nEndRow, m_nStartRow ) ); } } int nCol, nRow; RECT rcChar; m_pView->GetCharPosFromPoint( x, y, nCol, nRow, &rcChar ); RECT rcView; m_pView->GetViewRect( &rcView ); CBuffer *pBuffer = m_pCtrl->GetBuffer(); int nLineCount = pBuffer->GetLineCount(); if ( !bEmpty && bAllowLineSel && ( x > rcView.left && x < ( rcView.left + m_pView->GetLeftMargin( TRUE ) ) ) ) { // line selecting nCol = 0; if ( nRow < m_nStartRow ) { m_nStartCol = CEditView::MAXCOL; } else { m_nStartCol = 0; nRow++; } nRow = min( nRow, nLineCount - 1 ); nRow = max( 0, nRow ); // selecting the last line should just go to the end of the line since // there is no line below nRow. if ( nLineCount && ( nRow == nLineCount - 1 ) ) { nCol = pBuffer->GetLineLength( nRow ); } } else { nRow = min( nRow, nLineCount - 1 ); nRow = max( 0, nRow ); nCol = pBuffer->ConvertViewColToBufferCol( nRow, nCol ); } // since the column might have changed above, let's refetch the // char rect. m_pView->GetCharBoundingRect( nCol, nRow, &rcChar ); if ( !IsRectEmpty( &rcChar ) && ( x > ( ( rcChar.left + rcChar.right ) / 2 ) ) ) { // cursor is closer to the next char nCol += pBuffer->GetCharSize( nRow, nCol ); } if ( bEmpty ) { SetEmptySelection( nCol, nRow ); } else { if ( nCol != m_nEndCol || nRow != m_nEndRow ) { ExtendTo( nCol, nRow ); } } }