/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #include #include #include #include #include #include #include #include #include #include #include #include // *** edit engine *** #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // *** edit engine *** namespace { struct Func_SetType { sal_Int32 mnType; explicit Func_SetType( sal_Int32 nType ) : mnType( nType ) {} void operator()( ScCsvColState& rState ) const { rState.mnType = mnType; } }; struct Func_Select { bool mbSelect; explicit Func_Select( bool bSelect ) : mbSelect( bSelect ) {} void operator()( ScCsvColState& rState ) const { rState.Select( mbSelect ); } }; } ScCsvGrid::ScCsvGrid(const ScCsvLayoutData& rData, std::unique_ptr xPopup, ScCsvTableBox* pTableBox) : ScCsvControl(rData) , mpTableBox(pTableBox) , mpBackgrDev( VclPtr::Create() ) , mpGridDev( VclPtr::Create() ) , mxPopup(std::move(xPopup)) , mpColorConfig( nullptr ) , mpEditEngine( new ScEditEngineDefaulter( EditEngine::CreatePool().get(), true ) ) , maColStates( 1 ) , maTypeNames( 1 ) , mnFirstImpLine( 0 ) , mnRecentSelCol( CSV_COLUMN_INVALID ) , mnMTCurrCol( SAL_MAX_UINT32 ) , mbTracking( false ) , mbMTSelecting( false ) { mpEditEngine->SetRefDevice( mpBackgrDev.get() ); mpEditEngine->SetRefMapMode( MapMode( MapUnit::MapPixel ) ); maEdEngSize = mpEditEngine->GetPaperSize(); } void ScCsvGrid::SetDrawingArea(weld::DrawingArea* pDrawingArea) { OutputDevice& rRefDevice = pDrawingArea->get_ref_device(); maHeaderFont = Application::GetSettings().GetStyleSettings().GetAppFont(); // expand the point size of the desired font to the equivalent pixel size weld::SetPointFont(rRefDevice, maHeaderFont); maHeaderFont = rRefDevice.GetFont(); // Because this is an always LeftToRight layout widget the initial size of // this widget needs to be smaller than the size of the parent scrolling // window (ScCsvTableBox ctor) because in RTL mode the alignment is against // the right edge of the parent, and if larger than the scrolling window // the left edge will be lost. If this widget is smaller than the scrolling // window it is stretched to fit the parent and the problem doesn't arise. Size aInitialSize(10, 10); if (comphelper::LibreOfficeKit::isActive()) aInitialSize = Size(-1, 150); ScCsvControl::SetDrawingArea(pDrawingArea); pDrawingArea->set_size_request(aInitialSize.Width(), aInitialSize.Height()); SetOutputSizePixel(aInitialSize); EnableRTL( false ); // RTL InitFonts(); ImplClearSplits(); } ScCsvGrid::~ScCsvGrid() { OSL_ENSURE(mpColorConfig, "the object hasn't been initialized properly"); if (mpColorConfig) mpColorConfig->RemoveListener(this); mpBackgrDev.disposeAndClear(); mpGridDev.disposeAndClear(); } void ScCsvGrid::Init() { OSL_PRECOND(!mpColorConfig, "the object has already been initialized"); mpColorConfig = &ScModule::get()->GetColorConfig(); InitColors(); mpColorConfig->AddListener(this); } // common grid handling ------------------------------------------------------- void ScCsvGrid::UpdateLayoutData() { DisableRepaint(); OutputDevice& rRefDevice = GetDrawingArea()->get_ref_device(); rRefDevice.SetFont(maMonoFont); Execute(CSVCMD_SETCHARWIDTH, rRefDevice.GetTextWidth(OUString('X'))); Execute(CSVCMD_SETLINEHEIGHT, rRefDevice.GetTextHeight() + 1); rRefDevice.SetFont(maHeaderFont); Execute(CSVCMD_SETHDRHEIGHT, rRefDevice.GetTextHeight() + 1); UpdateOffsetX(); EnableRepaint(); } void ScCsvGrid::UpdateOffsetX() { sal_Int32 nLastLine = GetLastVisLine() + 1; sal_Int32 nDigits = 2; for (;;) { nLastLine /= 10; if (!nLastLine) break; ++nDigits; } nDigits = std::max( nDigits, sal_Int32( 3 ) ); Execute(CSVCMD_SETHDRWIDTH, GetDrawingArea()->get_approximate_digit_width() * nDigits); } void ScCsvGrid::ApplyLayout( const ScCsvLayoutData& rOldData ) { ScCsvDiff nDiff = GetLayoutData().GetDiff( rOldData ); if( nDiff == ScCsvDiff::Equal ) return; DisableRepaint(); if( nDiff & ScCsvDiff::RulerCursor ) { ImplInvertCursor( rOldData.mnPosCursor ); ImplInvertCursor( GetRulerCursorPos() ); } if( nDiff & ScCsvDiff::PosCount ) { if( GetPosCount() < rOldData.mnPosCount ) { SelectAll( false ); maSplits.RemoveRange( GetPosCount(), rOldData.mnPosCount ); } else maSplits.Remove( rOldData.mnPosCount ); maSplits.Insert( GetPosCount() ); maColStates.resize( maSplits.Count() - 1 ); } if( nDiff & ScCsvDiff::LineOffset ) { Execute( CSVCMD_UPDATECELLTEXTS ); UpdateOffsetX(); } ScCsvDiff nHVDiff = nDiff & (ScCsvDiff::HorizontalMask | ScCsvDiff::VerticalMask); if( nHVDiff == ScCsvDiff::PosOffset ) ImplDrawHorzScrolled( rOldData.mnPosOffset ); else if( nHVDiff != ScCsvDiff::Equal ) InvalidateGfx(); EnableRepaint(); if( nDiff & (ScCsvDiff::PosOffset | ScCsvDiff::LineOffset) ) AccSendVisibleEvent(); } void ScCsvGrid::SetFirstImportedLine( sal_Int32 nLine ) { ImplDrawFirstLineSep( false ); mnFirstImpLine = nLine; ImplDrawFirstLineSep( true ); ImplDrawGridDev(); Repaint(); } sal_Int32 ScCsvGrid::GetNoScrollCol( sal_Int32 nPos ) const { sal_Int32 nNewPos = nPos; if( nNewPos != CSV_POS_INVALID ) { if( nNewPos < GetFirstVisPos() + CSV_SCROLL_DIST ) { sal_Int32 nScroll = (GetFirstVisPos() > 0) ? CSV_SCROLL_DIST : 0; nNewPos = GetFirstVisPos() + nScroll; } else if( nNewPos > GetLastVisPos() - CSV_SCROLL_DIST - 1 ) { sal_Int32 nScroll = (GetFirstVisPos() < GetMaxPosOffset()) ? CSV_SCROLL_DIST : 0; nNewPos = GetLastVisPos() - nScroll - 1; } } return nNewPos; } void ScCsvGrid::InitColors() { OSL_PRECOND(mpColorConfig, "the object hasn't been initialized properly"); if ( !mpColorConfig ) return; maBackColor = mpColorConfig->GetColorValue( ::svtools::DOCCOLOR ).nColor; maGridColor = mpColorConfig->GetColorValue( ::svtools::CALCGRID ).nColor; maGridPBColor = mpColorConfig->GetColorValue( ::svtools::CALCPAGEBREAK ).nColor; maAppBackColor = mpColorConfig->GetColorValue( ::svtools::APPBACKGROUND ).nColor; maTextColor = mpColorConfig->GetColorValue( ::svtools::FONTCOLOR, false ).nColor; // tdf#147386 If Automatic font color is used, then check background color and use Black/White as font color if ( maTextColor == COL_AUTO ) { if ( maBackColor.IsDark() ) maTextColor = COL_WHITE; else maTextColor = COL_BLACK; } const StyleSettings& rSett = Application::GetSettings().GetStyleSettings(); maHeaderBackColor = rSett.GetFaceColor(); maHeaderGridColor = rSett.GetDarkShadowColor(); maHeaderTextColor = rSett.GetButtonTextColor(); maSelectColor = rSett.GetActiveColor(); InvalidateGfx(); } void ScCsvGrid::InitFonts() { maMonoFont = OutputDevice::GetDefaultFont( DefaultFontType::FIXED, LANGUAGE_ENGLISH_US, GetDefaultFontFlags::NONE ); maMonoFont.SetFontSize( Size( maMonoFont.GetFontSize().Width(), maHeaderFont.GetFontSize().Height() ) ); /* *** Set edit engine defaults *** maMonoFont for Latin script, smaller default font for Asian and Complex script. */ // get default fonts SvxFontItem aLatinItem( EE_CHAR_FONTINFO ); SvxFontItem aAsianItem( EE_CHAR_FONTINFO_CJK ); SvxFontItem aComplexItem( EE_CHAR_FONTINFO_CTL ); ::GetDefaultFonts( aLatinItem, aAsianItem, aComplexItem ); // create item set for defaults auto pDefSet = std::make_unique(mpEditEngine->GetEmptyItemSet()); EditEngine::SetFontInfoInItemSet(*pDefSet, maMonoFont); pDefSet->Put(aAsianItem); pDefSet->Put(aComplexItem); // set Asian/Complex font size to height of character in Latin font sal_uLong nFontHt = static_cast< sal_uLong >( maMonoFont.GetFontSize().Height() ); pDefSet->Put(SvxFontHeightItem(nFontHt, 100, EE_CHAR_FONTHEIGHT_CJK)); pDefSet->Put(SvxFontHeightItem(nFontHt, 100, EE_CHAR_FONTHEIGHT_CTL)); // copy other items from default font const SfxPoolItem& rWeightItem = pDefSet->Get(EE_CHAR_WEIGHT); std::unique_ptr pNewItem(rWeightItem.Clone()); pNewItem->SetWhich(EE_CHAR_WEIGHT_CJK); pDefSet->Put(*pNewItem); pNewItem->SetWhich(EE_CHAR_WEIGHT_CTL); pDefSet->Put(*pNewItem); const SfxPoolItem& rItalicItem = pDefSet->Get(EE_CHAR_ITALIC); pNewItem.reset(rItalicItem.Clone()); pNewItem->SetWhich(EE_CHAR_ITALIC_CJK); pDefSet->Put(*pNewItem); pNewItem->SetWhich(EE_CHAR_ITALIC_CTL); pDefSet->Put(*pNewItem); const SfxPoolItem& rLangItem = pDefSet->Get(EE_CHAR_LANGUAGE); pNewItem.reset(rLangItem.Clone()); pNewItem->SetWhich(EE_CHAR_LANGUAGE_CJK); pDefSet->Put(*pNewItem); pNewItem->SetWhich(EE_CHAR_LANGUAGE_CTL); pDefSet->Put(*pNewItem); mpEditEngine->SetDefaults(std::move(pDefSet)); InvalidateGfx(); } void ScCsvGrid::InitSizeData() { maWinSize = GetOutputSizePixel(); mpBackgrDev->SetOutputSizePixel( maWinSize ); mpGridDev->SetOutputSizePixel( maWinSize ); InvalidateGfx(); } // split handling ------------------------------------------------------------- void ScCsvGrid::InsertSplit( sal_Int32 nPos ) { if( ImplInsertSplit( nPos ) ) { DisableRepaint(); Execute( CSVCMD_EXPORTCOLUMNTYPE ); Execute( CSVCMD_UPDATECELLTEXTS ); sal_uInt32 nColIx = GetColumnFromPos( nPos ); ImplDrawColumn( nColIx - 1 ); ImplDrawColumn( nColIx ); ValidateGfx(); // performance: do not redraw all columns EnableRepaint(); } } void ScCsvGrid::RemoveSplit( sal_Int32 nPos ) { if( ImplRemoveSplit( nPos ) ) { DisableRepaint(); Execute( CSVCMD_EXPORTCOLUMNTYPE ); Execute( CSVCMD_UPDATECELLTEXTS ); ImplDrawColumn( GetColumnFromPos( nPos ) ); ValidateGfx(); // performance: do not redraw all columns EnableRepaint(); } } void ScCsvGrid::MoveSplit( sal_Int32 nPos, sal_Int32 nNewPos ) { sal_uInt32 nColIx = GetColumnFromPos( nPos ); if( nColIx == CSV_COLUMN_INVALID ) return; DisableRepaint(); if( (GetColumnPos( nColIx - 1 ) < nNewPos) && (nNewPos < GetColumnPos( nColIx + 1 )) ) { // move a split in the range between 2 others -> keep selection state of both columns maSplits.Remove( nPos ); maSplits.Insert( nNewPos ); Execute( CSVCMD_UPDATECELLTEXTS ); ImplDrawColumn( nColIx - 1 ); ImplDrawColumn( nColIx ); ValidateGfx(); // performance: do not redraw all columns AccSendTableUpdateEvent( nColIx - 1, nColIx ); } else { ImplRemoveSplit( nPos ); ImplInsertSplit( nNewPos ); Execute( CSVCMD_EXPORTCOLUMNTYPE ); Execute( CSVCMD_UPDATECELLTEXTS ); } EnableRepaint(); } void ScCsvGrid::RemoveAllSplits() { DisableRepaint(); ImplClearSplits(); Execute( CSVCMD_EXPORTCOLUMNTYPE ); Execute( CSVCMD_UPDATECELLTEXTS ); EnableRepaint(); } void ScCsvGrid::SetSplits( const ScCsvSplits& rSplits ) { DisableRepaint(); ImplClearSplits(); sal_uInt32 nCount = rSplits.Count(); for( sal_uInt32 nIx = 0; nIx < nCount; ++nIx ) maSplits.Insert( rSplits[ nIx ] ); maColStates.clear(); maColStates.resize( maSplits.Count() - 1 ); Execute( CSVCMD_EXPORTCOLUMNTYPE ); Execute( CSVCMD_UPDATECELLTEXTS ); EnableRepaint(); } bool ScCsvGrid::ImplInsertSplit( sal_Int32 nPos ) { sal_uInt32 nColIx = GetColumnFromPos( nPos ); bool bRet = (nColIx < GetColumnCount()) && maSplits.Insert( nPos ); if( bRet ) { ScCsvColState aState( GetColumnType( nColIx ) ); aState.Select( IsSelected( nColIx ) && IsSelected( nColIx + 1 ) ); maColStates.insert( maColStates.begin() + nColIx + 1, aState ); AccSendInsertColumnEvent( nColIx + 1, nColIx + 1 ); AccSendTableUpdateEvent( nColIx, nColIx ); } return bRet; } bool ScCsvGrid::ImplRemoveSplit( sal_Int32 nPos ) { bool bRet = maSplits.Remove( nPos ); if( bRet ) { sal_uInt32 nColIx = GetColumnFromPos( nPos ); bool bSel = IsSelected( nColIx ) || IsSelected( nColIx + 1 ); maColStates.erase( maColStates.begin() + nColIx + 1 ); maColStates[ nColIx ].Select( bSel ); AccSendRemoveColumnEvent( nColIx + 1, nColIx + 1 ); AccSendTableUpdateEvent( nColIx, nColIx ); } return bRet; } void ScCsvGrid::ImplClearSplits() { sal_uInt32 nColumns = GetColumnCount(); maSplits.Clear(); maSplits.Insert( 0 ); maSplits.Insert( GetPosCount() ); maColStates.resize( 1 ); InvalidateGfx(); AccSendRemoveColumnEvent( 1, nColumns - 1 ); } // columns/column types ------------------------------------------------------- sal_uInt32 ScCsvGrid::GetFirstVisColumn() const { return GetColumnFromPos( GetFirstVisPos() ); } sal_uInt32 ScCsvGrid::GetLastVisColumn() const { return GetColumnFromPos( std::min( GetLastVisPos(), GetPosCount() ) - 1 ); } bool ScCsvGrid::IsValidColumn( sal_uInt32 nColIndex ) const { return nColIndex < GetColumnCount(); } bool ScCsvGrid::IsVisibleColumn( sal_uInt32 nColIndex ) const { return IsValidColumn( nColIndex ) && (GetColumnPos( nColIndex ) < GetLastVisPos()) && (GetFirstVisPos() < GetColumnPos( nColIndex + 1 )); } sal_Int32 ScCsvGrid::GetColumnX( sal_uInt32 nColIndex ) const { return GetX( GetColumnPos( nColIndex ) ); } sal_uInt32 ScCsvGrid::GetColumnFromX( sal_Int32 nX ) const { sal_Int32 nPos = (nX - GetFirstX()) / GetCharWidth() + GetFirstVisPos(); return ((GetFirstVisPos() <= nPos) && (nPos <= GetLastVisPos())) ? GetColumnFromPos( nPos ) : CSV_COLUMN_INVALID; } sal_uInt32 ScCsvGrid::GetColumnFromPos( sal_Int32 nPos ) const { return maSplits.UpperBound( nPos ); } sal_Int32 ScCsvGrid::GetColumnWidth( sal_uInt32 nColIndex ) const { return IsValidColumn( nColIndex ) ? (GetColumnPos( nColIndex + 1 ) - GetColumnPos( nColIndex )) : 0; } void ScCsvGrid::SetColumnStates( ScCsvColStateVec&& rStates ) { maColStates = std::move(rStates); maColStates.resize( maSplits.Count() - 1 ); Execute( CSVCMD_EXPORTCOLUMNTYPE ); AccSendTableUpdateEvent( 0, GetColumnCount(), false ); AccSendSelectionEvent(); } sal_Int32 ScCsvGrid::GetColumnType( sal_uInt32 nColIndex ) const { return IsValidColumn( nColIndex ) ? maColStates[ nColIndex ].mnType : CSV_TYPE_NOSELECTION; } void ScCsvGrid::SetColumnType( sal_uInt32 nColIndex, sal_Int32 nColType ) { if( IsValidColumn( nColIndex ) ) { maColStates[ nColIndex ].mnType = nColType; AccSendTableUpdateEvent( nColIndex, nColIndex, false ); } } sal_Int32 ScCsvGrid::GetSelColumnType() const { sal_uInt32 nColIx = GetFirstSelected(); if( nColIx == CSV_COLUMN_INVALID ) return CSV_TYPE_NOSELECTION; sal_Int32 nType = GetColumnType( nColIx ); while( (nColIx != CSV_COLUMN_INVALID) && (nType != CSV_TYPE_MULTI) ) { if( nType != GetColumnType( nColIx ) ) nType = CSV_TYPE_MULTI; nColIx = GetNextSelected( nColIx ); } return nType; } void ScCsvGrid::SetSelColumnType( sal_Int32 nType ) { if( (nType != CSV_TYPE_MULTI) && (nType != CSV_TYPE_NOSELECTION) ) { for( sal_uInt32 nColIx = GetFirstSelected(); nColIx != CSV_COLUMN_INVALID; nColIx = GetNextSelected( nColIx ) ) SetColumnType( nColIx, nType ); Repaint( true ); Execute( CSVCMD_EXPORTCOLUMNTYPE ); } } void ScCsvGrid::SetTypeNames( std::vector&& rTypeNames ) { OSL_ENSURE( !rTypeNames.empty(), "ScCsvGrid::SetTypeNames - vector is empty" ); maTypeNames = std::move(rTypeNames); Repaint( true ); mxPopup->clear(); sal_uInt32 nCount = maTypeNames.size(); for (sal_uInt32 nIx = 0; nIx < nCount; ++nIx) mxPopup->append(OUString::number(nIx), maTypeNames[nIx]); ::std::for_each( maColStates.begin(), maColStates.end(), Func_SetType( CSV_TYPE_DEFAULT ) ); } OUString ScCsvGrid::GetColumnTypeName( sal_uInt32 nColIndex ) const { sal_uInt32 nTypeIx = static_cast< sal_uInt32 >( GetColumnType( nColIndex ) ); return (nTypeIx < maTypeNames.size()) ? maTypeNames[ nTypeIx ] : OUString(); } static sal_uInt8 lcl_GetExtColumnType( sal_Int32 nIntType ) { static const sal_uInt8 pExtTypes[] = { SC_COL_STANDARD, SC_COL_TEXT, SC_COL_DMY, SC_COL_MDY, SC_COL_YMD, SC_COL_ENGLISH, SC_COL_SKIP }; static const sal_Int32 nExtTypeCount = SAL_N_ELEMENTS(pExtTypes); return pExtTypes[ ((0 <= nIntType) && (nIntType < nExtTypeCount)) ? nIntType : 0 ]; } void ScCsvGrid::FillColumnDataSep( ScAsciiOptions& rOptions ) const { sal_uInt32 nCount = GetColumnCount(); ScCsvExpDataVec aDataVec; for( sal_uInt32 nColIx = 0; nColIx < nCount; ++nColIx ) { if( GetColumnType( nColIx ) != CSV_TYPE_DEFAULT ) // 1-based column index aDataVec.emplace_back( static_cast< sal_Int32 >( nColIx + 1 ), lcl_GetExtColumnType( GetColumnType( nColIx ) ) ); } rOptions.SetColumnInfo( aDataVec ); } void ScCsvGrid::FillColumnDataFix( ScAsciiOptions& rOptions ) const { sal_uInt32 nCount = std::min( GetColumnCount(), static_cast(MAXCOLCOUNT) ); ScCsvExpDataVec aDataVec( nCount + 1 ); for( sal_uInt32 nColIx = 0; nColIx < nCount; ++nColIx ) { ScCsvExpData& rData = aDataVec[ nColIx ]; rData.mnIndex = GetColumnPos( nColIx ); rData.mnType = lcl_GetExtColumnType( GetColumnType( nColIx ) ); } aDataVec[ nCount ].mnIndex = SAL_MAX_INT32; aDataVec[ nCount ].mnType = SC_COL_SKIP; rOptions.SetColumnInfo( aDataVec ); } void ScCsvGrid::ScrollVertRel( ScMoveMode eDir ) { sal_Int32 nLine = GetFirstVisLine(); switch( eDir ) { case MOVE_PREV: --nLine; break; case MOVE_NEXT: ++nLine; break; case MOVE_FIRST: nLine = 0; break; case MOVE_LAST: nLine = GetMaxLineOffset(); break; case MOVE_PREVPAGE: nLine -= GetVisLineCount() - 2; break; case MOVE_NEXTPAGE: nLine += GetVisLineCount() - 2; break; default: { // added to avoid warnings } } Execute( CSVCMD_SETLINEOFFSET, nLine ); } void ScCsvGrid::ExecutePopup( const Point& rPos ) { OUString sItemId = mxPopup->popup_at_rect(GetDrawingArea(), tools::Rectangle(rPos, Size(1, 1))); if (!sItemId.isEmpty()) // empty = cancelled Execute(CSVCMD_SETCOLUMNTYPE, sItemId.toInt32()); } // selection handling --------------------------------------------------------- bool ScCsvGrid::IsSelected( sal_uInt32 nColIndex ) const { return IsValidColumn( nColIndex ) && maColStates[ nColIndex ].IsSelected(); } sal_uInt32 ScCsvGrid::GetFirstSelected() const { return IsSelected( 0 ) ? 0 : GetNextSelected( 0 ); } sal_uInt32 ScCsvGrid::GetNextSelected( sal_uInt32 nFromIndex ) const { sal_uInt32 nColCount = GetColumnCount(); for( sal_uInt32 nColIx = nFromIndex + 1; nColIx < nColCount; ++nColIx ) if( IsSelected( nColIx ) ) return nColIx; return CSV_COLUMN_INVALID; } void ScCsvGrid::Select( sal_uInt32 nColIndex, bool bSelect ) { if( IsValidColumn( nColIndex ) ) { maColStates[ nColIndex ].Select( bSelect ); ImplDrawColumnSelection( nColIndex ); Repaint(); Execute( CSVCMD_EXPORTCOLUMNTYPE ); if( bSelect ) mnRecentSelCol = nColIndex; AccSendSelectionEvent(); } } void ScCsvGrid::ToggleSelect( sal_uInt32 nColIndex ) { Select( nColIndex, !IsSelected( nColIndex ) ); } void ScCsvGrid::SelectRange( sal_uInt32 nColIndex1, sal_uInt32 nColIndex2, bool bSelect ) { if( nColIndex1 == CSV_COLUMN_INVALID ) Select( nColIndex2 ); else if( nColIndex2 == CSV_COLUMN_INVALID ) Select( nColIndex1 ); else if( nColIndex1 > nColIndex2 ) { SelectRange( nColIndex2, nColIndex1, bSelect ); if( bSelect ) mnRecentSelCol = nColIndex1; } else if( IsValidColumn( nColIndex1 ) && IsValidColumn( nColIndex2 ) ) { for( sal_uInt32 nColIx = nColIndex1; nColIx <= nColIndex2; ++nColIx ) { maColStates[ nColIx ].Select( bSelect ); ImplDrawColumnSelection( nColIx ); } Repaint(); Execute( CSVCMD_EXPORTCOLUMNTYPE ); if( bSelect ) mnRecentSelCol = nColIndex1; AccSendSelectionEvent(); } } void ScCsvGrid::SelectAll( bool bSelect ) { SelectRange( 0, GetColumnCount() - 1, bSelect ); } void ScCsvGrid::MoveCursor( sal_uInt32 nColIndex ) { DisableRepaint(); if( IsValidColumn( nColIndex ) ) { sal_Int32 nPosBeg = GetColumnPos( nColIndex ); sal_Int32 nPosEnd = GetColumnPos( nColIndex + 1 ); sal_Int32 nMinPos = std::max( nPosBeg - CSV_SCROLL_DIST, sal_Int32( 0 ) ); sal_Int32 nMaxPos = std::min( nPosEnd - GetVisPosCount() + CSV_SCROLL_DIST + sal_Int32( 1 ), nMinPos ); if( nPosBeg - CSV_SCROLL_DIST + 1 <= GetFirstVisPos() ) Execute( CSVCMD_SETPOSOFFSET, nMinPos ); else if( nPosEnd + CSV_SCROLL_DIST >= GetLastVisPos() ) Execute( CSVCMD_SETPOSOFFSET, nMaxPos ); } Execute( CSVCMD_MOVEGRIDCURSOR, GetColumnPos( nColIndex ) ); EnableRepaint(); } void ScCsvGrid::MoveCursorRel( ScMoveMode eDir ) { if( GetFocusColumn() == CSV_COLUMN_INVALID ) return; switch( eDir ) { case MOVE_FIRST: MoveCursor( 0 ); break; case MOVE_LAST: MoveCursor( GetColumnCount() - 1 ); break; case MOVE_PREV: if( GetFocusColumn() > 0 ) MoveCursor( GetFocusColumn() - 1 ); break; case MOVE_NEXT: if( GetFocusColumn() < GetColumnCount() - 1 ) MoveCursor( GetFocusColumn() + 1 ); break; default: { // added to avoid warnings } } } void ScCsvGrid::ImplClearSelection() { ::std::for_each( maColStates.begin(), maColStates.end(), Func_Select( false ) ); ImplDrawGridDev(); } void ScCsvGrid::DoSelectAction( sal_uInt32 nColIndex, sal_uInt16 nModifier ) { if( !(nModifier & KEY_MOD1) ) ImplClearSelection(); if( nModifier & KEY_SHIFT ) // SHIFT always expands SelectRange( mnRecentSelCol, nColIndex ); else if( !(nModifier & KEY_MOD1) ) // no SHIFT/CTRL always selects 1 column Select( nColIndex ); else if( mbTracking ) // CTRL in tracking does not toggle Select( nColIndex, mbMTSelecting ); else // CTRL only toggles ToggleSelect( nColIndex ); Execute( CSVCMD_MOVEGRIDCURSOR, GetColumnPos( nColIndex ) ); } // cell contents -------------------------------------------------------------- void ScCsvGrid::ImplSetTextLineSep( sal_Int32 nLine, const OUString& rTextLine, const OUString& rSepChars, sal_Unicode cTextSep, bool bMergeSep, bool bRemoveSpace ) { if( nLine < GetFirstVisLine() ) return; sal_uInt32 nLineIx = nLine - GetFirstVisLine(); while( maTexts.size() <= nLineIx ) maTexts.emplace_back( ); std::vector& rStrVec = maTexts[ nLineIx ]; rStrVec.clear(); // scan for separators OUString aCellText; const sal_Unicode* pSepChars = rSepChars.getStr(); const sal_Unicode* pChar = rTextLine.getStr(); sal_uInt32 nColIx = 0; while( *pChar && (nColIx < sal::static_int_cast(CSV_MAXCOLCOUNT)) ) { // scan for next cell text bool bIsQuoted = false; bool bOverflowCell = false; pChar = ScImportExport::ScanNextFieldFromString( pChar, aCellText, cTextSep, pSepChars, bMergeSep, bIsQuoted, bOverflowCell, bRemoveSpace ); /* TODO: signal overflow somewhere in UI */ // update column width sal_Int32 nWidth = std::max( CSV_MINCOLWIDTH, ScImportExport::CountVisualWidth( aCellText ) + 1 ); if( IsValidColumn( nColIx ) ) { // expand existing column sal_Int32 nDiff = nWidth - GetColumnWidth( nColIx ); if( nDiff > 0 ) { Execute( CSVCMD_SETPOSCOUNT, GetPosCount() + nDiff ); for( sal_uInt32 nSplitIx = GetColumnCount() - 1; nSplitIx > nColIx; --nSplitIx ) { sal_Int32 nPos = maSplits[ nSplitIx ]; maSplits.Remove( nPos ); maSplits.Insert( nPos + nDiff ); } } } else { // append new column sal_Int32 nLastPos = GetPosCount(); Execute( CSVCMD_SETPOSCOUNT, nLastPos + nWidth ); ImplInsertSplit( nLastPos ); } if( aCellText.getLength() <= CSV_MAXSTRLEN ) rStrVec.push_back( aCellText ); else rStrVec.push_back( aCellText.copy( 0, CSV_MAXSTRLEN ) ); ++nColIx; } InvalidateGfx(); } void ScCsvGrid::ImplSetTextLineFix( sal_Int32 nLine, std::u16string_view rTextLine ) { if( nLine < GetFirstVisLine() ) return; sal_Int32 nWidth = ScImportExport::CountVisualWidth( rTextLine ); if( nWidth > GetPosCount() ) Execute( CSVCMD_SETPOSCOUNT, nWidth ); sal_uInt32 nLineIx = nLine - GetFirstVisLine(); while( maTexts.size() <= nLineIx ) maTexts.emplace_back( ); std::vector& rStrVec = maTexts[ nLineIx ]; rStrVec.clear(); sal_uInt32 nColCount = GetColumnCount(); sal_Int32 nStrLen = rTextLine.size(); sal_Int32 nStrIx = 0; for( sal_uInt32 nColIx = 0; (nColIx < nColCount) && (nStrIx < nStrLen); ++nColIx ) { sal_Int32 nColWidth = GetColumnWidth( nColIx ); sal_Int32 nLastIx = nStrIx; ScImportExport::CountVisualWidth( rTextLine, nLastIx, nColWidth ); sal_Int32 nLen = std::min( CSV_MAXSTRLEN, nLastIx - nStrIx ); rStrVec.push_back( OUString(rTextLine.substr( nStrIx, nLen )) ); nStrIx = nStrIx + nLen; } InvalidateGfx(); } OUString ScCsvGrid::GetCellText( sal_uInt32 nColIndex, sal_Int32 nLine ) const { if( nLine < GetFirstVisLine() ) return OUString(); sal_uInt32 nLineIx = nLine - GetFirstVisLine(); if( nLineIx >= maTexts.size() ) return OUString(); const std::vector& rStrVec = maTexts[ nLineIx ]; if( nColIndex >= rStrVec.size() ) return OUString(); return rStrVec[ nColIndex ]; } // event handling ------------------------------------------------------------- void ScCsvGrid::Resize() { mpTableBox->InitControls(); ScCsvControl::Resize(); InitSizeData(); Execute( CSVCMD_UPDATECELLTEXTS ); } void ScCsvGrid::GetFocus() { ScCsvControl::GetFocus(); Execute( CSVCMD_MOVEGRIDCURSOR, GetNoScrollCol( GetGridCursorPos() ) ); Repaint(); } void ScCsvGrid::LoseFocus() { ScCsvControl::LoseFocus(); Repaint(); } bool ScCsvGrid::MouseButtonDown( const MouseEvent& rMEvt ) { DisableRepaint(); if( !HasFocus() ) GrabFocus(); Point aPos( rMEvt.GetPosPixel() ); sal_uInt32 nColIx = GetColumnFromX( aPos.X() ); if( rMEvt.IsLeft() ) { if( (GetFirstX() > aPos.X()) || (aPos.X() > GetLastX()) ) // in header column { if( aPos.Y() <= GetHdrHeight() ) SelectAll(); } else if( IsValidColumn( nColIx ) ) { DoSelectAction( nColIx, rMEvt.GetModifier() ); mnMTCurrCol = nColIx; mbMTSelecting = IsSelected( nColIx ); mbTracking = true; } } EnableRepaint(); return true; } bool ScCsvGrid::MouseButtonUp( const MouseEvent& ) { mbTracking = false; return true; } bool ScCsvGrid::MouseMove( const MouseEvent& rMEvt ) { if (!mbTracking) return true; DisableRepaint(); sal_Int32 nPos = (rMEvt.GetPosPixel().X() - GetFirstX()) / GetCharWidth() + GetFirstVisPos(); // on mouse tracking: keep position valid nPos = std::clamp( nPos, sal_Int32(0), GetPosCount() - 1 ); Execute( CSVCMD_MAKEPOSVISIBLE, nPos ); sal_uInt32 nColIx = GetColumnFromPos( nPos ); if( mnMTCurrCol != nColIx ) { DoSelectAction( nColIx, rMEvt.GetModifier() ); mnMTCurrCol = nColIx; } EnableRepaint(); return true; } bool ScCsvGrid::KeyInput( const KeyEvent& rKEvt ) { const vcl::KeyCode& rKCode = rKEvt.GetKeyCode(); sal_uInt16 nCode = rKCode.GetCode(); bool bShift = rKCode.IsShift(); bool bMod1 = rKCode.IsMod1(); if( !rKCode.IsMod2() ) { ScMoveMode eHDir = GetHorzDirection( nCode, !bMod1 ); ScMoveMode eVDir = GetVertDirection( nCode, bMod1 ); if( eHDir != MOVE_NONE ) { DisableRepaint(); MoveCursorRel( eHDir ); if( !bMod1 ) ImplClearSelection(); if( bShift ) SelectRange( mnRecentSelCol, GetFocusColumn() ); else if( !bMod1 ) Select( GetFocusColumn() ); EnableRepaint(); } else if( eVDir != MOVE_NONE ) ScrollVertRel( eVDir ); else if( nCode == KEY_SPACE ) { if( !bMod1 ) ImplClearSelection(); if( bShift ) SelectRange( mnRecentSelCol, GetFocusColumn() ); else if( bMod1 ) ToggleSelect( GetFocusColumn() ); else Select( GetFocusColumn() ); } else if( !bShift && bMod1 ) { if( nCode == KEY_A ) SelectAll(); else if( (KEY_1 <= nCode) && (nCode <= KEY_9) ) { sal_uInt32 nType = nCode - KEY_1; if( nType < maTypeNames.size() ) Execute( CSVCMD_SETCOLUMNTYPE, nType ); } } } return rKCode.GetGroup() == KEYGROUP_CURSOR; } bool ScCsvGrid::Command( const CommandEvent& rCEvt ) { bool bConsumed = true; switch( rCEvt.GetCommand() ) { case CommandEventId::ContextMenu: { if( rCEvt.IsMouseEvent() ) { Point aPos( rCEvt.GetMousePosPixel() ); sal_uInt32 nColIx = GetColumnFromX( aPos.X() ); if( IsValidColumn( nColIx ) && (GetFirstX() <= aPos.X()) && (aPos.X() <= GetLastX()) ) { if( !IsSelected( nColIx ) ) DoSelectAction( nColIx, 0 ); // focus & select ExecutePopup( aPos ); } } else { sal_uInt32 nColIx = GetFocusColumn(); if( !IsSelected( nColIx ) ) Select( nColIx ); sal_Int32 nX1 = std::max( GetColumnX( nColIx ), GetFirstX() ); sal_Int32 nX2 = std::min( GetColumnX( nColIx + 1 ), GetWidth() ); ExecutePopup( Point( (nX1 + nX2) / 2, GetHeight() / 2 ) ); } break; } case CommandEventId::Wheel: { tools::Rectangle aRect( Point(), maWinSize ); if( aRect.Contains( rCEvt.GetMousePosPixel() ) ) { const CommandWheelData* pData = rCEvt.GetWheelData(); if( pData && (pData->GetMode() == CommandWheelMode::SCROLL) && !pData->IsHorz() ) Execute( CSVCMD_SETLINEOFFSET, GetFirstVisLine() - pData->GetNotchDelta() ); } break; } default: bConsumed = false; break; } return bConsumed; } void ScCsvGrid::StyleUpdated() { InitColors(); InitFonts(); UpdateLayoutData(); Execute( CSVCMD_UPDATECELLTEXTS ); ScCsvControl::StyleUpdated(); } void ScCsvGrid::ConfigurationChanged( utl::ConfigurationBroadcaster*, ConfigurationHints ) { InitColors(); Repaint(); } // painting ------------------------------------------------------------------- void ScCsvGrid::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) { ImplRedraw(rRenderContext); } void ScCsvGrid::ImplRedraw(vcl::RenderContext& rRenderContext) { if( IsVisible() ) { if( !IsValidGfx() ) { ValidateGfx(); ImplDrawBackgrDev(); ImplDrawGridDev(); } rRenderContext.DrawOutDev( Point(), maWinSize, Point(), maWinSize, *mpGridDev ); } } EditEngine* ScCsvGrid::GetEditEngine() { return mpEditEngine.get(); } void ScCsvGrid::ImplSetColumnClipRegion( OutputDevice& rOutDev, sal_uInt32 nColIndex ) { rOutDev.SetClipRegion( vcl::Region( tools::Rectangle( std::max( GetColumnX( nColIndex ), GetFirstX() ) + 1, 0, std::min( GetColumnX( nColIndex + 1 ), GetLastX() ), GetHeight() - 1 ) ) ); } void ScCsvGrid::ImplDrawColumnHeader( OutputDevice& rOutDev, sal_uInt32 nColIndex, Color aFillColor ) { sal_Int32 nX1 = GetColumnX( nColIndex ) + 1; sal_Int32 nX2 = GetColumnX( nColIndex + 1 ); sal_Int32 nHdrHt = GetHdrHeight(); rOutDev.SetLineColor(); rOutDev.SetFillColor( aFillColor ); rOutDev.DrawRect( tools::Rectangle( nX1, 0, nX2, nHdrHt ) ); rOutDev.SetFont( maHeaderFont ); rOutDev.SetTextColor( maHeaderTextColor ); rOutDev.SetTextFillColor(); rOutDev.DrawText( Point( nX1 + 1, 0 ), GetColumnTypeName( nColIndex ) ); rOutDev.SetLineColor( maHeaderGridColor ); rOutDev.DrawLine( Point( nX1, nHdrHt ), Point( nX2, nHdrHt ) ); rOutDev.DrawLine( Point( nX2, 0 ), Point( nX2, nHdrHt ) ); } void ScCsvGrid::ImplDrawCellText( const Point& rPos, const OUString& rText ) { OUString aPlainText = rText.replaceAll( "\t", " " ); aPlainText = aPlainText.replaceAll( "\n", " " ); mpEditEngine->SetPaperSize( maEdEngSize ); mpEditEngine->SetTextCurrentDefaults(aPlainText); mpEditEngine->Draw(*mpBackgrDev, rPos); sal_Int32 nCharIx = 0; while( (nCharIx = rText.indexOf( '\t', nCharIx )) != -1 ) { sal_Int32 nX1 = rPos.X() + GetCharWidth() * nCharIx; sal_Int32 nX2 = nX1 + GetCharWidth() - 2; sal_Int32 nY = rPos.Y() + GetLineHeight() / 2; Color aColor( maTextColor ); mpBackgrDev->SetLineColor( aColor ); mpBackgrDev->DrawLine( Point( nX1, nY ), Point( nX2, nY ) ); mpBackgrDev->DrawLine( Point( nX2 - 2, nY - 2 ), Point( nX2, nY ) ); mpBackgrDev->DrawLine( Point( nX2 - 2, nY + 2 ), Point( nX2, nY ) ); ++nCharIx; } nCharIx = 0; while( (nCharIx = rText.indexOf( '\n', nCharIx )) != -1 ) { sal_Int32 nX1 = rPos.X() + GetCharWidth() * nCharIx; sal_Int32 nX2 = nX1 + GetCharWidth() - 2; sal_Int32 nY = rPos.Y() + GetLineHeight() / 2; Color aColor( maTextColor ); mpBackgrDev->SetLineColor( aColor ); mpBackgrDev->DrawLine( Point( nX1, nY ), Point( nX2, nY ) ); mpBackgrDev->DrawLine( Point( nX1 + 2, nY - 2 ), Point( nX1, nY ) ); mpBackgrDev->DrawLine( Point( nX1 + 2, nY + 2 ), Point( nX1, nY ) ); mpBackgrDev->DrawLine( Point( nX2, nY - 2 ), Point( nX2, nY ) ); ++nCharIx; } } void ScCsvGrid::ImplDrawFirstLineSep( bool bSet ) { if( IsVisibleLine( mnFirstImpLine ) && (mnFirstImpLine != GetFirstVisLine() ) ) { sal_Int32 nY = GetY( mnFirstImpLine ); sal_Int32 nX = std::min( GetColumnX( GetLastVisColumn() + 1 ), GetLastX() ); mpBackgrDev->SetLineColor( bSet ? maGridPBColor : maGridColor ); mpBackgrDev->DrawLine( Point( GetFirstX() + 1, nY ), Point( nX, nY ) ); } } void ScCsvGrid::ImplDrawColumnBackgr( sal_uInt32 nColIndex ) { if( !IsVisibleColumn( nColIndex ) ) return; ImplSetColumnClipRegion( *mpBackgrDev, nColIndex ); // grid mpBackgrDev->SetLineColor(); mpBackgrDev->SetFillColor( maBackColor ); sal_Int32 nX1 = GetColumnX( nColIndex ) + 1; sal_Int32 nX2 = GetColumnX( nColIndex + 1 ); sal_Int32 nY2 = GetY( GetLastVisLine() + 1 ); sal_Int32 nHdrHt = GetHdrHeight(); tools::Rectangle aRect( nX1, nHdrHt, nX2, nY2 ); mpBackgrDev->DrawRect( aRect ); mpBackgrDev->SetLineColor( maGridColor ); mpBackgrDev->DrawGrid( aRect, Size( 1, GetLineHeight() ), DrawGridFlags::HorzLines ); mpBackgrDev->DrawLine( Point( nX2, nHdrHt ), Point( nX2, nY2 ) ); ImplDrawFirstLineSep( true ); // cell texts mpEditEngine->SetDefaultItem( SvxColorItem( maTextColor, EE_CHAR_COLOR ) ); size_t nLineCount = ::std::min( static_cast< size_t >( GetLastVisLine() - GetFirstVisLine() + 1 ), maTexts.size() ); // #i67432# cut string to avoid edit engine performance problems with very large strings sal_Int32 nFirstVisPos = ::std::max( GetColumnPos( nColIndex ), GetFirstVisPos() ); sal_Int32 nLastVisPos = ::std::min( GetColumnPos( nColIndex + 1 ), GetLastVisPos() ); sal_Int32 nStrPos = nFirstVisPos - GetColumnPos( nColIndex ); sal_Int32 nStrLen = nLastVisPos - nFirstVisPos + 1; sal_Int32 nStrX = GetX( nFirstVisPos ); for( size_t nLine = 0; nLine < nLineCount; ++nLine ) { std::vector& rStrVec = maTexts[ nLine ]; if( (nColIndex < rStrVec.size()) && (rStrVec[ nColIndex ].getLength() > nStrPos) ) { const OUString& rStr = rStrVec[ nColIndex ]; OUString aText = rStr.copy( nStrPos, ::std::min( nStrLen, rStr.getLength() - nStrPos) ); ImplDrawCellText( Point( nStrX, GetY( GetFirstVisLine() + nLine ) ), aText ); } } // header ImplDrawColumnHeader( *mpBackgrDev, nColIndex, maHeaderBackColor ); mpBackgrDev->SetClipRegion(); } void ScCsvGrid::ImplDrawRowHeaders() { mpBackgrDev->SetLineColor(); mpBackgrDev->SetFillColor( maAppBackColor ); Point aPoint( GetHdrX(), 0 ); tools::Rectangle aRect( aPoint, Size( GetHdrWidth() + 1, GetHeight() ) ); mpBackgrDev->DrawRect( aRect ); mpBackgrDev->SetFillColor( maHeaderBackColor ); aRect.SetBottom( GetY( GetLastVisLine() + 1 ) ); mpBackgrDev->DrawRect( aRect ); // line numbers mpBackgrDev->SetFont( maHeaderFont ); mpBackgrDev->SetTextColor( maHeaderTextColor ); mpBackgrDev->SetTextFillColor(); sal_Int32 nLastLine = GetLastVisLine(); for( sal_Int32 nLine = GetFirstVisLine(); nLine <= nLastLine; ++nLine ) { OUString aText( OUString::number( nLine + 1 ) ); sal_Int32 nX = GetHdrX() + (GetHdrWidth() - mpBackgrDev->GetTextWidth( aText )) / 2; mpBackgrDev->DrawText( Point( nX, GetY( nLine ) ), aText ); } // grid mpBackgrDev->SetLineColor( maHeaderGridColor ); if( IsRTL() ) { mpBackgrDev->DrawLine( Point( 0, 0 ), Point( 0, GetHeight() - 1 ) ); mpBackgrDev->DrawLine( aRect.TopLeft(), aRect.BottomLeft() ); } else mpBackgrDev->DrawLine( aRect.TopRight(), aRect.BottomRight() ); aRect.SetTop( GetHdrHeight() ); mpBackgrDev->DrawGrid( aRect, Size( 1, GetLineHeight() ), DrawGridFlags::HorzLines ); } void ScCsvGrid::ImplDrawBackgrDev() { mpBackgrDev->SetLineColor(); mpBackgrDev->SetFillColor( maAppBackColor ); mpBackgrDev->DrawRect( tools::Rectangle( Point( GetFirstX() + 1, 0 ), Size( GetWidth() - GetHdrWidth(), GetHeight() ) ) ); sal_uInt32 nLastCol = GetLastVisColumn(); if (nLastCol == CSV_COLUMN_INVALID) return; for( sal_uInt32 nColIx = GetFirstVisColumn(); nColIx <= nLastCol; ++nColIx ) ImplDrawColumnBackgr( nColIx ); ImplDrawRowHeaders(); } void ScCsvGrid::ImplDrawColumnSelection( sal_uInt32 nColIndex ) { ImplInvertCursor( GetRulerCursorPos() ); ImplSetColumnClipRegion( *mpGridDev, nColIndex ); mpGridDev->DrawOutDev( Point(), maWinSize, Point(), maWinSize, *mpBackgrDev ); if( IsSelected( nColIndex ) ) { sal_Int32 nX1 = GetColumnX( nColIndex ) + 1; sal_Int32 nX2 = GetColumnX( nColIndex + 1 ); // header tools::Rectangle aRect( nX1, 0, nX2, GetHdrHeight() ); mpGridDev->SetLineColor(); if( maHeaderBackColor.IsDark() ) // redraw with light gray background in dark mode ImplDrawColumnHeader( *mpGridDev, nColIndex, COL_LIGHTGRAY ); else { // use transparent active color mpGridDev->SetFillColor( maSelectColor ); mpGridDev->DrawTransparent( tools::PolyPolygon( tools::Polygon( aRect ) ), CSV_HDR_TRANSPARENCY ); } // column selection aRect = tools::Rectangle( nX1, GetHdrHeight() + 1, nX2, GetY( GetLastVisLine() + 1 ) - 1 ); ImplInvertRect( *mpGridDev, aRect ); } mpGridDev->SetClipRegion(); ImplInvertCursor( GetRulerCursorPos() ); } void ScCsvGrid::ImplDrawGridDev() { mpGridDev->DrawOutDev( Point(), maWinSize, Point(), maWinSize, *mpBackgrDev ); sal_uInt32 nLastCol = GetLastVisColumn(); if (nLastCol == CSV_COLUMN_INVALID) return; for( sal_uInt32 nColIx = GetFirstVisColumn(); nColIx <= nLastCol; ++nColIx ) ImplDrawColumnSelection( nColIx ); } void ScCsvGrid::ImplDrawColumn( sal_uInt32 nColIndex ) { ImplDrawColumnBackgr( nColIndex ); ImplDrawColumnSelection( nColIndex ); } void ScCsvGrid::ImplDrawHorzScrolled( sal_Int32 nOldPos ) { sal_Int32 nPos = GetFirstVisPos(); if( !IsValidGfx() || (nPos == nOldPos) ) return; if( std::abs( nPos - nOldPos ) > GetVisPosCount() / 2 ) { ImplDrawBackgrDev(); ImplDrawGridDev(); return; } Point aSrc, aDest; sal_uInt32 nFirstColIx, nLastColIx; if( nPos < nOldPos ) { aSrc = Point( GetFirstX() + 1, 0 ); aDest = Point( GetFirstX() + GetCharWidth() * (nOldPos - nPos) + 1, 0 ); nFirstColIx = GetColumnFromPos( nPos ); nLastColIx = GetColumnFromPos( nOldPos ); } else { aSrc = Point( GetFirstX() + GetCharWidth() * (nPos - nOldPos) + 1, 0 ); aDest = Point( GetFirstX() + 1, 0 ); nFirstColIx = GetColumnFromPos( std::min( nOldPos + GetVisPosCount(), GetPosCount() ) - 1 ); nLastColIx = GetColumnFromPos( std::min( nPos + GetVisPosCount(), GetPosCount() ) - 1 ); } ImplInvertCursor( GetRulerCursorPos() + (nPos - nOldPos) ); tools::Rectangle aRectangle( GetFirstX(), 0, GetLastX(), GetHeight() - 1 ); vcl::Region aClipReg( aRectangle ); mpBackgrDev->SetClipRegion( aClipReg ); mpBackgrDev->CopyArea( aDest, aSrc, maWinSize ); mpBackgrDev->SetClipRegion(); mpGridDev->SetClipRegion( aClipReg ); mpGridDev->CopyArea( aDest, aSrc, maWinSize ); mpGridDev->SetClipRegion(); ImplInvertCursor( GetRulerCursorPos() ); for( sal_uInt32 nColIx = nFirstColIx; nColIx <= nLastColIx; ++nColIx ) ImplDrawColumn( nColIx ); sal_Int32 nLastX = GetX( GetPosCount() ) + 1; if( nLastX <= GetLastX() ) { tools::Rectangle aRect( nLastX, 0, GetLastX(), GetHeight() - 1 ); mpBackgrDev->SetLineColor(); mpBackgrDev->SetFillColor( maAppBackColor ); mpBackgrDev->DrawRect( aRect ); mpGridDev->SetLineColor(); mpGridDev->SetFillColor( maAppBackColor ); mpGridDev->DrawRect( aRect ); } } void ScCsvGrid::ImplInvertCursor( sal_Int32 nPos ) { if( IsVisibleSplitPos( nPos ) ) { sal_Int32 nX = GetX( nPos ) - 1; tools::Rectangle aRect( Point( nX, 0 ), Size( 3, GetHdrHeight() ) ); ImplInvertRect( *mpGridDev, aRect ); aRect.SetTop( GetHdrHeight() + 1 ); aRect.SetBottom( GetY( GetLastVisLine() + 1 ) ); ImplInvertRect( *mpGridDev, aRect ); } } tools::Rectangle ScCsvGrid::GetFocusRect() { auto nColIndex = GetFocusColumn(); if( HasFocus() && IsVisibleColumn( nColIndex ) ) { sal_Int32 nX1 = std::max( GetColumnX( nColIndex ), GetFirstX() ) + 1; sal_Int32 nX2 = std::min( GetColumnX( nColIndex + 1 ) - sal_Int32( 1 ), GetLastX() ); sal_Int32 nY2 = std::min( GetY( GetLastVisLine() + 1 ), GetHeight() ) - 1; return tools::Rectangle( nX1, 0, nX2, nY2 ); } return weld::CustomWidgetController::GetFocusRect(); } // accessibility ============================================================== css::uno::Reference ScCsvGrid::CreateAccessible() { rtl::Reference xRef(new ScAccessibleCsvGrid(*this)); mxAccessible = xRef; return xRef; } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */