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