calc: on editing invalidation of view with different zoom is wrong
[LibreOffice.git] / sc / source / ui / dbgui / csvruler.cxx
blob3af7645e24314438e11ddaead716bed07626d71c
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <csvruler.hxx>
21 #include <AccessibleCsvControl.hxx>
23 #include <optutil.hxx>
24 #include <com/sun/star/uno/Any.hxx>
25 #include <com/sun/star/uno/Sequence.hxx>
26 #include <vcl/event.hxx>
27 #include <vcl/settings.hxx>
28 #include <vcl/ptrstyle.hxx>
29 #include <vcl/svapp.hxx>
30 #include <vcl/virdev.hxx>
31 #include <o3tl/string_view.hxx>
33 using namespace com::sun::star::uno;
35 constexpr OUStringLiteral SEP_PATH = u"Office.Calc/Dialogs/CSVImport";
36 constexpr OUStringLiteral FIXED_WIDTH_LIST = u"FixedWidthList";
38 static void load_FixedWidthList(ScCsvSplits &rSplits)
40 Sequence<Any>aValues;
41 const Any *pProperties;
42 Sequence<OUString> aNames { FIXED_WIDTH_LIST };
43 ScLinkConfigItem aItem( SEP_PATH );
45 aValues = aItem.GetProperties( aNames );
46 pProperties = aValues.getConstArray();
48 if( !pProperties[0].hasValue() )
49 return;
51 rSplits.Clear();
53 OUString sFixedWidthLists;
54 pProperties[0] >>= sFixedWidthLists;
56 sal_Int32 nIdx {0};
57 for(;;)
59 const sal_Int32 n = o3tl::toInt32(o3tl::getToken(sFixedWidthLists, 0, ';', nIdx));
60 if (nIdx<0)
62 // String ends with a semi-colon so there
63 // is no useful 'int' after the last one.
64 // This also works in case of empty string
65 break;
67 rSplits.Insert(n);
70 static void save_FixedWidthList(const ScCsvSplits& rSplits)
72 OUStringBuffer sSplits;
73 // Create a semi-colon separated string to save the splits
74 sal_uInt32 n = rSplits.Count();
75 for (sal_uInt32 i = 0; i < n; ++i)
77 sSplits.append(rSplits[i]);
78 sSplits.append(";");
81 OUString sFixedWidthLists = sSplits.makeStringAndClear();
82 Sequence<Any> aValues;
83 Any *pProperties;
84 Sequence<OUString> aNames { FIXED_WIDTH_LIST };
85 ScLinkConfigItem aItem( SEP_PATH );
87 aValues = aItem.GetProperties( aNames );
88 pProperties = aValues.getArray();
89 pProperties[0] <<= sFixedWidthLists;
91 aItem.PutProperties(aNames, aValues);
94 ScCsvRuler::ScCsvRuler(const ScCsvLayoutData& rData, ScCsvTableBox* pTableBox)
95 : ScCsvControl(rData)
96 , mpTableBox(pTableBox)
97 , mnPosCursorLast(1)
98 , mnPosMTStart(0)
99 , mnPosMTCurr(0)
100 , mbPosMTMoved(false)
101 , mnSplitSize(0)
102 , mbTracking(false)
106 void ScCsvRuler::SetDrawingArea(weld::DrawingArea* pDrawingArea)
108 ScCsvControl::SetDrawingArea(pDrawingArea);
110 UpdateSplitSize();
112 Size aSize(1, GetTextHeight() + mnSplitSize + 2);
113 pDrawingArea->set_size_request(aSize.Width(), aSize.Height());
114 SetOutputSizePixel(aSize);
116 EnableRTL( false ); // RTL
117 InitColors();
118 InitSizeData();
120 OutputDevice& rRefDevice = pDrawingArea->get_ref_device();
121 maBackgrDev->SetFont( rRefDevice.GetFont() );
122 maRulerDev->SetFont( rRefDevice.GetFont() );
123 load_FixedWidthList( maSplits );
126 ScCsvRuler::~ScCsvRuler()
128 save_FixedWidthList( maSplits );
131 // common ruler handling ------------------------------------------------------
133 void ScCsvRuler::ApplyLayout( const ScCsvLayoutData& rOldData )
135 ScCsvDiff nDiff = GetLayoutData().GetDiff( rOldData ) & (ScCsvDiff::HorizontalMask | ScCsvDiff::RulerCursor);
136 if( nDiff == ScCsvDiff::Equal ) return;
138 DisableRepaint();
139 if( nDiff & ScCsvDiff::HorizontalMask )
141 InitSizeData();
142 if( GetRulerCursorPos() >= GetPosCount() )
143 MoveCursor( GetPosCount() - 1 );
145 if( nDiff & ScCsvDiff::RulerCursor )
147 ImplInvertCursor( rOldData.mnPosCursor );
148 ImplInvertCursor( GetRulerCursorPos() );
150 EnableRepaint();
152 if( nDiff & ScCsvDiff::PosOffset )
153 AccSendVisibleEvent();
156 void ScCsvRuler::InitColors()
158 const StyleSettings& rSett = Application::GetSettings().GetStyleSettings();
159 maBackColor = rSett.GetFaceColor();
160 maActiveColor = rSett.GetWindowColor();
161 maTextColor = rSett.GetLabelTextColor();
162 maSplitColor = maBackColor.IsDark() ? maTextColor : COL_LIGHTRED;
163 InvalidateGfx();
166 void ScCsvRuler::UpdateSplitSize()
168 mnSplitSize = (GetCharWidth() * 3 / 5) | 1; // make an odd number
171 void ScCsvRuler::InitSizeData()
173 maWinSize = GetOutputSizePixel();
175 UpdateSplitSize();
177 sal_Int32 nActiveWidth = std::min( GetWidth() - GetHdrWidth(), GetPosCount() * GetCharWidth() );
178 sal_Int32 nActiveHeight = GetTextHeight();
180 maActiveRect.SetPos( Point( GetFirstX(), (GetHeight() - nActiveHeight - 1) / 2 ) );
181 maActiveRect.SetSize( Size( nActiveWidth, nActiveHeight ) );
183 maBackgrDev->SetOutputSizePixel( maWinSize );
184 maRulerDev->SetOutputSizePixel( maWinSize );
186 InvalidateGfx();
189 void ScCsvRuler::MoveCursor( sal_Int32 nPos, bool bScroll )
191 DisableRepaint();
192 if( bScroll )
193 Execute( CSVCMD_MAKEPOSVISIBLE, nPos );
194 Execute( CSVCMD_MOVERULERCURSOR, IsVisibleSplitPos( nPos ) ? nPos : CSV_POS_INVALID );
195 EnableRepaint();
196 AccSendCaretEvent();
199 void ScCsvRuler::MoveCursorRel( ScMoveMode eDir )
201 if( GetRulerCursorPos() == CSV_POS_INVALID )
202 return;
204 switch( eDir )
206 case MOVE_FIRST:
207 MoveCursor( 1 );
208 break;
209 case MOVE_LAST:
210 MoveCursor( GetPosCount() - 1 );
211 break;
212 case MOVE_PREV:
213 if( GetRulerCursorPos() > 1 )
214 MoveCursor( GetRulerCursorPos() - 1 );
215 break;
216 case MOVE_NEXT:
217 if( GetRulerCursorPos() < GetPosCount() - 1 )
218 MoveCursor( GetRulerCursorPos() + 1 );
219 break;
220 default:
222 // added to avoid warnings
227 void ScCsvRuler::MoveCursorToSplit( ScMoveMode eDir )
229 if( GetRulerCursorPos() == CSV_POS_INVALID )
230 return;
232 sal_uInt32 nIndex = CSV_VEC_NOTFOUND;
233 switch( eDir )
235 case MOVE_FIRST: nIndex = maSplits.LowerBound( 0 ); break;
236 case MOVE_LAST: nIndex = maSplits.UpperBound( GetPosCount() ); break;
237 case MOVE_PREV: nIndex = maSplits.UpperBound( GetRulerCursorPos() - 1 ); break;
238 case MOVE_NEXT: nIndex = maSplits.LowerBound( GetRulerCursorPos() + 1 ); break;
239 default:
241 // added to avoid warnings
244 sal_Int32 nPos = maSplits[ nIndex ];
245 if( nPos != CSV_POS_INVALID )
246 MoveCursor( nPos );
249 void ScCsvRuler::ScrollVertRel( ScMoveMode eDir )
251 sal_Int32 nLine = GetFirstVisLine();
252 switch( eDir )
254 case MOVE_PREV: --nLine; break;
255 case MOVE_NEXT: ++nLine; break;
256 case MOVE_PREVPAGE: nLine -= GetVisLineCount() - 1; break;
257 case MOVE_NEXTPAGE: nLine += GetVisLineCount() - 1; break;
258 default:
260 // added to avoid warnings
263 Execute( CSVCMD_SETLINEOFFSET, nLine );
266 // split handling -------------------------------------------------------------
268 sal_Int32 ScCsvRuler::GetNoScrollPos( sal_Int32 nPos ) const
270 sal_Int32 nNewPos = nPos;
271 if( nNewPos != CSV_POS_INVALID )
273 if( nNewPos < GetFirstVisPos() + CSV_SCROLL_DIST )
275 sal_Int32 nScroll = (GetFirstVisPos() > 0) ? CSV_SCROLL_DIST : 0;
276 nNewPos = std::max( nPos, GetFirstVisPos() + nScroll );
278 else if( nNewPos > GetLastVisPos() - CSV_SCROLL_DIST - 1 )
280 sal_Int32 nScroll = (GetFirstVisPos() < GetMaxPosOffset()) ? CSV_SCROLL_DIST : 0;
281 nNewPos = std::min( nNewPos, GetLastVisPos() - nScroll - sal_Int32( 1 ) );
284 return nNewPos;
287 void ScCsvRuler::InsertSplit( sal_Int32 nPos )
289 if( maSplits.Insert( nPos ) )
291 ImplDrawSplit( nPos );
292 Repaint();
296 void ScCsvRuler::RemoveSplit( sal_Int32 nPos )
298 if( maSplits.Remove( nPos ) )
300 ImplEraseSplit( nPos );
301 Repaint();
305 void ScCsvRuler::MoveSplit( sal_Int32 nPos, sal_Int32 nNewPos )
307 bool bRemove = maSplits.Remove( nPos );
308 bool bInsert = maSplits.Insert( nNewPos );
309 if( bRemove || bInsert )
311 ImplEraseSplit( nPos );
312 ImplDrawSplit( nNewPos );
313 Repaint();
317 void ScCsvRuler::RemoveAllSplits()
319 maSplits.Clear();
320 Repaint( true );
323 sal_Int32 ScCsvRuler::FindEmptyPos( sal_Int32 nPos, ScMoveMode eDir ) const
325 sal_Int32 nNewPos = nPos;
326 if( nNewPos != CSV_POS_INVALID )
328 switch( eDir )
330 case MOVE_FIRST:
331 nNewPos = std::min( nPos, FindEmptyPos( 0, MOVE_NEXT ) );
332 break;
333 case MOVE_LAST:
334 nNewPos = std::max( nPos, FindEmptyPos( GetPosCount(), MOVE_PREV ) );
335 break;
336 case MOVE_PREV:
337 while( HasSplit( --nNewPos ) ) ;
338 break;
339 case MOVE_NEXT:
340 while( HasSplit( ++nNewPos ) ) ;
341 break;
342 default:
344 // added to avoid warnings
348 return IsValidSplitPos( nNewPos ) ? nNewPos : CSV_POS_INVALID;
351 void ScCsvRuler::MoveCurrSplit( sal_Int32 nNewPos )
353 DisableRepaint();
354 Execute( CSVCMD_MOVESPLIT, GetRulerCursorPos(), nNewPos );
355 MoveCursor( nNewPos );
356 EnableRepaint();
359 void ScCsvRuler::MoveCurrSplitRel( ScMoveMode eDir )
361 if( HasSplit( GetRulerCursorPos() ) )
363 sal_Int32 nNewPos = FindEmptyPos( GetRulerCursorPos(), eDir );
364 if( nNewPos != CSV_POS_INVALID )
365 MoveCurrSplit( nNewPos );
369 // event handling -------------------------------------------------------------
371 void ScCsvRuler::Resize()
373 ScCsvControl::Resize();
374 InitSizeData();
375 Repaint();
378 void ScCsvRuler::GetFocus()
380 ScCsvControl::GetFocus();
381 DisableRepaint();
382 if( GetRulerCursorPos() == CSV_POS_INVALID )
383 MoveCursor( GetNoScrollPos( mnPosCursorLast ) );
384 EnableRepaint();
387 void ScCsvRuler::LoseFocus()
389 ScCsvControl::LoseFocus();
390 mnPosCursorLast = GetRulerCursorPos();
391 MoveCursor( CSV_POS_INVALID );
394 void ScCsvRuler::StyleUpdated()
396 InitColors();
397 Repaint();
399 ScCsvControl::StyleUpdated();
402 bool ScCsvRuler::MouseButtonDown( const MouseEvent& rMEvt )
404 DisableRepaint();
405 if( !HasFocus() )
406 GrabFocus();
407 if( rMEvt.IsLeft() )
409 sal_Int32 nPos = GetPosFromX( rMEvt.GetPosPixel().X() );
410 if( IsVisibleSplitPos( nPos ) )
411 StartMouseTracking( nPos );
412 ImplSetMousePointer( nPos );
414 EnableRepaint();
415 return true;
418 bool ScCsvRuler::MouseButtonUp( const MouseEvent& )
420 if (mbTracking)
422 EndMouseTracking();
423 mbTracking = false;
425 return true;
428 bool ScCsvRuler::MouseMove( const MouseEvent& rMEvt )
430 if( !rMEvt.IsModifierChanged() )
432 sal_Int32 nPos = GetPosFromX( rMEvt.GetPosPixel().X() );
433 if( mbTracking )
435 // on mouse tracking: keep position valid
436 nPos = std::clamp( nPos, sal_Int32(1), GetPosCount() - 1 );
437 MoveMouseTracking( nPos );
439 else
441 tools::Rectangle aRect( Point(), maWinSize );
442 if( !IsVisibleSplitPos( nPos ) || !aRect.Contains( rMEvt.GetPosPixel() ) )
443 // if focused, keep old cursor position for key input
444 nPos = HasFocus() ? GetRulerCursorPos() : CSV_POS_INVALID;
445 MoveCursor( nPos, false );
447 ImplSetMousePointer( nPos );
449 return true;
452 bool ScCsvRuler::KeyInput( const KeyEvent& rKEvt )
454 const vcl::KeyCode& rKCode = rKEvt.GetKeyCode();
455 sal_uInt16 nCode = rKCode.GetCode();
456 bool bNoMod = !rKCode.GetModifier();
457 bool bShift = (rKCode.GetModifier() == KEY_SHIFT);
458 bool bJump = (rKCode.GetModifier() == KEY_MOD1);
459 bool bMove = (rKCode.GetModifier() == (KEY_MOD1 | KEY_SHIFT));
461 ScMoveMode eHDir = GetHorzDirection( nCode, true );
462 ScMoveMode eVDir = GetVertDirection( nCode, false );
464 if( bNoMod )
466 if( eHDir != MOVE_NONE )
467 MoveCursorRel( eHDir );
468 else if( eVDir != MOVE_NONE )
469 ScrollVertRel( eVDir );
470 else switch( nCode )
472 case KEY_SPACE: Execute( CSVCMD_TOGGLESPLIT, GetRulerCursorPos() ); break;
473 case KEY_INSERT: Execute( CSVCMD_INSERTSPLIT, GetRulerCursorPos() ); break;
474 case KEY_DELETE: Execute( CSVCMD_REMOVESPLIT, GetRulerCursorPos() ); break;
477 else if( bJump && (eHDir != MOVE_NONE) )
478 MoveCursorToSplit( eHDir );
479 else if( bMove && (eHDir != MOVE_NONE) )
480 MoveCurrSplitRel( eHDir );
481 else if( bShift && (nCode == KEY_DELETE) )
482 Execute( CSVCMD_REMOVEALLSPLITS );
484 return rKCode.GetGroup() == KEYGROUP_CURSOR;
487 void ScCsvRuler::StartMouseTracking( sal_Int32 nPos )
489 mnPosMTStart = mnPosMTCurr = nPos;
490 mbPosMTMoved = false;
491 maOldSplits = maSplits;
492 Execute( CSVCMD_INSERTSPLIT, nPos );
493 if( HasSplit( nPos ) )
494 mbTracking = true;
497 void ScCsvRuler::MoveMouseTracking( sal_Int32 nPos )
499 if( mnPosMTCurr != nPos )
501 DisableRepaint();
502 MoveCursor( nPos );
503 if( (mnPosMTCurr != mnPosMTStart) && maOldSplits.HasSplit( mnPosMTCurr ) )
504 Execute( CSVCMD_INSERTSPLIT, nPos );
505 else
506 Execute( CSVCMD_MOVESPLIT, mnPosMTCurr, nPos );
507 mnPosMTCurr = nPos;
508 mbPosMTMoved = true;
509 EnableRepaint();
513 void ScCsvRuler::EndMouseTracking()
515 // remove on simple click on an existing split
516 if( (mnPosMTCurr == mnPosMTStart) && maOldSplits.HasSplit( mnPosMTCurr ) && !mbPosMTMoved )
517 Execute( CSVCMD_REMOVESPLIT, mnPosMTCurr );
518 mnPosMTStart = CSV_POS_INVALID;
521 // painting -------------------------------------------------------------------
523 void ScCsvRuler::Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& )
525 ImplRedraw(rRenderContext);
528 void ScCsvRuler::ImplRedraw(vcl::RenderContext& rRenderContext)
530 if( IsVisible() )
532 if( !IsValidGfx() )
534 ValidateGfx();
535 ImplDrawBackgrDev();
536 ImplDrawRulerDev();
538 rRenderContext.DrawOutDev( Point(), maWinSize, Point(), maWinSize, *maRulerDev );
542 tools::Rectangle ScCsvRuler::GetFocusRect()
544 /* Draws directly tracking rectangle to the column with the specified index. */
545 if(HasFocus())
546 return tools::Rectangle(0, 0, GetWidth() - 1, GetHeight() - 2);
547 return weld::CustomWidgetController::GetFocusRect();
550 void ScCsvRuler::ImplDrawArea( sal_Int32 nPosX, sal_Int32 nWidth )
552 maBackgrDev->SetLineColor();
553 tools::Rectangle aRect( Point( nPosX, 0 ), Size( nWidth, GetHeight() ) );
554 maBackgrDev->SetFillColor( maBackColor );
555 maBackgrDev->DrawRect( aRect );
557 aRect = maActiveRect;
558 aRect.SetLeft( std::max( GetFirstX(), nPosX ) );
559 aRect.SetRight( std::min( std::min( GetX( GetPosCount() ), GetLastX() ), nPosX + nWidth - sal_Int32( 1 ) ) );
560 if( aRect.Left() <= aRect.Right() )
562 maBackgrDev->SetFillColor( maActiveColor );
563 maBackgrDev->DrawRect( aRect );
566 maBackgrDev->SetLineColor( maTextColor );
567 sal_Int32 nY = GetHeight() - 1;
568 maBackgrDev->DrawLine( Point( nPosX, nY ), Point( nPosX + nWidth - 1, nY ) );
571 void ScCsvRuler::ImplDrawBackgrDev()
573 ImplDrawArea( 0, GetWidth() );
575 // scale
576 maBackgrDev->SetLineColor( maTextColor );
577 maBackgrDev->SetFillColor();
578 sal_Int32 nPos;
580 sal_Int32 nFirstPos = std::max( GetPosFromX( 0 ) - 1, sal_Int32(0) );
581 sal_Int32 nLastPos = GetPosFromX( GetWidth() );
582 sal_Int32 nY = (maActiveRect.Top() + maActiveRect.Bottom()) / 2;
583 for( nPos = nFirstPos; nPos <= nLastPos; ++nPos )
585 sal_Int32 nX = GetX( nPos );
586 if( nPos % 5 )
587 maBackgrDev->DrawPixel( Point( nX, nY ) );
588 else
589 maBackgrDev->DrawLine( Point( nX, nY - 1 ), Point( nX, nY + 1 ) );
592 // texts
593 maBackgrDev->SetTextColor( maTextColor );
594 maBackgrDev->SetTextFillColor();
595 for( nPos = ((nFirstPos + 9) / 10) * 10; nPos <= nLastPos; nPos += 10 )
597 OUString aText( OUString::number( nPos ) );
598 sal_Int32 nTextWidth = maBackgrDev->GetTextWidth( aText );
599 sal_Int32 nTextX = GetX( nPos ) - nTextWidth / 2;
600 ImplDrawArea( nTextX - 1, nTextWidth + 2 );
601 maBackgrDev->DrawText( Point( nTextX, maActiveRect.Top() ), aText );
605 void ScCsvRuler::ImplDrawSplit( sal_Int32 nPos )
607 if( IsVisibleSplitPos( nPos ) )
609 Point aPos( GetX( nPos ) - mnSplitSize / 2, GetHeight() - mnSplitSize - 2 );
610 Size aSize( mnSplitSize, mnSplitSize );
611 maRulerDev->SetLineColor( maTextColor );
612 maRulerDev->SetFillColor( maSplitColor );
613 maRulerDev->DrawEllipse( tools::Rectangle( aPos, aSize ) );
614 maRulerDev->DrawPixel( Point( GetX( nPos ), GetHeight() - 2 ) );
618 void ScCsvRuler::ImplEraseSplit( sal_Int32 nPos )
620 if( IsVisibleSplitPos( nPos ) )
622 ImplInvertCursor( GetRulerCursorPos() );
623 Point aPos( GetX( nPos ) - mnSplitSize / 2, 0 );
624 Size aSize( mnSplitSize, GetHeight() );
625 maRulerDev->DrawOutDev( aPos, aSize, aPos, aSize, *maBackgrDev );
626 ImplInvertCursor( GetRulerCursorPos() );
630 void ScCsvRuler::ImplDrawRulerDev()
632 maRulerDev->DrawOutDev( Point(), maWinSize, Point(), maWinSize, *maBackgrDev );
633 ImplInvertCursor( GetRulerCursorPos() );
635 sal_uInt32 nFirst = maSplits.LowerBound( GetFirstVisPos() );
636 sal_uInt32 nLast = maSplits.UpperBound( GetLastVisPos() );
637 if( (nFirst != CSV_VEC_NOTFOUND) && (nLast != CSV_VEC_NOTFOUND) )
638 for( sal_uInt32 nIndex = nFirst; nIndex <= nLast; ++nIndex )
639 ImplDrawSplit( GetSplitPos( nIndex ) );
642 void ScCsvRuler::ImplInvertCursor( sal_Int32 nPos )
644 if( IsVisibleSplitPos( nPos ) )
646 ImplInvertRect( *maRulerDev, tools::Rectangle( Point( GetX( nPos ) - 1, 0 ), Size( 3, GetHeight() - 1 ) ) );
647 if( HasSplit( nPos ) )
648 ImplDrawSplit( nPos );
652 void ScCsvRuler::ImplSetMousePointer( sal_Int32 nPos )
654 SetPointer( HasSplit( nPos ) ? PointerStyle::HSplit : PointerStyle::Arrow );
657 // accessibility ==============================================================
659 css::uno::Reference<css::accessibility::XAccessible> ScCsvRuler::CreateAccessible()
661 rtl::Reference<ScAccessibleCsvRuler> xRef(new ScAccessibleCsvRuler(*this));
662 mxAccessible = xRef;
663 return xRef;
666 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */