xref: /aoo42x/main/sc/source/ui/dbgui/csvgrid.cxx (revision cdf0e10c)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_sc.hxx"
30 
31 
32 // ============================================================================
33 #include "csvgrid.hxx"
34 
35 #include <algorithm>
36 #include <svtools/colorcfg.hxx>
37 #include <svl/smplhint.hxx>
38 #include <tools/poly.hxx>
39 #include "scmod.hxx"
40 #include "asciiopt.hxx"
41 #include "impex.hxx"
42 #include "AccessibleCsvControl.hxx"
43 
44 // *** edit engine ***
45 #include "scitems.hxx"
46 #include <editeng/eeitem.hxx>
47 
48 
49 #include <editeng/colritem.hxx>
50 #include <editeng/fhgtitem.hxx>
51 #include <editeng/fontitem.hxx>
52 #include <svl/itemset.hxx>
53 #include "editutil.hxx"
54 // *** edit engine ***
55 
56 
57 // ============================================================================
58 
59 struct Func_SetType
60 {
61     sal_Int32                   mnType;
62     inline                      Func_SetType( sal_Int32 nType ) : mnType( nType ) {}
63     inline void                 operator()( ScCsvColState& rState ) { rState.mnType = mnType; }
64 };
65 
66 struct Func_Select
67 {
68     bool                        mbSelect;
69     inline                      Func_Select( bool bSelect ) : mbSelect( bSelect ) {}
70     inline void                 operator()( ScCsvColState& rState ) { rState.Select( mbSelect ); }
71 };
72 
73 
74 // ============================================================================
75 
76 ScCsvGrid::ScCsvGrid( ScCsvControl& rParent ) :
77     ScCsvControl( rParent ),
78     mrColorConfig( SC_MOD()->GetColorConfig() ),
79     mpEditEngine( new ScEditEngineDefaulter( EditEngine::CreatePool(), sal_True ) ),
80     maHeaderFont( GetFont() ),
81     maColStates( 1 ),
82     maTypeNames( 1 ),
83     mnFirstImpLine( 0 ),
84     mnRecentSelCol( CSV_COLUMN_INVALID )
85 {
86     mpEditEngine->SetRefDevice( &maBackgrDev );
87     mpEditEngine->SetRefMapMode( MapMode( MAP_PIXEL ) );
88     maEdEngSize = mpEditEngine->GetPaperSize();
89 
90     maPopup.SetMenuFlags( maPopup.GetMenuFlags() | MENU_FLAG_NOAUTOMNEMONICS );
91 
92     EnableRTL( false ); // #107812# RTL
93     InitColors();
94     InitFonts();
95     ImplClearSplits();
96     mrColorConfig.AddListener(this);
97 }
98 
99 ScCsvGrid::~ScCsvGrid()
100 {
101     mrColorConfig.RemoveListener(this);
102 }
103 
104 
105 // common grid handling -------------------------------------------------------
106 
107 void ScCsvGrid::UpdateLayoutData()
108 {
109     DisableRepaint();
110     SetFont( maMonoFont );
111     Execute( CSVCMD_SETCHARWIDTH, GetTextWidth( String( 'X' ) ) );
112     Execute( CSVCMD_SETLINEHEIGHT, GetTextHeight() + 1 );
113     SetFont( maHeaderFont );
114     Execute( CSVCMD_SETHDRHEIGHT, GetTextHeight() + 1 );
115     UpdateOffsetX();
116     EnableRepaint();
117 }
118 
119 void ScCsvGrid::UpdateOffsetX()
120 {
121     sal_Int32 nLastLine = GetLastVisLine() + 1;
122     sal_Int32 nDigits = 2;
123     while( nLastLine /= 10 ) ++nDigits;
124     nDigits = Max( nDigits, sal_Int32( 3 ) );
125     Execute( CSVCMD_SETHDRWIDTH, GetTextWidth( String( '0' ) ) * nDigits );
126 }
127 
128 void ScCsvGrid::ApplyLayout( const ScCsvLayoutData& rOldData )
129 {
130     ScCsvDiff nDiff = GetLayoutData().GetDiff( rOldData );
131     if( nDiff == CSV_DIFF_EQUAL ) return;
132 
133     DisableRepaint();
134 
135     if( nDiff & CSV_DIFF_RULERCURSOR )
136     {
137         ImplInvertCursor( rOldData.mnPosCursor );
138         ImplInvertCursor( GetRulerCursorPos() );
139     }
140 
141     if( nDiff & CSV_DIFF_POSCOUNT )
142     {
143         if( GetPosCount() < rOldData.mnPosCount )
144         {
145             SelectAll( false );
146             maSplits.RemoveRange( GetPosCount(), rOldData.mnPosCount );
147         }
148         else
149             maSplits.Remove( rOldData.mnPosCount );
150         maSplits.Insert( GetPosCount() );
151         maColStates.resize( maSplits.Count() - 1 );
152     }
153 
154     if( nDiff & CSV_DIFF_LINEOFFSET )
155     {
156         Execute( CSVCMD_UPDATECELLTEXTS );
157         UpdateOffsetX();
158     }
159 
160     ScCsvDiff nHVDiff = nDiff & (CSV_DIFF_HORIZONTAL | CSV_DIFF_VERTICAL);
161     if( nHVDiff == CSV_DIFF_POSOFFSET )
162         ImplDrawHorzScrolled( rOldData.mnPosOffset );
163     else if( nHVDiff != CSV_DIFF_EQUAL )
164         InvalidateGfx();
165 
166     EnableRepaint();
167 
168     if( nDiff & (CSV_DIFF_POSOFFSET | CSV_DIFF_LINEOFFSET) )
169         AccSendVisibleEvent();
170 }
171 
172 void ScCsvGrid::SetFirstImportedLine( sal_Int32 nLine )
173 {
174     ImplDrawFirstLineSep( false );
175     mnFirstImpLine = nLine;
176     ImplDrawFirstLineSep( true );
177     ImplDrawGridDev();
178     Repaint();
179 }
180 
181 sal_Int32 ScCsvGrid::GetNoScrollCol( sal_Int32 nPos ) const
182 {
183     sal_Int32 nNewPos = nPos;
184     if( nNewPos != CSV_POS_INVALID )
185     {
186         if( nNewPos < GetFirstVisPos() + CSV_SCROLL_DIST )
187         {
188             sal_Int32 nScroll = (GetFirstVisPos() > 0) ? CSV_SCROLL_DIST : 0;
189             nNewPos = GetFirstVisPos() + nScroll;
190         }
191         else if( nNewPos > GetLastVisPos() - CSV_SCROLL_DIST - 1L )
192         {
193             sal_Int32 nScroll = (GetFirstVisPos() < GetMaxPosOffset()) ? CSV_SCROLL_DIST : 0;
194             nNewPos = GetLastVisPos() - nScroll - 1;
195         }
196     }
197     return nNewPos;
198 }
199 
200 void ScCsvGrid::InitColors()
201 {
202     maBackColor.SetColor( static_cast< sal_uInt32 >( mrColorConfig.GetColorValue( ::svtools::DOCCOLOR ).nColor ) );
203     maGridColor.SetColor( static_cast< sal_uInt32 >( mrColorConfig.GetColorValue( ::svtools::CALCGRID ).nColor ) );
204     maGridPBColor.SetColor( static_cast< sal_uInt32 >( mrColorConfig.GetColorValue( ::svtools::CALCPAGEBREAK ).nColor ) );
205     maAppBackColor.SetColor( static_cast< sal_uInt32 >( mrColorConfig.GetColorValue( ::svtools::APPBACKGROUND ).nColor ) );
206     maTextColor.SetColor( static_cast< sal_uInt32 >( mrColorConfig.GetColorValue( ::svtools::FONTCOLOR ).nColor ) );
207 
208     const StyleSettings& rSett = GetSettings().GetStyleSettings();
209     maHeaderBackColor = rSett.GetFaceColor();
210     maHeaderGridColor = rSett.GetDarkShadowColor();
211     maHeaderTextColor = rSett.GetButtonTextColor();
212     maSelectColor = rSett.GetActiveColor();
213 
214     InvalidateGfx();
215 }
216 
217 void ScCsvGrid::InitFonts()
218 {
219     maMonoFont = OutputDevice::GetDefaultFont( DEFAULTFONT_FIXED, LANGUAGE_ENGLISH_US, 0 );
220     maMonoFont.SetSize( Size( maMonoFont.GetSize().Width(), maHeaderFont.GetSize().Height() ) );
221 
222     /* *** Set edit engine defaults ***
223         maMonoFont for Latin script, smaller default font for Asian and Complex script. */
224 
225     // get default fonts
226     SvxFontItem aLatinItem( EE_CHAR_FONTINFO );
227     SvxFontItem aAsianItem( EE_CHAR_FONTINFO_CJK );
228     SvxFontItem aComplexItem( EE_CHAR_FONTINFO_CTL );
229     ::GetDefaultFonts( aLatinItem, aAsianItem, aComplexItem );
230 
231     // create item set for defaults
232     SfxItemSet aDefSet( mpEditEngine->GetEmptyItemSet() );
233     EditEngine::SetFontInfoInItemSet( aDefSet, maMonoFont );
234     aDefSet.Put( aAsianItem );
235     aDefSet.Put( aComplexItem );
236 
237     // set Asian/Complex font size to height of character in Latin font
238     sal_uLong nFontHt = static_cast< sal_uLong >( maMonoFont.GetSize().Height() );
239     aDefSet.Put( SvxFontHeightItem( nFontHt, 100, EE_CHAR_FONTHEIGHT_CJK ) );
240     aDefSet.Put( SvxFontHeightItem( nFontHt, 100, EE_CHAR_FONTHEIGHT_CTL ) );
241 
242     // copy other items from default font
243     const SfxPoolItem& rWeightItem = aDefSet.Get( EE_CHAR_WEIGHT );
244     aDefSet.Put( rWeightItem, EE_CHAR_WEIGHT_CJK );
245     aDefSet.Put( rWeightItem, EE_CHAR_WEIGHT_CTL );
246     const SfxPoolItem& rItalicItem = aDefSet.Get( EE_CHAR_ITALIC );
247     aDefSet.Put( rItalicItem, EE_CHAR_ITALIC_CJK );
248     aDefSet.Put( rItalicItem, EE_CHAR_ITALIC_CTL );
249     const SfxPoolItem& rLangItem = aDefSet.Get( EE_CHAR_LANGUAGE );
250     aDefSet.Put( rLangItem, EE_CHAR_LANGUAGE_CJK );
251     aDefSet.Put( rLangItem, EE_CHAR_LANGUAGE_CTL );
252 
253     mpEditEngine->SetDefaults( aDefSet );
254     InvalidateGfx();
255 }
256 
257 void ScCsvGrid::InitSizeData()
258 {
259     maWinSize = GetSizePixel();
260     maBackgrDev.SetOutputSizePixel( maWinSize );
261     maGridDev.SetOutputSizePixel( maWinSize );
262     InvalidateGfx();
263 }
264 
265 
266 // split handling -------------------------------------------------------------
267 
268 void ScCsvGrid::InsertSplit( sal_Int32 nPos )
269 {
270     if( ImplInsertSplit( nPos ) )
271     {
272         DisableRepaint();
273         Execute( CSVCMD_EXPORTCOLUMNTYPE );
274         Execute( CSVCMD_UPDATECELLTEXTS );
275         sal_uInt32 nColIx = GetColumnFromPos( nPos );
276         ImplDrawColumn( nColIx - 1 );
277         ImplDrawColumn( nColIx );
278         ValidateGfx();  // performance: do not redraw all columns
279         EnableRepaint();
280     }
281 }
282 
283 void ScCsvGrid::RemoveSplit( sal_Int32 nPos )
284 {
285     if( ImplRemoveSplit( nPos ) )
286     {
287         DisableRepaint();
288         Execute( CSVCMD_EXPORTCOLUMNTYPE );
289         Execute( CSVCMD_UPDATECELLTEXTS );
290         ImplDrawColumn( GetColumnFromPos( nPos ) );
291         ValidateGfx();  // performance: do not redraw all columns
292         EnableRepaint();
293     }
294 }
295 
296 void ScCsvGrid::MoveSplit( sal_Int32 nPos, sal_Int32 nNewPos )
297 {
298     sal_uInt32 nColIx = GetColumnFromPos( nPos );
299     if( nColIx != CSV_COLUMN_INVALID )
300     {
301         DisableRepaint();
302         if( (GetColumnPos( nColIx - 1 ) < nNewPos) && (nNewPos < GetColumnPos( nColIx + 1 )) )
303         {
304             // move a split in the range between 2 others -> keep selection state of both columns
305             maSplits.Remove( nPos );
306             maSplits.Insert( nNewPos );
307             Execute( CSVCMD_UPDATECELLTEXTS );
308             ImplDrawColumn( nColIx - 1 );
309             ImplDrawColumn( nColIx );
310             ValidateGfx();  // performance: do not redraw all columns
311             AccSendTableUpdateEvent( nColIx - 1, nColIx );
312         }
313         else
314         {
315             ImplRemoveSplit( nPos );
316             ImplInsertSplit( nNewPos );
317             Execute( CSVCMD_EXPORTCOLUMNTYPE );
318             Execute( CSVCMD_UPDATECELLTEXTS );
319         }
320         EnableRepaint();
321     }
322 }
323 
324 void ScCsvGrid::RemoveAllSplits()
325 {
326     DisableRepaint();
327     ImplClearSplits();
328     Execute( CSVCMD_EXPORTCOLUMNTYPE );
329     Execute( CSVCMD_UPDATECELLTEXTS );
330     EnableRepaint();
331 }
332 
333 void ScCsvGrid::SetSplits( const ScCsvSplits& rSplits )
334 {
335     DisableRepaint();
336     ImplClearSplits();
337     sal_uInt32 nCount = rSplits.Count();
338     for( sal_uInt32 nIx = 0; nIx < nCount; ++nIx )
339         maSplits.Insert( rSplits[ nIx ] );
340     maColStates.clear();
341     maColStates.resize( maSplits.Count() - 1 );
342     Execute( CSVCMD_EXPORTCOLUMNTYPE );
343     Execute( CSVCMD_UPDATECELLTEXTS );
344     EnableRepaint();
345 }
346 
347 bool ScCsvGrid::ImplInsertSplit( sal_Int32 nPos )
348 {
349     sal_uInt32 nColIx = GetColumnFromPos( nPos );
350     bool bRet = (nColIx < GetColumnCount()) && maSplits.Insert( nPos );
351     if( bRet )
352     {
353         ScCsvColState aState( GetColumnType( nColIx ) );
354         aState.Select( IsSelected( nColIx ) && IsSelected( nColIx + 1 ) );
355         maColStates.insert( maColStates.begin() + nColIx + 1, aState );
356         AccSendInsertColumnEvent( nColIx + 1, nColIx + 1 );
357         AccSendTableUpdateEvent( nColIx, nColIx );
358     }
359     return bRet;
360 }
361 
362 bool ScCsvGrid::ImplRemoveSplit( sal_Int32 nPos )
363 {
364     bool bRet = maSplits.Remove( nPos );
365     if( bRet )
366     {
367         sal_uInt32 nColIx = GetColumnFromPos( nPos );
368         bool bSel = IsSelected( nColIx ) || IsSelected( nColIx + 1 );
369         maColStates.erase( maColStates.begin() + nColIx + 1 );
370         maColStates[ nColIx ].Select( bSel );
371         AccSendRemoveColumnEvent( nColIx + 1, nColIx + 1 );
372         AccSendTableUpdateEvent( nColIx, nColIx );
373     }
374     return bRet;
375 }
376 
377 void ScCsvGrid::ImplClearSplits()
378 {
379     sal_uInt32 nColumns = GetColumnCount();
380     maSplits.Clear();
381     maSplits.Insert( 0 );
382     maSplits.Insert( GetPosCount() );
383     maColStates.resize( 1 );
384     InvalidateGfx();
385     AccSendRemoveColumnEvent( 1, nColumns - 1 );
386 }
387 
388 // columns/column types -------------------------------------------------------
389 
390 sal_uInt32 ScCsvGrid::GetFirstVisColumn() const
391 {
392     return GetColumnFromPos( GetFirstVisPos() );
393 }
394 
395 sal_uInt32 ScCsvGrid::GetLastVisColumn() const
396 {
397     return GetColumnFromPos( Min( GetLastVisPos(), GetPosCount() ) - 1 );
398 }
399 
400 bool ScCsvGrid::IsValidColumn( sal_uInt32 nColIndex ) const
401 {
402     return nColIndex < GetColumnCount();
403 }
404 
405 bool ScCsvGrid::IsVisibleColumn( sal_uInt32 nColIndex ) const
406 {
407     return  IsValidColumn( nColIndex ) &&
408             (GetColumnPos( nColIndex ) < GetLastVisPos()) &&
409             (GetFirstVisPos() < GetColumnPos( nColIndex + 1 ));
410 }
411 
412 sal_Int32 ScCsvGrid::GetColumnX( sal_uInt32 nColIndex ) const
413 {
414     return GetX( GetColumnPos( nColIndex ) );
415 }
416 
417 sal_uInt32 ScCsvGrid::GetColumnFromX( sal_Int32 nX ) const
418 {
419     sal_Int32 nPos = (nX - GetFirstX()) / GetCharWidth() + GetFirstVisPos();
420     return ((GetFirstVisPos() <= nPos) && (nPos <= GetLastVisPos())) ?
421         GetColumnFromPos( nPos ) : CSV_COLUMN_INVALID;
422 }
423 
424 sal_uInt32 ScCsvGrid::GetColumnFromPos( sal_Int32 nPos ) const
425 {
426     return maSplits.UpperBound( nPos );
427 }
428 
429 sal_Int32 ScCsvGrid::GetColumnWidth( sal_uInt32 nColIndex ) const
430 {
431     return IsValidColumn( nColIndex ) ? (GetColumnPos( nColIndex + 1 ) - GetColumnPos( nColIndex )) : 0;
432 }
433 
434 void ScCsvGrid::SetColumnStates( const ScCsvColStateVec& rStates )
435 {
436     maColStates = rStates;
437     maColStates.resize( maSplits.Count() - 1 );
438     Execute( CSVCMD_EXPORTCOLUMNTYPE );
439     AccSendTableUpdateEvent( 0, GetColumnCount(), false );
440     AccSendSelectionEvent();
441 }
442 
443 sal_Int32 ScCsvGrid::GetColumnType( sal_uInt32 nColIndex ) const
444 {
445     return IsValidColumn( nColIndex ) ? maColStates[ nColIndex ].mnType : CSV_TYPE_NOSELECTION;
446 }
447 
448 void ScCsvGrid::SetColumnType( sal_uInt32 nColIndex, sal_Int32 nColType )
449 {
450     if( IsValidColumn( nColIndex ) )
451     {
452         maColStates[ nColIndex ].mnType = nColType;
453         AccSendTableUpdateEvent( nColIndex, nColIndex, false );
454     }
455 }
456 
457 sal_Int32 ScCsvGrid::GetSelColumnType() const
458 {
459     sal_uInt32 nColIx = GetFirstSelected();
460     if( nColIx == CSV_COLUMN_INVALID )
461         return CSV_TYPE_NOSELECTION;
462 
463     sal_Int32 nType = GetColumnType( nColIx );
464     while( (nColIx != CSV_COLUMN_INVALID) && (nType != CSV_TYPE_MULTI) )
465     {
466         if( nType != GetColumnType( nColIx ) )
467             nType = CSV_TYPE_MULTI;
468         nColIx = GetNextSelected( nColIx );
469     }
470     return nType;
471 }
472 
473 void ScCsvGrid::SetSelColumnType( sal_Int32 nType )
474 {
475     if( (nType != CSV_TYPE_MULTI) && (nType != CSV_TYPE_NOSELECTION) )
476     {
477         for( sal_uInt32 nColIx = GetFirstSelected(); nColIx != CSV_COLUMN_INVALID; nColIx = GetNextSelected( nColIx ) )
478             SetColumnType( nColIx, nType );
479         Repaint( true );
480         Execute( CSVCMD_EXPORTCOLUMNTYPE );
481     }
482 }
483 
484 void ScCsvGrid::SetTypeNames( const StringVec& rTypeNames )
485 {
486     DBG_ASSERT( !rTypeNames.empty(), "ScCsvGrid::SetTypeNames - vector is empty" );
487     maTypeNames = rTypeNames;
488     Repaint( true );
489 
490     maPopup.Clear();
491     sal_uInt32 nCount = maTypeNames.size();
492     sal_uInt32 nIx;
493     sal_uInt16 nItemId;
494     for( nIx = 0, nItemId = 1; nIx < nCount; ++nIx, ++nItemId )
495         maPopup.InsertItem( nItemId, maTypeNames[ nIx ] );
496 
497     ::std::for_each( maColStates.begin(), maColStates.end(), Func_SetType( CSV_TYPE_DEFAULT ) );
498 }
499 
500 const String& ScCsvGrid::GetColumnTypeName( sal_uInt32 nColIndex ) const
501 {
502     sal_uInt32 nTypeIx = static_cast< sal_uInt32 >( GetColumnType( nColIndex ) );
503     return (nTypeIx < maTypeNames.size()) ? maTypeNames[ nTypeIx ] : EMPTY_STRING;
504 }
505 
506 sal_uInt8 lcl_GetExtColumnType( sal_Int32 nIntType )
507 {
508     static sal_uInt8 pExtTypes[] =
509         { SC_COL_STANDARD, SC_COL_TEXT, SC_COL_DMY, SC_COL_MDY, SC_COL_YMD, SC_COL_ENGLISH, SC_COL_SKIP };
510     static sal_Int32 nExtTypeCount = sizeof( pExtTypes ) / sizeof( *pExtTypes );
511     return pExtTypes[ ((0 <= nIntType) && (nIntType < nExtTypeCount)) ? nIntType : 0 ];
512 }
513 
514 void ScCsvGrid::FillColumnDataSep( ScAsciiOptions& rOptions ) const
515 {
516     sal_uInt32 nCount = GetColumnCount();
517     ScCsvExpDataVec aDataVec;
518 
519     for( sal_uInt32 nColIx = 0; nColIx < nCount; ++nColIx )
520     {
521         if( GetColumnType( nColIx ) != CSV_TYPE_DEFAULT )
522             // 1-based column index
523             aDataVec.push_back( ScCsvExpData(
524                 static_cast< xub_StrLen >( nColIx + 1 ),
525                 lcl_GetExtColumnType( GetColumnType( nColIx ) ) ) );
526     }
527     rOptions.SetColumnInfo( aDataVec );
528 }
529 
530 void ScCsvGrid::FillColumnDataFix( ScAsciiOptions& rOptions ) const
531 {
532     sal_uInt32 nCount = Min( GetColumnCount(), static_cast<sal_uInt32>(MAXCOLCOUNT) );
533     ScCsvExpDataVec aDataVec( nCount + 1 );
534 
535     for( sal_uInt32 nColIx = 0; nColIx < nCount; ++nColIx )
536     {
537         ScCsvExpData& rData = aDataVec[ nColIx ];
538         rData.mnIndex = static_cast< xub_StrLen >(
539             Min( static_cast< sal_Int32 >( STRING_MAXLEN ), GetColumnPos( nColIx ) ) );
540         rData.mnType = lcl_GetExtColumnType( GetColumnType( nColIx ) );
541     }
542     aDataVec[ nCount ].mnIndex = STRING_MAXLEN;
543     aDataVec[ nCount ].mnType = SC_COL_SKIP;
544     rOptions.SetColumnInfo( aDataVec );
545 }
546 
547 void ScCsvGrid::ScrollVertRel( ScMoveMode eDir )
548 {
549     sal_Int32 nLine = GetFirstVisLine();
550     switch( eDir )
551     {
552         case MOVE_PREV:     --nLine;                        break;
553         case MOVE_NEXT:     ++nLine;                        break;
554         case MOVE_FIRST:    nLine = 0;                      break;
555         case MOVE_LAST:     nLine = GetMaxLineOffset();     break;
556         case MOVE_PREVPAGE: nLine -= GetVisLineCount() - 2; break;
557         case MOVE_NEXTPAGE: nLine += GetVisLineCount() - 2; break;
558         default:
559         {
560             // added to avoid warnings
561         }
562     }
563     Execute( CSVCMD_SETLINEOFFSET, nLine );
564 }
565 
566 void ScCsvGrid::ExecutePopup( const Point& rPos )
567 {
568     sal_uInt16 nItemId = maPopup.Execute( this, rPos );
569     if( nItemId )   // 0 = cancelled
570         Execute( CSVCMD_SETCOLUMNTYPE, maPopup.GetItemPos( nItemId ) );
571 }
572 
573 
574 // selection handling ---------------------------------------------------------
575 
576 bool ScCsvGrid::IsSelected( sal_uInt32 nColIndex ) const
577 {
578     return IsValidColumn( nColIndex ) && maColStates[ nColIndex ].IsSelected();
579 }
580 
581 sal_uInt32 ScCsvGrid::GetFirstSelected() const
582 {
583     return IsSelected( 0 ) ? 0 : GetNextSelected( 0 );
584 }
585 
586 sal_uInt32 ScCsvGrid::GetNextSelected( sal_uInt32 nFromIndex ) const
587 {
588     sal_uInt32 nColCount = GetColumnCount();
589     for( sal_uInt32 nColIx = nFromIndex + 1; nColIx < nColCount; ++nColIx )
590         if( IsSelected( nColIx ) )
591             return nColIx;
592     return CSV_COLUMN_INVALID;
593 }
594 
595 void ScCsvGrid::Select( sal_uInt32 nColIndex, bool bSelect )
596 {
597     if( IsValidColumn( nColIndex ) )
598     {
599         maColStates[ nColIndex ].Select( bSelect );
600         ImplDrawColumnSelection( nColIndex );
601         Repaint();
602         Execute( CSVCMD_EXPORTCOLUMNTYPE );
603         if( bSelect )
604             mnRecentSelCol = nColIndex;
605         AccSendSelectionEvent();
606     }
607 }
608 
609 void ScCsvGrid::ToggleSelect( sal_uInt32 nColIndex )
610 {
611     Select( nColIndex, !IsSelected( nColIndex ) );
612 }
613 
614 void ScCsvGrid::SelectRange( sal_uInt32 nColIndex1, sal_uInt32 nColIndex2, bool bSelect )
615 {
616     if( nColIndex1 == CSV_COLUMN_INVALID )
617         Select( nColIndex2 );
618     else if( nColIndex2 == CSV_COLUMN_INVALID )
619         Select( nColIndex1 );
620     else if( nColIndex1 > nColIndex2 )
621     {
622         SelectRange( nColIndex2, nColIndex1, bSelect );
623         if( bSelect )
624             mnRecentSelCol = nColIndex1;
625     }
626     else if( IsValidColumn( nColIndex1 ) && IsValidColumn( nColIndex2 ) )
627     {
628         for( sal_uInt32 nColIx = nColIndex1; nColIx <= nColIndex2; ++nColIx )
629         {
630             maColStates[ nColIx ].Select( bSelect );
631             ImplDrawColumnSelection( nColIx );
632         }
633         Repaint();
634         Execute( CSVCMD_EXPORTCOLUMNTYPE );
635         if( bSelect )
636             mnRecentSelCol = nColIndex1;
637         AccSendSelectionEvent();
638     }
639 }
640 
641 void ScCsvGrid::SelectAll( bool bSelect )
642 {
643     SelectRange( 0, GetColumnCount() - 1, bSelect );
644 }
645 
646 void ScCsvGrid::MoveCursor( sal_uInt32 nColIndex )
647 {
648     DisableRepaint();
649     if( IsValidColumn( nColIndex ) )
650     {
651         sal_Int32 nPosBeg = GetColumnPos( nColIndex );
652         sal_Int32 nPosEnd = GetColumnPos( nColIndex + 1 );
653         sal_Int32 nMinPos = Max( nPosBeg - CSV_SCROLL_DIST, sal_Int32( 0 ) );
654         sal_Int32 nMaxPos = Min( nPosEnd - GetVisPosCount() + CSV_SCROLL_DIST + sal_Int32( 1 ), nMinPos );
655         if( nPosBeg - CSV_SCROLL_DIST + 1 <= GetFirstVisPos() )
656             Execute( CSVCMD_SETPOSOFFSET, nMinPos );
657         else if( nPosEnd + CSV_SCROLL_DIST >= GetLastVisPos() )
658             Execute( CSVCMD_SETPOSOFFSET, nMaxPos );
659     }
660     Execute( CSVCMD_MOVEGRIDCURSOR, GetColumnPos( nColIndex ) );
661     EnableRepaint();
662 }
663 
664 void ScCsvGrid::MoveCursorRel( ScMoveMode eDir )
665 {
666     if( GetFocusColumn() != CSV_COLUMN_INVALID )
667     {
668         switch( eDir )
669         {
670             case MOVE_FIRST:
671                 MoveCursor( 0 );
672             break;
673             case MOVE_LAST:
674                 MoveCursor( GetColumnCount() - 1 );
675             break;
676             case MOVE_PREV:
677                 if( GetFocusColumn() > 0 )
678                     MoveCursor( GetFocusColumn() - 1 );
679             break;
680             case MOVE_NEXT:
681                 if( GetFocusColumn() < GetColumnCount() - 1 )
682                     MoveCursor( GetFocusColumn() + 1 );
683             break;
684             default:
685             {
686                 // added to avoid warnings
687             }
688         }
689     }
690 }
691 
692 void ScCsvGrid::ImplClearSelection()
693 {
694     ::std::for_each( maColStates.begin(), maColStates.end(), Func_Select( false ) );
695     ImplDrawGridDev();
696 }
697 
698 void ScCsvGrid::DoSelectAction( sal_uInt32 nColIndex, sal_uInt16 nModifier )
699 {
700     if( !(nModifier & KEY_MOD1) )
701         ImplClearSelection();
702     if( nModifier & KEY_SHIFT )             // SHIFT always expands
703         SelectRange( mnRecentSelCol, nColIndex );
704     else if( !(nModifier & KEY_MOD1) )      // no SHIFT/CTRL always selects 1 column
705         Select( nColIndex );
706     else if( IsTracking() )                 // CTRL in tracking does not toggle
707         Select( nColIndex, mbMTSelecting );
708     else                                    // CTRL only toggles
709         ToggleSelect( nColIndex );
710     Execute( CSVCMD_MOVEGRIDCURSOR, GetColumnPos( nColIndex ) );
711 }
712 
713 
714 // cell contents --------------------------------------------------------------
715 
716 void ScCsvGrid::ImplSetTextLineSep(
717         sal_Int32 nLine, const String& rTextLine,
718         const String& rSepChars, sal_Unicode cTextSep, bool bMergeSep )
719 {
720     if( nLine < GetFirstVisLine() ) return;
721 
722     sal_uInt32 nLineIx = nLine - GetFirstVisLine();
723     while( maTexts.size() <= nLineIx )
724         maTexts.push_back( StringVec() );
725     StringVec& rStrVec = maTexts[ nLineIx ];
726     rStrVec.clear();
727 
728     // scan for separators
729     String aCellText;
730     const sal_Unicode* pSepChars = rSepChars.GetBuffer();
731     const sal_Unicode* pChar = rTextLine.GetBuffer();
732     sal_uInt32 nColIx = 0;
733 
734     while( *pChar && (nColIx < sal::static_int_cast<sal_uInt32>(CSV_MAXCOLCOUNT)) )
735     {
736         // scan for next cell text
737         bool bIsQuoted = false;
738         pChar = ScImportExport::ScanNextFieldFromString( pChar, aCellText, cTextSep, pSepChars, bMergeSep, bIsQuoted );
739 
740         // update column width
741         sal_Int32 nWidth = Max( CSV_MINCOLWIDTH, aCellText.Len() + sal_Int32( 1 ) );
742         if( IsValidColumn( nColIx ) )
743         {
744             // expand existing column
745             sal_Int32 nDiff = nWidth - GetColumnWidth( nColIx );
746             if( nDiff > 0 )
747             {
748                 Execute( CSVCMD_SETPOSCOUNT, GetPosCount() + nDiff );
749                 for( sal_uInt32 nSplitIx = GetColumnCount() - 1; nSplitIx > nColIx; --nSplitIx )
750                 {
751                     sal_Int32 nPos = maSplits[ nSplitIx ];
752                     maSplits.Remove( nPos );
753                     maSplits.Insert( nPos + nDiff );
754                 }
755             }
756         }
757         else
758         {
759             // append new column
760             sal_Int32 nLastPos = GetPosCount();
761             Execute( CSVCMD_SETPOSCOUNT, nLastPos + nWidth );
762             ImplInsertSplit( nLastPos );
763         }
764 
765         if( aCellText.Len() <= CSV_MAXSTRLEN )
766             rStrVec.push_back( aCellText );
767         else
768             rStrVec.push_back( aCellText.Copy( 0, CSV_MAXSTRLEN ) );
769         ++nColIx;
770     }
771     InvalidateGfx();
772 }
773 
774 void ScCsvGrid::ImplSetTextLineFix( sal_Int32 nLine, const String& rTextLine )
775 {
776     if( nLine < GetFirstVisLine() ) return;
777 
778     sal_Int32 nChars = rTextLine.Len();
779     if( nChars > GetPosCount() )
780         Execute( CSVCMD_SETPOSCOUNT, nChars );
781 
782     sal_uInt32 nLineIx = nLine - GetFirstVisLine();
783     while( maTexts.size() <= nLineIx )
784         maTexts.push_back( StringVec() );
785 
786     StringVec& rStrVec = maTexts[ nLineIx ];
787     rStrVec.clear();
788     sal_uInt32 nColCount = GetColumnCount();
789     xub_StrLen nStrLen = rTextLine.Len();
790     xub_StrLen nStrIx = 0;
791     for( sal_uInt32 nColIx = 0; (nColIx < nColCount) && (nStrIx < nStrLen); ++nColIx )
792     {
793         xub_StrLen nColWidth = static_cast< xub_StrLen >( GetColumnWidth( nColIx ) );
794         rStrVec.push_back( rTextLine.Copy( nStrIx, Max( nColWidth, CSV_MAXSTRLEN ) ) );
795         nStrIx = sal::static_int_cast<xub_StrLen>( nStrIx + nColWidth );
796     }
797     InvalidateGfx();
798 }
799 
800 const String& ScCsvGrid::GetCellText( sal_uInt32 nColIndex, sal_Int32 nLine ) const
801 {
802     if( nLine < GetFirstVisLine() ) return EMPTY_STRING;
803 
804     sal_uInt32 nLineIx = nLine - GetFirstVisLine();
805     if( nLineIx >= maTexts.size() ) return EMPTY_STRING;
806 
807     const StringVec& rStrVec = maTexts[ nLineIx ];
808     if( nColIndex >= rStrVec.size() ) return EMPTY_STRING;
809 
810     return rStrVec[ nColIndex ];
811 }
812 
813 
814 // event handling -------------------------------------------------------------
815 
816 void ScCsvGrid::Resize()
817 {
818     ScCsvControl::Resize();
819     InitSizeData();
820     Execute( CSVCMD_UPDATECELLTEXTS );
821 }
822 
823 void ScCsvGrid::GetFocus()
824 {
825     ScCsvControl::GetFocus();
826     Execute( CSVCMD_MOVEGRIDCURSOR, GetNoScrollCol( GetGridCursorPos() ) );
827     Repaint();
828 }
829 
830 void ScCsvGrid::LoseFocus()
831 {
832     ScCsvControl::LoseFocus();
833     Repaint();
834 }
835 
836 void ScCsvGrid::MouseButtonDown( const MouseEvent& rMEvt )
837 {
838     DisableRepaint();
839     if( !HasFocus() )
840         GrabFocus();
841 
842     Point aPos( rMEvt.GetPosPixel() );
843     sal_uInt32 nColIx = GetColumnFromX( aPos.X() );
844 
845     if( rMEvt.IsLeft() )
846     {
847         if( (GetFirstX() > aPos.X()) || (aPos.X() > GetLastX()) )   // in header column
848         {
849             if( aPos.Y() <= GetHdrHeight() )
850                 SelectAll();
851         }
852         else if( IsValidColumn( nColIx ) )
853         {
854             DoSelectAction( nColIx, rMEvt.GetModifier() );
855             mnMTCurrCol = nColIx;
856             mbMTSelecting = IsSelected( nColIx );
857             StartTracking( STARTTRACK_BUTTONREPEAT );
858         }
859     }
860     EnableRepaint();
861 }
862 
863 void ScCsvGrid::Tracking( const TrackingEvent& rTEvt )
864 {
865     if( rTEvt.IsTrackingEnded() || rTEvt.IsTrackingRepeat() )
866     {
867         DisableRepaint();
868         const MouseEvent& rMEvt = rTEvt.GetMouseEvent();
869 
870         sal_Int32 nPos = (rMEvt.GetPosPixel().X() - GetFirstX()) / GetCharWidth() + GetFirstVisPos();
871         // on mouse tracking: keep position valid
872         nPos = Max( Min( nPos, GetPosCount() - sal_Int32( 1 ) ), sal_Int32( 0 ) );
873         Execute( CSVCMD_MAKEPOSVISIBLE, nPos );
874 
875         sal_uInt32 nColIx = GetColumnFromPos( nPos );
876         if( mnMTCurrCol != nColIx )
877         {
878             DoSelectAction( nColIx, rMEvt.GetModifier() );
879             mnMTCurrCol = nColIx;
880         }
881         EnableRepaint();
882     }
883 }
884 
885 void ScCsvGrid::KeyInput( const KeyEvent& rKEvt )
886 {
887     const KeyCode& rKCode = rKEvt.GetKeyCode();
888     sal_uInt16 nCode = rKCode.GetCode();
889     bool bShift = rKCode.IsShift() == sal_True;
890     bool bMod1 = rKCode.IsMod1() == sal_True;
891 
892     if( !rKCode.IsMod2() )
893     {
894         ScMoveMode eHDir = GetHorzDirection( nCode, !bMod1 );
895         ScMoveMode eVDir = GetVertDirection( nCode, bMod1 );
896 
897         if( eHDir != MOVE_NONE )
898         {
899             DisableRepaint();
900             MoveCursorRel( eHDir );
901             if( !bMod1 )
902                 ImplClearSelection();
903             if( bShift )
904                 SelectRange( mnRecentSelCol, GetFocusColumn() );
905             else if( !bMod1 )
906                 Select( GetFocusColumn() );
907             EnableRepaint();
908         }
909         else if( eVDir != MOVE_NONE )
910             ScrollVertRel( eVDir );
911         else if( nCode == KEY_SPACE )
912         {
913             if( !bMod1 )
914                 ImplClearSelection();
915             if( bShift )
916                 SelectRange( mnRecentSelCol, GetFocusColumn() );
917             else if( bMod1 )
918                 ToggleSelect( GetFocusColumn() );
919             else
920                 Select( GetFocusColumn() );
921         }
922         else if( !bShift && bMod1 )
923         {
924             if( nCode == KEY_A )
925                 SelectAll();
926             else if( (KEY_1 <= nCode) && (nCode <= KEY_9) )
927             {
928                 sal_uInt32 nType = nCode - KEY_1;
929                 if( nType < maTypeNames.size() )
930                     Execute( CSVCMD_SETCOLUMNTYPE, nType );
931             }
932         }
933     }
934 
935     if( rKCode.GetGroup() != KEYGROUP_CURSOR )
936         ScCsvControl::KeyInput( rKEvt );
937 }
938 
939 void ScCsvGrid::Command( const CommandEvent& rCEvt )
940 {
941     switch( rCEvt.GetCommand() )
942     {
943         case COMMAND_CONTEXTMENU:
944         {
945             if( rCEvt.IsMouseEvent() )
946             {
947                 Point aPos( rCEvt.GetMousePosPixel() );
948                 sal_uInt32 nColIx = GetColumnFromX( aPos.X() );
949                 if( IsValidColumn( nColIx ) && (GetFirstX() <= aPos.X()) && (aPos.X() <= GetLastX()) )
950                 {
951                     if( !IsSelected( nColIx ) )
952                         DoSelectAction( nColIx, 0 );    // focus & select
953                     ExecutePopup( aPos );
954                 }
955             }
956             else
957             {
958                 sal_uInt32 nColIx = GetFocusColumn();
959                 if( !IsSelected( nColIx ) )
960                     Select( nColIx );
961                 sal_Int32 nX1 = Max( GetColumnX( nColIx ), GetFirstX() );
962                 sal_Int32 nX2 = Min( GetColumnX( nColIx + 1 ), GetWidth() );
963                 ExecutePopup( Point( (nX1 + nX2) / 2, GetHeight() / 2 ) );
964             }
965         }
966         break;
967         case COMMAND_WHEEL:
968         {
969             Point aPoint;
970             Rectangle aRect( aPoint, maWinSize );
971             if( aRect.IsInside( rCEvt.GetMousePosPixel() ) )
972             {
973                 const CommandWheelData* pData = rCEvt.GetWheelData();
974                 if( pData && (pData->GetMode() == COMMAND_WHEEL_SCROLL) && !pData->IsHorz() )
975                     Execute( CSVCMD_SETLINEOFFSET, GetFirstVisLine() - pData->GetNotchDelta() );
976             }
977         }
978         break;
979         default:
980             ScCsvControl::Command( rCEvt );
981     }
982 }
983 
984 void ScCsvGrid::DataChanged( const DataChangedEvent& rDCEvt )
985 {
986     if( (rDCEvt.GetType() == DATACHANGED_SETTINGS) && (rDCEvt.GetFlags() & SETTINGS_STYLE) )
987     {
988         InitColors();
989         InitFonts();
990         UpdateLayoutData();
991         Execute( CSVCMD_UPDATECELLTEXTS );
992     }
993     ScCsvControl::DataChanged( rDCEvt );
994 }
995 
996 void ScCsvGrid::ConfigurationChanged( utl::ConfigurationBroadcaster*, sal_uInt32 )
997 {
998     InitColors();
999     Repaint();
1000 }
1001 
1002 
1003 // painting -------------------------------------------------------------------
1004 
1005 void ScCsvGrid::Paint( const Rectangle& )
1006 {
1007     Repaint();
1008 }
1009 
1010 void ScCsvGrid::ImplRedraw()
1011 {
1012     if( IsVisible() )
1013     {
1014         if( !IsValidGfx() )
1015         {
1016             ValidateGfx();
1017             ImplDrawBackgrDev();
1018             ImplDrawGridDev();
1019         }
1020         DrawOutDev( Point(), maWinSize, Point(), maWinSize, maGridDev );
1021         ImplDrawTrackingRect( GetFocusColumn() );
1022     }
1023 }
1024 
1025 EditEngine* ScCsvGrid::GetEditEngine()
1026 {
1027     return mpEditEngine.get();
1028 }
1029 
1030 void ScCsvGrid::ImplSetColumnClipRegion( OutputDevice& rOutDev, sal_uInt32 nColIndex )
1031 {
1032     rOutDev.SetClipRegion( Region( Rectangle(
1033         Max( GetColumnX( nColIndex ), GetFirstX() ) + 1, 0,
1034         Min( GetColumnX( nColIndex + 1 ), GetLastX() ), GetHeight() - 1 ) ) );
1035 }
1036 
1037 void ScCsvGrid::ImplDrawColumnHeader( OutputDevice& rOutDev, sal_uInt32 nColIndex, Color aFillColor )
1038 {
1039     sal_Int32 nX1 = GetColumnX( nColIndex ) + 1;
1040     sal_Int32 nX2 = GetColumnX( nColIndex + 1 );
1041     sal_Int32 nHdrHt = GetHdrHeight();
1042 
1043     rOutDev.SetLineColor();
1044     rOutDev.SetFillColor( aFillColor );
1045     rOutDev.DrawRect( Rectangle( nX1, 0, nX2, nHdrHt ) );
1046 
1047     rOutDev.SetFont( maHeaderFont );
1048     rOutDev.SetTextColor( maHeaderTextColor );
1049     rOutDev.SetTextFillColor();
1050     rOutDev.DrawText( Point( nX1 + 1, 0 ), GetColumnTypeName( nColIndex ) );
1051 
1052     rOutDev.SetLineColor( maHeaderGridColor );
1053     rOutDev.DrawLine( Point( nX1, nHdrHt ), Point( nX2, nHdrHt ) );
1054     rOutDev.DrawLine( Point( nX2, 0 ), Point( nX2, nHdrHt ) );
1055 }
1056 
1057 void ScCsvGrid::ImplDrawCellText( const Point& rPos, const String& rText )
1058 {
1059     String aPlainText( rText );
1060     aPlainText.SearchAndReplaceAll( '\t', ' ' );
1061     aPlainText.SearchAndReplaceAll( '\n', ' ' );
1062     mpEditEngine->SetPaperSize( maEdEngSize );
1063 
1064     /*  #i60296# If string contains mixed script types, the space character
1065         U+0020 may be drawn with a wrong width (from non-fixed-width Asian or
1066         Complex font). Now we draw every non-space portion separately. */
1067     xub_StrLen nTokenCount = aPlainText.GetTokenCount( ' ' );
1068     xub_StrLen nCharIx = 0;
1069     for( xub_StrLen nToken = 0; nToken < nTokenCount; ++nToken )
1070     {
1071         xub_StrLen nBeginIx = nCharIx;
1072         String aToken = aPlainText.GetToken( 0, ' ', nCharIx );
1073         if( aToken.Len() > 0 )
1074         {
1075             sal_Int32 nX = rPos.X() + GetCharWidth() * nBeginIx;
1076             mpEditEngine->SetText( aToken );
1077             mpEditEngine->Draw( &maBackgrDev, Point( nX, rPos.Y() ) );
1078         }
1079     }
1080 
1081     nCharIx = 0;
1082     while( (nCharIx = rText.Search( '\t', nCharIx )) != STRING_NOTFOUND )
1083     {
1084         sal_Int32 nX1 = rPos.X() + GetCharWidth() * nCharIx;
1085         sal_Int32 nX2 = nX1 + GetCharWidth() - 2;
1086         sal_Int32 nY = rPos.Y() + GetLineHeight() / 2;
1087         Color aColor( maTextColor );
1088         maBackgrDev.SetLineColor( aColor );
1089         maBackgrDev.DrawLine( Point( nX1, nY ), Point( nX2, nY ) );
1090         maBackgrDev.DrawLine( Point( nX2 - 2, nY - 2 ), Point( nX2, nY ) );
1091         maBackgrDev.DrawLine( Point( nX2 - 2, nY + 2 ), Point( nX2, nY ) );
1092         ++nCharIx;
1093     }
1094     nCharIx = 0;
1095     while( (nCharIx = rText.Search( '\n', nCharIx )) != STRING_NOTFOUND )
1096     {
1097         sal_Int32 nX1 = rPos.X() + GetCharWidth() * nCharIx;
1098         sal_Int32 nX2 = nX1 + GetCharWidth() - 2;
1099         sal_Int32 nY = rPos.Y() + GetLineHeight() / 2;
1100         Color aColor( maTextColor );
1101         maBackgrDev.SetLineColor( aColor );
1102         maBackgrDev.DrawLine( Point( nX1, nY ), Point( nX2, nY ) );
1103         maBackgrDev.DrawLine( Point( nX1 + 2, nY - 2 ), Point( nX1, nY ) );
1104         maBackgrDev.DrawLine( Point( nX1 + 2, nY + 2 ), Point( nX1, nY ) );
1105         maBackgrDev.DrawLine( Point( nX2, nY - 2 ), Point( nX2, nY ) );
1106         ++nCharIx;
1107     }
1108 }
1109 
1110 void ScCsvGrid::ImplDrawFirstLineSep( bool bSet )
1111 {
1112     if( IsVisibleLine( mnFirstImpLine ) && (mnFirstImpLine != GetFirstVisLine() ) )
1113     {
1114         sal_Int32 nY = GetY( mnFirstImpLine );
1115         sal_Int32 nX = Min( GetColumnX( GetLastVisColumn() + 1 ), GetLastX() );
1116         maBackgrDev.SetLineColor( bSet ? maGridPBColor : maGridColor );
1117         maBackgrDev.DrawLine( Point( GetFirstX() + 1, nY ), Point( nX, nY ) );
1118     }
1119 }
1120 
1121 void ScCsvGrid::ImplDrawColumnBackgr( sal_uInt32 nColIndex )
1122 {
1123     if( !IsVisibleColumn( nColIndex ) )
1124         return;
1125 
1126     ImplSetColumnClipRegion( maBackgrDev, nColIndex );
1127 
1128     // grid
1129     maBackgrDev.SetLineColor();
1130     maBackgrDev.SetFillColor( maBackColor );
1131     sal_Int32 nX1 = GetColumnX( nColIndex ) + 1;
1132     sal_Int32 nX2 = GetColumnX( nColIndex + 1 );
1133     sal_Int32 nY2 = GetY( GetLastVisLine() + 1 );
1134     sal_Int32 nHdrHt = GetHdrHeight();
1135     Rectangle aRect( nX1, nHdrHt, nX2, nY2 );
1136     maBackgrDev.DrawRect( aRect );
1137     maBackgrDev.SetLineColor( maGridColor );
1138     maBackgrDev.DrawGrid( aRect, Size( 1, GetLineHeight() ), GRID_HORZLINES );
1139     maBackgrDev.DrawLine( Point( nX2, nHdrHt ), Point( nX2, nY2 ) );
1140     ImplDrawFirstLineSep( true );
1141 
1142     // cell texts
1143     mpEditEngine->SetDefaultItem( SvxColorItem( maTextColor, EE_CHAR_COLOR ) );
1144     size_t nLineCount = ::std::min( static_cast< size_t >( GetLastVisLine() - GetFirstVisLine() + 1 ), maTexts.size() );
1145     // #i67432# cut string to avoid edit engine performance problems with very large strings
1146     sal_Int32 nFirstVisPos = ::std::max( GetColumnPos( nColIndex ), GetFirstVisPos() );
1147     sal_Int32 nLastVisPos = ::std::min( GetColumnPos( nColIndex + 1 ), GetLastVisPos() );
1148     xub_StrLen nStrPos = static_cast< xub_StrLen >( nFirstVisPos - GetColumnPos( nColIndex ) );
1149     xub_StrLen nStrLen = static_cast< xub_StrLen >( nLastVisPos - nFirstVisPos + 1 );
1150     sal_Int32 nStrX = GetX( nFirstVisPos );
1151     for( size_t nLine = 0; nLine < nLineCount; ++nLine )
1152     {
1153         StringVec& rStrVec = maTexts[ nLine ];
1154         if( (nColIndex < rStrVec.size()) && (rStrVec[ nColIndex ].Len() > nStrPos) )
1155         {
1156             String aText( rStrVec[ nColIndex ], nStrPos, nStrLen );
1157             ImplDrawCellText( Point( nStrX, GetY( GetFirstVisLine() + nLine ) ), aText );
1158         }
1159     }
1160 
1161     // header
1162     ImplDrawColumnHeader( maBackgrDev, nColIndex, maHeaderBackColor );
1163 
1164     maBackgrDev.SetClipRegion();
1165 }
1166 
1167 void ScCsvGrid::ImplDrawRowHeaders()
1168 {
1169     maBackgrDev.SetLineColor();
1170     maBackgrDev.SetFillColor( maAppBackColor );
1171     Point aPoint( GetHdrX(), 0 );
1172     Rectangle aRect( aPoint, Size( GetHdrWidth() + 1, GetHeight() ) );
1173     maBackgrDev.DrawRect( aRect );
1174 
1175     maBackgrDev.SetFillColor( maHeaderBackColor );
1176     aRect.Bottom() = GetY( GetLastVisLine() + 1 );
1177     maBackgrDev.DrawRect( aRect );
1178 
1179     // line numbers
1180     maBackgrDev.SetFont( maHeaderFont );
1181     maBackgrDev.SetTextColor( maHeaderTextColor );
1182     maBackgrDev.SetTextFillColor();
1183     sal_Int32 nLastLine = GetLastVisLine();
1184     for( sal_Int32 nLine = GetFirstVisLine(); nLine <= nLastLine; ++nLine )
1185     {
1186         String aText( String::CreateFromInt32( nLine + 1 ) );
1187         sal_Int32 nX = GetHdrX() + (GetHdrWidth() - maBackgrDev.GetTextWidth( aText )) / 2;
1188         maBackgrDev.DrawText( Point( nX, GetY( nLine ) ), aText );
1189     }
1190 
1191     // grid
1192     maBackgrDev.SetLineColor( maHeaderGridColor );
1193     if( IsRTL() )
1194     {
1195         maBackgrDev.DrawLine( Point( 0, 0 ), Point( 0, GetHeight() - 1 ) );
1196         maBackgrDev.DrawLine( aRect.TopLeft(), aRect.BottomLeft() );
1197     }
1198     else
1199         maBackgrDev.DrawLine( aRect.TopRight(), aRect.BottomRight() );
1200     aRect.Top() = GetHdrHeight();
1201     maBackgrDev.DrawGrid( aRect, Size( 1, GetLineHeight() ), GRID_HORZLINES );
1202 }
1203 
1204 void ScCsvGrid::ImplDrawBackgrDev()
1205 {
1206     maBackgrDev.SetLineColor();
1207     maBackgrDev.SetFillColor( maAppBackColor );
1208     maBackgrDev.DrawRect( Rectangle(
1209         Point( GetFirstX() + 1, 0 ), Size( GetWidth() - GetHdrWidth(), GetHeight() ) ) );
1210 
1211     sal_uInt32 nLastCol = GetLastVisColumn();
1212     for( sal_uInt32 nColIx = GetFirstVisColumn(); nColIx <= nLastCol; ++nColIx )
1213         ImplDrawColumnBackgr( nColIx );
1214 
1215     ImplDrawRowHeaders();
1216 }
1217 
1218 void ScCsvGrid::ImplDrawColumnSelection( sal_uInt32 nColIndex )
1219 {
1220     ImplInvertCursor( GetRulerCursorPos() );
1221     ImplSetColumnClipRegion( maGridDev, nColIndex );
1222     maGridDev.DrawOutDev( Point(), maWinSize, Point(), maWinSize, maBackgrDev );
1223 
1224     if( IsSelected( nColIndex ) )
1225     {
1226         sal_Int32 nX1 = GetColumnX( nColIndex ) + 1;
1227         sal_Int32 nX2 = GetColumnX( nColIndex + 1 );
1228 
1229         // header
1230         Rectangle aRect( nX1, 0, nX2, GetHdrHeight() );
1231         maGridDev.SetLineColor();
1232         if( maHeaderBackColor.IsDark() )
1233             // redraw with light gray background in dark mode
1234             ImplDrawColumnHeader( maGridDev, nColIndex, COL_LIGHTGRAY );
1235         else
1236         {
1237             // use transparent active color
1238             maGridDev.SetFillColor( maSelectColor );
1239             maGridDev.DrawTransparent( PolyPolygon( Polygon( aRect ) ), CSV_HDR_TRANSPARENCY );
1240         }
1241 
1242         // column selection
1243         aRect = Rectangle( nX1, GetHdrHeight() + 1, nX2, GetY( GetLastVisLine() + 1 ) - 1 );
1244         ImplInvertRect( maGridDev, aRect );
1245     }
1246 
1247     maGridDev.SetClipRegion();
1248     ImplInvertCursor( GetRulerCursorPos() );
1249 }
1250 
1251 void ScCsvGrid::ImplDrawGridDev()
1252 {
1253     maGridDev.DrawOutDev( Point(), maWinSize, Point(), maWinSize, maBackgrDev );
1254     sal_uInt32 nLastCol = GetLastVisColumn();
1255     for( sal_uInt32 nColIx = GetFirstVisColumn(); nColIx <= nLastCol; ++nColIx )
1256         ImplDrawColumnSelection( nColIx );
1257 }
1258 
1259 void ScCsvGrid::ImplDrawColumn( sal_uInt32 nColIndex )
1260 {
1261     ImplDrawColumnBackgr( nColIndex );
1262     ImplDrawColumnSelection( nColIndex );
1263 }
1264 
1265 void ScCsvGrid::ImplDrawHorzScrolled( sal_Int32 nOldPos )
1266 {
1267     sal_Int32 nPos = GetFirstVisPos();
1268     if( !IsValidGfx() || (nPos == nOldPos) )
1269         return;
1270     if( Abs( nPos - nOldPos ) > GetVisPosCount() / 2 )
1271     {
1272         ImplDrawBackgrDev();
1273         ImplDrawGridDev();
1274         return;
1275     }
1276 
1277     Point aSrc, aDest;
1278     sal_uInt32 nFirstColIx, nLastColIx;
1279     if( nPos < nOldPos )
1280     {
1281         aSrc = Point( GetFirstX() + 1, 0 );
1282         aDest = Point( GetFirstX() + GetCharWidth() * (nOldPos - nPos) + 1, 0 );
1283         nFirstColIx = GetColumnFromPos( nPos );
1284         nLastColIx = GetColumnFromPos( nOldPos );
1285     }
1286     else
1287     {
1288         aSrc = Point( GetFirstX() + GetCharWidth() * (nPos - nOldPos) + 1, 0 );
1289         aDest = Point( GetFirstX() + 1, 0 );
1290         nFirstColIx = GetColumnFromPos( Min( nOldPos + GetVisPosCount(), GetPosCount() ) - 1 );
1291         nLastColIx = GetColumnFromPos( Min( nPos + GetVisPosCount(), GetPosCount() ) - 1 );
1292     }
1293 
1294     ImplInvertCursor( GetRulerCursorPos() + (nPos - nOldPos) );
1295     Rectangle aRectangle( GetFirstX(), 0, GetLastX(), GetHeight() - 1 );
1296     Region aClipReg( aRectangle );
1297     maBackgrDev.SetClipRegion( aClipReg );
1298     maBackgrDev.CopyArea( aDest, aSrc, maWinSize );
1299     maBackgrDev.SetClipRegion();
1300     maGridDev.SetClipRegion( aClipReg );
1301     maGridDev.CopyArea( aDest, aSrc, maWinSize );
1302     maGridDev.SetClipRegion();
1303     ImplInvertCursor( GetRulerCursorPos() );
1304 
1305     for( sal_uInt32 nColIx = nFirstColIx; nColIx <= nLastColIx; ++nColIx )
1306         ImplDrawColumn( nColIx );
1307 
1308     sal_Int32 nLastX = GetX( GetPosCount() ) + 1;
1309     if( nLastX <= GetLastX() )
1310     {
1311         Rectangle aRect( nLastX, 0, GetLastX(), GetHeight() - 1 );
1312         maBackgrDev.SetLineColor();
1313         maBackgrDev.SetFillColor( maAppBackColor );
1314         maBackgrDev.DrawRect( aRect );
1315         maGridDev.SetLineColor();
1316         maGridDev.SetFillColor( maAppBackColor );
1317         maGridDev.DrawRect( aRect );
1318     }
1319 }
1320 
1321 void ScCsvGrid::ImplInvertCursor( sal_Int32 nPos )
1322 {
1323     if( IsVisibleSplitPos( nPos ) )
1324     {
1325         sal_Int32 nX = GetX( nPos ) - 1;
1326         Rectangle aRect( Point( nX, 0 ), Size( 3, GetHdrHeight() ) );
1327         ImplInvertRect( maGridDev, aRect );
1328         aRect.Top() = GetHdrHeight() + 1;
1329         aRect.Bottom() = GetY( GetLastVisLine() + 1 );
1330         ImplInvertRect( maGridDev, aRect );
1331     }
1332 }
1333 
1334 void ScCsvGrid::ImplDrawTrackingRect( sal_uInt32 nColIndex )
1335 {
1336     if( HasFocus() && IsVisibleColumn( nColIndex ) )
1337     {
1338         sal_Int32 nX1 = Max( GetColumnX( nColIndex ), GetFirstX() ) + 1;
1339         sal_Int32 nX2 = Min( GetColumnX( nColIndex + 1 ) - sal_Int32( 1 ), GetLastX() );
1340         sal_Int32 nY2 = Min( GetY( GetLastVisLine() + 1 ), GetHeight() ) - 1;
1341         InvertTracking( Rectangle( nX1, 0, nX2, nY2 ), SHOWTRACK_SMALL | SHOWTRACK_WINDOW );
1342     }
1343 }
1344 
1345 
1346 // accessibility ==============================================================
1347 
1348 ScAccessibleCsvControl* ScCsvGrid::ImplCreateAccessible()
1349 {
1350     return new ScAccessibleCsvGrid( *this );
1351 }
1352 
1353 
1354 // ============================================================================
1355 
1356