1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 <comphelper/string.hxx>
27 #include <vcl/settings.hxx>
28 #include "miscuno.hxx"
30 using namespace com::sun::star::uno
;
32 #define SEP_PATH "Office.Calc/Dialogs/CSVImport"
33 #define FIXED_WIDTH_LIST "FixedWidthList"
35 static void load_FixedWidthList(ScCsvSplits
&rSplits
)
38 OUString sFixedWidthLists
;
41 const Any
*pProperties
;
42 Sequence
<OUString
> aNames(1);
43 OUString
* pNames
= aNames
.getArray();
44 ScLinkConfigItem
aItem( OUString( SEP_PATH
) );
46 pNames
[0] = FIXED_WIDTH_LIST
;
47 aValues
= aItem
.GetProperties( aNames
);
48 pProperties
= aValues
.getConstArray();
50 if( pProperties
[0].hasValue() )
53 pProperties
[0] >>= sFixedWidthLists
;
55 sSplits
= sFixedWidthLists
;
57 // String ends with a semi-colon so there is no 'int' after the last one.
58 sal_Int32 n
= comphelper::string::getTokenCount(sSplits
, ';') - 1;
59 for (sal_Int32 i
= 0; i
< n
; ++i
)
60 rSplits
.Insert( sSplits
.getToken(i
, ';').toInt32() );
63 static void save_FixedWidthList(const ScCsvSplits
& rSplits
)
65 OUStringBuffer sSplits
;
66 // Create a semi-colon separated string to save the splits
67 sal_uInt32 n
= rSplits
.Count();
68 for (sal_uInt32 i
= 0; i
< n
; ++i
)
70 sSplits
.append(OUString::number(rSplits
[i
]));
74 OUString sFixedWidthLists
= sSplits
.makeStringAndClear();
75 Sequence
<Any
> aValues
;
77 Sequence
<OUString
> aNames(1);
78 OUString
* pNames
= aNames
.getArray();
79 ScLinkConfigItem
aItem( OUString( SEP_PATH
) );
81 pNames
[0] = FIXED_WIDTH_LIST
;
82 aValues
= aItem
.GetProperties( aNames
);
83 pProperties
= aValues
.getArray();
84 pProperties
[0] <<= sFixedWidthLists
;
86 aItem
.PutProperties(aNames
, aValues
);
89 ScCsvRuler::ScCsvRuler( ScCsvControl
& rParent
) :
90 ScCsvControl( rParent
),
93 EnableRTL( false ); // RTL
96 maBackgrDev
->SetFont( GetFont() );
97 maRulerDev
->SetFont( GetFont() );
99 load_FixedWidthList( maSplits
);
102 ScCsvRuler::~ScCsvRuler()
107 void ScCsvRuler::dispose()
109 save_FixedWidthList( maSplits
);
110 ScCsvControl::dispose();
113 // common ruler handling ------------------------------------------------------
115 void ScCsvRuler::setPosSizePixel(
116 long nX
, long nY
, long nWidth
, long nHeight
, PosSizeFlags nFlags
)
118 if( nFlags
& PosSizeFlags::Height
)
119 nHeight
= GetTextHeight() + mnSplitSize
+ 2;
120 ScCsvControl::setPosSizePixel( nX
, nY
, nWidth
, nHeight
, nFlags
);
123 void ScCsvRuler::ApplyLayout( const ScCsvLayoutData
& rOldData
)
125 ScCsvDiff nDiff
= GetLayoutData().GetDiff( rOldData
) & (CSV_DIFF_HORIZONTAL
| CSV_DIFF_RULERCURSOR
);
126 if( nDiff
== CSV_DIFF_EQUAL
) return;
129 if( nDiff
& CSV_DIFF_HORIZONTAL
)
132 if( GetRulerCursorPos() >= GetPosCount() )
133 MoveCursor( GetPosCount() - 1 );
135 if( nDiff
& CSV_DIFF_RULERCURSOR
)
137 ImplInvertCursor( rOldData
.mnPosCursor
);
138 ImplInvertCursor( GetRulerCursorPos() );
142 if( nDiff
& CSV_DIFF_POSOFFSET
)
143 AccSendVisibleEvent();
146 void ScCsvRuler::InitColors()
148 const StyleSettings
& rSett
= GetSettings().GetStyleSettings();
149 maBackColor
= rSett
.GetFaceColor();
150 maActiveColor
= rSett
.GetWindowColor();
151 maTextColor
= rSett
.GetLabelTextColor();
152 maSplitColor
= maBackColor
.IsDark() ? maTextColor
: Color( COL_LIGHTRED
);
156 void ScCsvRuler::InitSizeData()
158 maWinSize
= GetSizePixel();
160 mnSplitSize
= (GetCharWidth() * 3 / 5) | 1; // make an odd number
162 sal_Int32 nActiveWidth
= std::min( GetWidth() - GetHdrWidth(), GetPosCount() * GetCharWidth() );
163 sal_Int32 nActiveHeight
= GetTextHeight();
165 maActiveRect
.SetPos( Point( GetFirstX(), (GetHeight() - nActiveHeight
- 1) / 2 ) );
166 maActiveRect
.SetSize( Size( nActiveWidth
, nActiveHeight
) );
168 maBackgrDev
->SetOutputSizePixel( maWinSize
);
169 maRulerDev
->SetOutputSizePixel( maWinSize
);
174 void ScCsvRuler::MoveCursor( sal_Int32 nPos
, bool bScroll
)
178 Execute( CSVCMD_MAKEPOSVISIBLE
, nPos
);
179 Execute( CSVCMD_MOVERULERCURSOR
, IsVisibleSplitPos( nPos
) ? nPos
: CSV_POS_INVALID
);
184 void ScCsvRuler::MoveCursorRel( ScMoveMode eDir
)
186 if( GetRulerCursorPos() != CSV_POS_INVALID
)
194 MoveCursor( GetPosCount() - 1 );
197 if( GetRulerCursorPos() > 1 )
198 MoveCursor( GetRulerCursorPos() - 1 );
201 if( GetRulerCursorPos() < GetPosCount() - 1 )
202 MoveCursor( GetRulerCursorPos() + 1 );
206 // added to avoid warnings
212 void ScCsvRuler::MoveCursorToSplit( ScMoveMode eDir
)
214 if( GetRulerCursorPos() != CSV_POS_INVALID
)
216 sal_uInt32 nIndex
= CSV_VEC_NOTFOUND
;
219 case MOVE_FIRST
: nIndex
= maSplits
.LowerBound( 0 ); break;
220 case MOVE_LAST
: nIndex
= maSplits
.UpperBound( GetPosCount() ); break;
221 case MOVE_PREV
: nIndex
= maSplits
.UpperBound( GetRulerCursorPos() - 1 ); break;
222 case MOVE_NEXT
: nIndex
= maSplits
.LowerBound( GetRulerCursorPos() + 1 ); break;
225 // added to avoid warnings
228 sal_Int32 nPos
= maSplits
[ nIndex
];
229 if( nPos
!= CSV_POS_INVALID
)
234 void ScCsvRuler::ScrollVertRel( ScMoveMode eDir
)
236 sal_Int32 nLine
= GetFirstVisLine();
239 case MOVE_PREV
: --nLine
; break;
240 case MOVE_NEXT
: ++nLine
; break;
241 case MOVE_PREVPAGE
: nLine
-= GetVisLineCount() - 1; break;
242 case MOVE_NEXTPAGE
: nLine
+= GetVisLineCount() - 1; break;
245 // added to avoid warnings
248 Execute( CSVCMD_SETLINEOFFSET
, nLine
);
251 // split handling -------------------------------------------------------------
253 sal_Int32
ScCsvRuler::GetNoScrollPos( sal_Int32 nPos
) const
255 sal_Int32 nNewPos
= nPos
;
256 if( nNewPos
!= CSV_POS_INVALID
)
258 if( nNewPos
< GetFirstVisPos() + CSV_SCROLL_DIST
)
260 sal_Int32 nScroll
= (GetFirstVisPos() > 0) ? CSV_SCROLL_DIST
: 0;
261 nNewPos
= std::max( nPos
, GetFirstVisPos() + nScroll
);
263 else if( nNewPos
> GetLastVisPos() - CSV_SCROLL_DIST
- 1L )
265 sal_Int32 nScroll
= (GetFirstVisPos() < GetMaxPosOffset()) ? CSV_SCROLL_DIST
: 0;
266 nNewPos
= std::min( nNewPos
, GetLastVisPos() - nScroll
- sal_Int32( 1 ) );
272 void ScCsvRuler::InsertSplit( sal_Int32 nPos
)
274 if( maSplits
.Insert( nPos
) )
276 ImplDrawSplit( nPos
);
281 void ScCsvRuler::RemoveSplit( sal_Int32 nPos
)
283 if( maSplits
.Remove( nPos
) )
285 ImplEraseSplit( nPos
);
290 void ScCsvRuler::MoveSplit( sal_Int32 nPos
, sal_Int32 nNewPos
)
292 bool bRemove
= maSplits
.Remove( nPos
);
293 bool bInsert
= maSplits
.Insert( nNewPos
);
294 if( bRemove
|| bInsert
)
296 ImplEraseSplit( nPos
);
297 ImplDrawSplit( nNewPos
);
302 void ScCsvRuler::RemoveAllSplits()
308 sal_Int32
ScCsvRuler::FindEmptyPos( sal_Int32 nPos
, ScMoveMode eDir
) const
310 sal_Int32 nNewPos
= nPos
;
311 if( nNewPos
!= CSV_POS_INVALID
)
316 nNewPos
= std::min( nPos
, FindEmptyPos( 0, MOVE_NEXT
) );
319 nNewPos
= std::max( nPos
, FindEmptyPos( GetPosCount(), MOVE_PREV
) );
322 while( HasSplit( --nNewPos
) ) ;
325 while( HasSplit( ++nNewPos
) ) ;
329 // added to avoid warnings
333 return IsValidSplitPos( nNewPos
) ? nNewPos
: CSV_POS_INVALID
;
336 void ScCsvRuler::MoveCurrSplit( sal_Int32 nNewPos
)
339 Execute( CSVCMD_MOVESPLIT
, GetRulerCursorPos(), nNewPos
);
340 MoveCursor( nNewPos
);
344 void ScCsvRuler::MoveCurrSplitRel( ScMoveMode eDir
)
346 if( HasSplit( GetRulerCursorPos() ) )
348 sal_Int32 nNewPos
= FindEmptyPos( GetRulerCursorPos(), eDir
);
349 if( nNewPos
!= CSV_POS_INVALID
)
350 MoveCurrSplit( nNewPos
);
354 // event handling -------------------------------------------------------------
356 void ScCsvRuler::Resize()
358 ScCsvControl::Resize();
363 void ScCsvRuler::GetFocus()
365 ScCsvControl::GetFocus();
367 if( GetRulerCursorPos() == CSV_POS_INVALID
)
368 MoveCursor( GetNoScrollPos( mnPosCursorLast
) );
372 void ScCsvRuler::LoseFocus()
374 ScCsvControl::LoseFocus();
375 mnPosCursorLast
= GetRulerCursorPos();
376 MoveCursor( CSV_POS_INVALID
);
379 void ScCsvRuler::DataChanged( const DataChangedEvent
& rDCEvt
)
381 if( (rDCEvt
.GetType() == DataChangedEventType::SETTINGS
) && (rDCEvt
.GetFlags() & AllSettingsFlags::STYLE
) )
386 ScCsvControl::DataChanged( rDCEvt
);
389 void ScCsvRuler::MouseButtonDown( const MouseEvent
& rMEvt
)
396 sal_Int32 nPos
= GetPosFromX( rMEvt
.GetPosPixel().X() );
397 if( IsVisibleSplitPos( nPos
) )
398 StartMouseTracking( nPos
);
399 ImplSetMousePointer( nPos
);
404 void ScCsvRuler::MouseMove( const MouseEvent
& rMEvt
)
406 if( !rMEvt
.IsModifierChanged() )
408 sal_Int32 nPos
= GetPosFromX( rMEvt
.GetPosPixel().X() );
411 // on mouse tracking: keep position valid
412 nPos
= std::max( std::min( nPos
, GetPosCount() - sal_Int32( 1 ) ), sal_Int32( 1 ) );
413 MoveMouseTracking( nPos
);
418 Rectangle
aRect( aPoint
, maWinSize
);
419 if( !IsVisibleSplitPos( nPos
) || !aRect
.IsInside( rMEvt
.GetPosPixel() ) )
420 // if focused, keep old cursor position for key input
421 nPos
= HasFocus() ? GetRulerCursorPos() : CSV_POS_INVALID
;
422 MoveCursor( nPos
, false );
424 ImplSetMousePointer( nPos
);
428 void ScCsvRuler::Tracking( const TrackingEvent
& rTEvt
)
430 if( rTEvt
.IsTrackingEnded() || rTEvt
.IsTrackingRepeat() )
431 MouseMove( rTEvt
.GetMouseEvent() );
432 if( rTEvt
.IsTrackingEnded() )
433 EndMouseTracking( !rTEvt
.IsTrackingCanceled() );
436 void ScCsvRuler::KeyInput( const KeyEvent
& rKEvt
)
438 const vcl::KeyCode
& rKCode
= rKEvt
.GetKeyCode();
439 sal_uInt16 nCode
= rKCode
.GetCode();
440 bool bNoMod
= !rKCode
.GetModifier();
441 bool bShift
= (rKCode
.GetModifier() == KEY_SHIFT
);
442 bool bJump
= (rKCode
.GetModifier() == KEY_MOD1
);
443 bool bMove
= (rKCode
.GetModifier() == (KEY_MOD1
| KEY_SHIFT
));
445 ScMoveMode eHDir
= GetHorzDirection( nCode
, true );
446 ScMoveMode eVDir
= GetVertDirection( nCode
, false );
450 if( eHDir
!= MOVE_NONE
)
451 MoveCursorRel( eHDir
);
452 else if( eVDir
!= MOVE_NONE
)
453 ScrollVertRel( eVDir
);
456 case KEY_SPACE
: Execute( CSVCMD_TOGGLESPLIT
, GetRulerCursorPos() ); break;
457 case KEY_INSERT
: Execute( CSVCMD_INSERTSPLIT
, GetRulerCursorPos() ); break;
458 case KEY_DELETE
: Execute( CSVCMD_REMOVESPLIT
, GetRulerCursorPos() ); break;
461 else if( bJump
&& (eHDir
!= MOVE_NONE
) )
462 MoveCursorToSplit( eHDir
);
463 else if( bMove
&& (eHDir
!= MOVE_NONE
) )
464 MoveCurrSplitRel( eHDir
);
465 else if( bShift
&& (nCode
== KEY_DELETE
) )
466 Execute( CSVCMD_REMOVEALLSPLITS
);
468 if( rKCode
.GetGroup() != KEYGROUP_CURSOR
)
469 ScCsvControl::KeyInput( rKEvt
);
472 void ScCsvRuler::StartMouseTracking( sal_Int32 nPos
)
474 mnPosMTStart
= mnPosMTCurr
= nPos
;
475 mbPosMTMoved
= false;
476 maOldSplits
= maSplits
;
477 Execute( CSVCMD_INSERTSPLIT
, nPos
);
478 if( HasSplit( nPos
) )
479 StartTracking( STARTTRACK_BUTTONREPEAT
);
482 void ScCsvRuler::MoveMouseTracking( sal_Int32 nPos
)
484 if( mnPosMTCurr
!= nPos
)
488 if( (mnPosMTCurr
!= mnPosMTStart
) && maOldSplits
.HasSplit( mnPosMTCurr
) )
489 Execute( CSVCMD_INSERTSPLIT
, nPos
);
491 Execute( CSVCMD_MOVESPLIT
, mnPosMTCurr
, nPos
);
498 void ScCsvRuler::EndMouseTracking( bool bApply
)
500 if( bApply
) // tracking finished successfully
502 // remove on simple click on an existing split
503 if( (mnPosMTCurr
== mnPosMTStart
) && maOldSplits
.HasSplit( mnPosMTCurr
) && !mbPosMTMoved
)
504 Execute( CSVCMD_REMOVESPLIT
, mnPosMTCurr
);
506 else // tracking cancelled
508 MoveCursor( mnPosMTStart
);
509 // move split to origin
510 if( maOldSplits
.HasSplit( mnPosMTStart
) )
511 MoveMouseTracking( mnPosMTStart
);
512 // remove temporarily inserted split
513 else if( !maOldSplits
.HasSplit( mnPosMTCurr
) )
514 Execute( CSVCMD_REMOVESPLIT
, mnPosMTCurr
);
516 mnPosMTStart
= CSV_POS_INVALID
;
519 // painting -------------------------------------------------------------------
521 void ScCsvRuler::Paint( vcl::RenderContext
& /*rRenderContext*/, const Rectangle
& )
526 void ScCsvRuler::ImplRedraw()
536 DrawOutDev( Point(), maWinSize
, Point(), maWinSize
, *maRulerDev
.get() );
537 ImplDrawTrackingRect();
541 void ScCsvRuler::ImplDrawArea( sal_Int32 nPosX
, sal_Int32 nWidth
)
543 maBackgrDev
->SetLineColor();
544 Rectangle
aRect( Point( nPosX
, 0 ), Size( nWidth
, GetHeight() ) );
545 maBackgrDev
->SetFillColor( maBackColor
);
546 maBackgrDev
->DrawRect( aRect
);
548 aRect
= maActiveRect
;
549 aRect
.Left() = std::max( GetFirstX(), nPosX
);
550 aRect
.Right() = std::min( std::min( GetX( GetPosCount() ), GetLastX() ), nPosX
+ nWidth
- sal_Int32( 1 ) );
551 if( aRect
.Left() <= aRect
.Right() )
553 maBackgrDev
->SetFillColor( maActiveColor
);
554 maBackgrDev
->DrawRect( aRect
);
557 maBackgrDev
->SetLineColor( maTextColor
);
558 sal_Int32 nY
= GetHeight() - 1;
559 maBackgrDev
->DrawLine( Point( nPosX
, nY
), Point( nPosX
+ nWidth
- 1, nY
) );
562 void ScCsvRuler::ImplDrawBackgrDev()
564 ImplDrawArea( 0, GetWidth() );
567 maBackgrDev
->SetLineColor( maTextColor
);
568 maBackgrDev
->SetFillColor();
571 sal_Int32 nFirstPos
= std::max( GetPosFromX( 0 ) - (sal_Int32
)(1L), (sal_Int32
)(0L) );
572 sal_Int32 nLastPos
= GetPosFromX( GetWidth() );
573 sal_Int32 nY
= (maActiveRect
.Top() + maActiveRect
.Bottom()) / 2;
574 for( nPos
= nFirstPos
; nPos
<= nLastPos
; ++nPos
)
576 sal_Int32 nX
= GetX( nPos
);
578 maBackgrDev
->DrawPixel( Point( nX
, nY
) );
580 maBackgrDev
->DrawLine( Point( nX
, nY
- 1 ), Point( nX
, nY
+ 1 ) );
584 maBackgrDev
->SetTextColor( maTextColor
);
585 maBackgrDev
->SetTextFillColor();
586 for( nPos
= ((nFirstPos
+ 9) / 10) * 10; nPos
<= nLastPos
; nPos
+= 10 )
588 OUString
aText( OUString::number( nPos
) );
589 sal_Int32 nTextWidth
= maBackgrDev
->GetTextWidth( aText
);
590 sal_Int32 nTextX
= GetX( nPos
) - nTextWidth
/ 2;
591 ImplDrawArea( nTextX
- 1, nTextWidth
+ 2 );
592 maBackgrDev
->DrawText( Point( nTextX
, maActiveRect
.Top() ), aText
);
596 void ScCsvRuler::ImplDrawSplit( sal_Int32 nPos
)
598 if( IsVisibleSplitPos( nPos
) )
600 Point
aPos( GetX( nPos
) - mnSplitSize
/ 2, GetHeight() - mnSplitSize
- 2 );
601 Size
aSize( mnSplitSize
, mnSplitSize
);
602 maRulerDev
->SetLineColor( maTextColor
);
603 maRulerDev
->SetFillColor( maSplitColor
);
604 maRulerDev
->DrawEllipse( Rectangle( aPos
, aSize
) );
605 maRulerDev
->DrawPixel( Point( GetX( nPos
), GetHeight() - 2 ) );
609 void ScCsvRuler::ImplEraseSplit( sal_Int32 nPos
)
611 if( IsVisibleSplitPos( nPos
) )
613 ImplInvertCursor( GetRulerCursorPos() );
614 Point
aPos( GetX( nPos
) - mnSplitSize
/ 2, 0 );
615 Size
aSize( mnSplitSize
, GetHeight() );
616 maRulerDev
->DrawOutDev( aPos
, aSize
, aPos
, aSize
, *maBackgrDev
.get() );
617 ImplInvertCursor( GetRulerCursorPos() );
621 void ScCsvRuler::ImplDrawRulerDev()
623 maRulerDev
->DrawOutDev( Point(), maWinSize
, Point(), maWinSize
, *maBackgrDev
.get() );
624 ImplInvertCursor( GetRulerCursorPos() );
626 sal_uInt32 nFirst
= maSplits
.LowerBound( GetFirstVisPos() );
627 sal_uInt32 nLast
= maSplits
.UpperBound( GetLastVisPos() );
628 if( (nFirst
!= CSV_VEC_NOTFOUND
) && (nLast
!= CSV_VEC_NOTFOUND
) )
629 for( sal_uInt32 nIndex
= nFirst
; nIndex
<= nLast
; ++nIndex
)
630 ImplDrawSplit( GetSplitPos( nIndex
) );
633 void ScCsvRuler::ImplInvertCursor( sal_Int32 nPos
)
635 if( IsVisibleSplitPos( nPos
) )
637 ImplInvertRect( *maRulerDev
.get(), Rectangle( Point( GetX( nPos
) - 1, 0 ), Size( 3, GetHeight() - 1 ) ) );
638 if( HasSplit( nPos
) )
639 ImplDrawSplit( nPos
);
643 void ScCsvRuler::ImplDrawTrackingRect()
646 InvertTracking( Rectangle( 0, 0, GetWidth() - 1, GetHeight() - 2 ),
647 SHOWTRACK_SMALL
| SHOWTRACK_WINDOW
);
650 void ScCsvRuler::ImplSetMousePointer( sal_Int32 nPos
)
652 SetPointer( Pointer( HasSplit( nPos
) ? PointerStyle::HSplit
: PointerStyle::Arrow
) );
655 // accessibility ==============================================================
657 ScAccessibleCsvControl
* ScCsvRuler::ImplCreateAccessible()
659 return new ScAccessibleCsvRuler( *this );
662 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */