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"
24 #include <optutil.hxx>
25 #include <com/sun/star/uno/Any.hxx>
26 #include <com/sun/star/uno/Sequence.hxx>
27 #include <comphelper/string.hxx>
28 #include "miscuno.hxx"
30 using namespace com::sun::star::uno
;
35 // ============================================================================
36 #define SEP_PATH "Office.Calc/Dialogs/CSVImport"
37 #define FIXED_WIDTH_LIST "FixedWidthList"
40 // ============================================================================
42 static void load_FixedWidthList(ScCsvSplits
&aSplits
)
45 OUString sFixedWidthLists
;
48 const Any
*pProperties
;
49 Sequence
<OUString
> aNames(1);
50 OUString
* pNames
= aNames
.getArray();
51 ScLinkConfigItem
aItem( OUString( SEP_PATH
) );
53 pNames
[0] = OUString( FIXED_WIDTH_LIST
);
54 aValues
= aItem
.GetProperties( aNames
);
55 pProperties
= aValues
.getConstArray();
57 if( pProperties
[0].hasValue() )
60 pProperties
[0] >>= sFixedWidthLists
;
62 sSplits
= String( sFixedWidthLists
);
64 // String ends with a semi-colon so there is no 'int' after the last one.
65 xub_StrLen n
= comphelper::string::getTokenCount(sSplits
, ';') - 1;
66 for (xub_StrLen i
= 0; i
< n
; ++i
)
67 aSplits
.Insert( sSplits
.GetToken(i
).ToInt32() );
70 static void save_FixedWidthList(ScCsvSplits aSplits
)
72 OUStringBuffer sSplits
;
73 // Create a semi-colon separated string to save the splits
74 sal_uInt32 n
= aSplits
.Count();
75 for (sal_uInt32 i
= 0; i
< n
; ++i
)
77 sSplits
.append( OUString::number( aSplits
[i
] ) );
81 OUString sFixedWidthLists
= sSplits
.makeStringAndClear();
82 Sequence
<Any
> aValues
;
84 Sequence
<OUString
> aNames(1);
85 OUString
* pNames
= aNames
.getArray();
86 ScLinkConfigItem
aItem( OUString( SEP_PATH
) );
88 pNames
[0] = OUString( FIXED_WIDTH_LIST
);
89 aValues
= aItem
.GetProperties( aNames
);
90 pProperties
= aValues
.getArray();
91 pProperties
[0] <<= sFixedWidthLists
;
93 aItem
.PutProperties(aNames
, aValues
);
96 ScCsvRuler::ScCsvRuler( ScCsvControl
& rParent
) :
97 ScCsvControl( rParent
),
100 EnableRTL( false ); // RTL
103 maBackgrDev
.SetFont( GetFont() );
104 maRulerDev
.SetFont( GetFont() );
106 load_FixedWidthList( maSplits
);
109 ScCsvRuler::~ScCsvRuler()
111 save_FixedWidthList( maSplits
);
115 // common ruler handling ------------------------------------------------------
117 void ScCsvRuler::setPosSizePixel(
118 long nX
, long nY
, long nWidth
, long nHeight
, sal_uInt16 nFlags
)
120 if( nFlags
& WINDOW_POSSIZE_HEIGHT
)
121 nHeight
= GetTextHeight() + mnSplitSize
+ 2;
122 ScCsvControl::setPosSizePixel( nX
, nY
, nWidth
, nHeight
, nFlags
);
125 void ScCsvRuler::ApplyLayout( const ScCsvLayoutData
& rOldData
)
127 ScCsvDiff nDiff
= GetLayoutData().GetDiff( rOldData
) & (CSV_DIFF_HORIZONTAL
| CSV_DIFF_RULERCURSOR
);
128 if( nDiff
== CSV_DIFF_EQUAL
) return;
131 if( nDiff
& CSV_DIFF_HORIZONTAL
)
134 if( GetRulerCursorPos() >= GetPosCount() )
135 MoveCursor( GetPosCount() - 1 );
137 if( nDiff
& CSV_DIFF_RULERCURSOR
)
139 ImplInvertCursor( rOldData
.mnPosCursor
);
140 ImplInvertCursor( GetRulerCursorPos() );
144 if( nDiff
& CSV_DIFF_POSOFFSET
)
145 AccSendVisibleEvent();
148 void ScCsvRuler::InitColors()
150 const StyleSettings
& rSett
= GetSettings().GetStyleSettings();
151 maBackColor
= rSett
.GetFaceColor();
152 maActiveColor
= rSett
.GetWindowColor();
153 maTextColor
= rSett
.GetLabelTextColor();
154 maSplitColor
= maBackColor
.IsDark() ? maTextColor
: Color( COL_LIGHTRED
);
158 void ScCsvRuler::InitSizeData()
160 maWinSize
= GetSizePixel();
162 mnSplitSize
= (GetCharWidth() * 3 / 5) | 1; // make an odd number
164 sal_Int32 nActiveWidth
= std::min( GetWidth() - GetHdrWidth(), GetPosCount() * GetCharWidth() );
165 sal_Int32 nActiveHeight
= GetTextHeight();
167 maActiveRect
.SetPos( Point( GetFirstX(), (GetHeight() - nActiveHeight
- 1) / 2 ) );
168 maActiveRect
.SetSize( Size( nActiveWidth
, nActiveHeight
) );
170 maBackgrDev
.SetOutputSizePixel( maWinSize
);
171 maRulerDev
.SetOutputSizePixel( maWinSize
);
176 void ScCsvRuler::MoveCursor( sal_Int32 nPos
, bool bScroll
)
180 Execute( CSVCMD_MAKEPOSVISIBLE
, nPos
);
181 Execute( CSVCMD_MOVERULERCURSOR
, IsVisibleSplitPos( nPos
) ? nPos
: CSV_POS_INVALID
);
186 void ScCsvRuler::MoveCursorRel( ScMoveMode eDir
)
188 if( GetRulerCursorPos() != CSV_POS_INVALID
)
196 MoveCursor( GetPosCount() - 1 );
199 if( GetRulerCursorPos() > 1 )
200 MoveCursor( GetRulerCursorPos() - 1 );
203 if( GetRulerCursorPos() < GetPosCount() - 1 )
204 MoveCursor( GetRulerCursorPos() + 1 );
208 // added to avoid warnings
214 void ScCsvRuler::MoveCursorToSplit( ScMoveMode eDir
)
216 if( GetRulerCursorPos() != CSV_POS_INVALID
)
218 sal_uInt32 nIndex
= CSV_VEC_NOTFOUND
;
221 case MOVE_FIRST
: nIndex
= maSplits
.LowerBound( 0 ); break;
222 case MOVE_LAST
: nIndex
= maSplits
.UpperBound( GetPosCount() ); break;
223 case MOVE_PREV
: nIndex
= maSplits
.UpperBound( GetRulerCursorPos() - 1 ); break;
224 case MOVE_NEXT
: nIndex
= maSplits
.LowerBound( GetRulerCursorPos() + 1 ); break;
227 // added to avoid warnings
230 sal_Int32 nPos
= maSplits
[ nIndex
];
231 if( nPos
!= CSV_POS_INVALID
)
236 void ScCsvRuler::ScrollVertRel( ScMoveMode eDir
)
238 sal_Int32 nLine
= GetFirstVisLine();
241 case MOVE_PREV
: --nLine
; break;
242 case MOVE_NEXT
: ++nLine
; break;
243 case MOVE_PREVPAGE
: nLine
-= GetVisLineCount() - 1; break;
244 case MOVE_NEXTPAGE
: nLine
+= GetVisLineCount() - 1; break;
247 // added to avoid warnings
250 Execute( CSVCMD_SETLINEOFFSET
, nLine
);
254 // split handling -------------------------------------------------------------
256 sal_Int32
ScCsvRuler::GetNoScrollPos( sal_Int32 nPos
) const
258 sal_Int32 nNewPos
= nPos
;
259 if( nNewPos
!= CSV_POS_INVALID
)
261 if( nNewPos
< GetFirstVisPos() + CSV_SCROLL_DIST
)
263 sal_Int32 nScroll
= (GetFirstVisPos() > 0) ? CSV_SCROLL_DIST
: 0;
264 nNewPos
= std::max( nPos
, GetFirstVisPos() + nScroll
);
266 else if( nNewPos
> GetLastVisPos() - CSV_SCROLL_DIST
- 1L )
268 sal_Int32 nScroll
= (GetFirstVisPos() < GetMaxPosOffset()) ? CSV_SCROLL_DIST
: 0;
269 nNewPos
= std::min( nNewPos
, GetLastVisPos() - nScroll
- sal_Int32( 1 ) );
275 void ScCsvRuler::InsertSplit( sal_Int32 nPos
)
277 if( maSplits
.Insert( nPos
) )
279 ImplDrawSplit( nPos
);
284 void ScCsvRuler::RemoveSplit( sal_Int32 nPos
)
286 if( maSplits
.Remove( nPos
) )
288 ImplEraseSplit( nPos
);
293 void ScCsvRuler::MoveSplit( sal_Int32 nPos
, sal_Int32 nNewPos
)
295 bool bRemove
= maSplits
.Remove( nPos
);
296 bool bInsert
= maSplits
.Insert( nNewPos
);
297 if( bRemove
|| bInsert
)
299 ImplEraseSplit( nPos
);
300 ImplDrawSplit( nNewPos
);
305 void ScCsvRuler::RemoveAllSplits()
311 sal_Int32
ScCsvRuler::FindEmptyPos( sal_Int32 nPos
, ScMoveMode eDir
) const
313 sal_Int32 nNewPos
= nPos
;
314 if( nNewPos
!= CSV_POS_INVALID
)
319 nNewPos
= std::min( nPos
, FindEmptyPos( 0, MOVE_NEXT
) );
322 nNewPos
= std::max( nPos
, FindEmptyPos( GetPosCount(), MOVE_PREV
) );
325 while( HasSplit( --nNewPos
) ) ;
328 while( HasSplit( ++nNewPos
) ) ;
332 // added to avoid warnings
336 return IsValidSplitPos( nNewPos
) ? nNewPos
: CSV_POS_INVALID
;
339 void ScCsvRuler::MoveCurrSplit( sal_Int32 nNewPos
)
342 Execute( CSVCMD_MOVESPLIT
, GetRulerCursorPos(), nNewPos
);
343 MoveCursor( nNewPos
);
347 void ScCsvRuler::MoveCurrSplitRel( ScMoveMode eDir
)
349 if( HasSplit( GetRulerCursorPos() ) )
351 sal_Int32 nNewPos
= FindEmptyPos( GetRulerCursorPos(), eDir
);
352 if( nNewPos
!= CSV_POS_INVALID
)
353 MoveCurrSplit( nNewPos
);
358 // event handling -------------------------------------------------------------
360 void ScCsvRuler::Resize()
362 ScCsvControl::Resize();
367 void ScCsvRuler::GetFocus()
369 ScCsvControl::GetFocus();
371 if( GetRulerCursorPos() == CSV_POS_INVALID
)
372 MoveCursor( GetNoScrollPos( mnPosCursorLast
) );
376 void ScCsvRuler::LoseFocus()
378 ScCsvControl::LoseFocus();
379 mnPosCursorLast
= GetRulerCursorPos();
380 MoveCursor( CSV_POS_INVALID
);
383 void ScCsvRuler::DataChanged( const DataChangedEvent
& rDCEvt
)
385 if( (rDCEvt
.GetType() == DATACHANGED_SETTINGS
) && (rDCEvt
.GetFlags() & SETTINGS_STYLE
) )
390 ScCsvControl::DataChanged( rDCEvt
);
393 void ScCsvRuler::MouseButtonDown( const MouseEvent
& rMEvt
)
400 sal_Int32 nPos
= GetPosFromX( rMEvt
.GetPosPixel().X() );
401 if( IsVisibleSplitPos( nPos
) )
402 StartMouseTracking( nPos
);
403 ImplSetMousePointer( nPos
);
408 void ScCsvRuler::MouseMove( const MouseEvent
& rMEvt
)
410 if( !rMEvt
.IsModifierChanged() )
412 sal_Int32 nPos
= GetPosFromX( rMEvt
.GetPosPixel().X() );
415 // on mouse tracking: keep position valid
416 nPos
= std::max( std::min( nPos
, GetPosCount() - sal_Int32( 1 ) ), sal_Int32( 1 ) );
417 MoveMouseTracking( nPos
);
422 Rectangle
aRect( aPoint
, maWinSize
);
423 if( !IsVisibleSplitPos( nPos
) || !aRect
.IsInside( rMEvt
.GetPosPixel() ) )
424 // if focused, keep old cursor position for key input
425 nPos
= HasFocus() ? GetRulerCursorPos() : CSV_POS_INVALID
;
426 MoveCursor( nPos
, false );
428 ImplSetMousePointer( nPos
);
432 void ScCsvRuler::Tracking( const TrackingEvent
& rTEvt
)
434 if( rTEvt
.IsTrackingEnded() || rTEvt
.IsTrackingRepeat() )
435 MouseMove( rTEvt
.GetMouseEvent() );
436 if( rTEvt
.IsTrackingEnded() )
437 EndMouseTracking( !rTEvt
.IsTrackingCanceled() );
440 void ScCsvRuler::KeyInput( const KeyEvent
& rKEvt
)
442 const KeyCode
& rKCode
= rKEvt
.GetKeyCode();
443 sal_uInt16 nCode
= rKCode
.GetCode();
444 bool bNoMod
= !rKCode
.GetModifier();
445 bool bShift
= (rKCode
.GetModifier() == KEY_SHIFT
);
446 bool bJump
= (rKCode
.GetModifier() == KEY_MOD1
);
447 bool bMove
= (rKCode
.GetModifier() == (KEY_MOD1
| KEY_SHIFT
));
449 ScMoveMode eHDir
= GetHorzDirection( nCode
, true );
450 ScMoveMode eVDir
= GetVertDirection( nCode
, false );
454 if( eHDir
!= MOVE_NONE
)
455 MoveCursorRel( eHDir
);
456 else if( eVDir
!= MOVE_NONE
)
457 ScrollVertRel( eVDir
);
460 case KEY_SPACE
: Execute( CSVCMD_TOGGLESPLIT
, GetRulerCursorPos() ); break;
461 case KEY_INSERT
: Execute( CSVCMD_INSERTSPLIT
, GetRulerCursorPos() ); break;
462 case KEY_DELETE
: Execute( CSVCMD_REMOVESPLIT
, GetRulerCursorPos() ); break;
465 else if( bJump
&& (eHDir
!= MOVE_NONE
) )
466 MoveCursorToSplit( eHDir
);
467 else if( bMove
&& (eHDir
!= MOVE_NONE
) )
468 MoveCurrSplitRel( eHDir
);
469 else if( bShift
&& (nCode
== KEY_DELETE
) )
470 Execute( CSVCMD_REMOVEALLSPLITS
);
472 if( rKCode
.GetGroup() != KEYGROUP_CURSOR
)
473 ScCsvControl::KeyInput( rKEvt
);
476 void ScCsvRuler::StartMouseTracking( sal_Int32 nPos
)
478 mnPosMTStart
= mnPosMTCurr
= nPos
;
479 mbPosMTMoved
= false;
480 maOldSplits
= maSplits
;
481 Execute( CSVCMD_INSERTSPLIT
, nPos
);
482 if( HasSplit( nPos
) )
483 StartTracking( STARTTRACK_BUTTONREPEAT
);
486 void ScCsvRuler::MoveMouseTracking( sal_Int32 nPos
)
488 if( mnPosMTCurr
!= nPos
)
492 if( (mnPosMTCurr
!= mnPosMTStart
) && maOldSplits
.HasSplit( mnPosMTCurr
) )
493 Execute( CSVCMD_INSERTSPLIT
, nPos
);
495 Execute( CSVCMD_MOVESPLIT
, mnPosMTCurr
, nPos
);
502 void ScCsvRuler::EndMouseTracking( bool bApply
)
504 if( bApply
) // tracking finished successfully
506 // remove on simple click on an existing split
507 if( (mnPosMTCurr
== mnPosMTStart
) && maOldSplits
.HasSplit( mnPosMTCurr
) && !mbPosMTMoved
)
508 Execute( CSVCMD_REMOVESPLIT
, mnPosMTCurr
);
510 else // tracking cancelled
512 MoveCursor( mnPosMTStart
);
513 // move split to origin
514 if( maOldSplits
.HasSplit( mnPosMTStart
) )
515 MoveMouseTracking( mnPosMTStart
);
516 // remove temporarily inserted split
517 else if( !maOldSplits
.HasSplit( mnPosMTCurr
) )
518 Execute( CSVCMD_REMOVESPLIT
, mnPosMTCurr
);
520 mnPosMTStart
= CSV_POS_INVALID
;
524 // painting -------------------------------------------------------------------
526 void ScCsvRuler::Paint( const Rectangle
& )
531 void ScCsvRuler::ImplRedraw()
541 DrawOutDev( Point(), maWinSize
, Point(), maWinSize
, maRulerDev
);
542 ImplDrawTrackingRect();
546 void ScCsvRuler::ImplDrawArea( sal_Int32 nPosX
, sal_Int32 nWidth
)
548 maBackgrDev
.SetLineColor();
549 Rectangle
aRect( Point( nPosX
, 0 ), Size( nWidth
, GetHeight() ) );
550 maBackgrDev
.SetFillColor( maBackColor
);
551 maBackgrDev
.DrawRect( aRect
);
553 aRect
= maActiveRect
;
554 aRect
.Left() = std::max( GetFirstX(), nPosX
);
555 aRect
.Right() = std::min( std::min( GetX( GetPosCount() ), GetLastX() ), nPosX
+ nWidth
- sal_Int32( 1 ) );
556 if( aRect
.Left() <= aRect
.Right() )
558 maBackgrDev
.SetFillColor( maActiveColor
);
559 maBackgrDev
.DrawRect( aRect
);
562 maBackgrDev
.SetLineColor( maTextColor
);
563 sal_Int32 nY
= GetHeight() - 1;
564 maBackgrDev
.DrawLine( Point( nPosX
, nY
), Point( nPosX
+ nWidth
- 1, nY
) );
567 void ScCsvRuler::ImplDrawBackgrDev()
569 ImplDrawArea( 0, GetWidth() );
572 maBackgrDev
.SetLineColor( maTextColor
);
573 maBackgrDev
.SetFillColor();
576 sal_Int32 nFirstPos
= std::max( GetPosFromX( 0 ) - (sal_Int32
)(1L), (sal_Int32
)(0L) );
577 sal_Int32 nLastPos
= GetPosFromX( GetWidth() );
578 sal_Int32 nY
= (maActiveRect
.Top() + maActiveRect
.Bottom()) / 2;
579 for( nPos
= nFirstPos
; nPos
<= nLastPos
; ++nPos
)
581 sal_Int32 nX
= GetX( nPos
);
583 maBackgrDev
.DrawPixel( Point( nX
, nY
) );
585 maBackgrDev
.DrawLine( Point( nX
, nY
- 1 ), Point( nX
, nY
+ 1 ) );
589 maBackgrDev
.SetTextColor( maTextColor
);
590 maBackgrDev
.SetTextFillColor();
591 for( nPos
= ((nFirstPos
+ 9) / 10) * 10; nPos
<= nLastPos
; nPos
+= 10 )
593 String
aText( OUString::number( nPos
) );
594 sal_Int32 nTextWidth
= maBackgrDev
.GetTextWidth( aText
);
595 sal_Int32 nTextX
= GetX( nPos
) - nTextWidth
/ 2;
596 ImplDrawArea( nTextX
- 1, nTextWidth
+ 2 );
597 maBackgrDev
.DrawText( Point( nTextX
, maActiveRect
.Top() ), aText
);
601 void ScCsvRuler::ImplDrawSplit( sal_Int32 nPos
)
603 if( IsVisibleSplitPos( nPos
) )
605 Point
aPos( GetX( nPos
) - mnSplitSize
/ 2, GetHeight() - mnSplitSize
- 2 );
606 Size
aSize( mnSplitSize
, mnSplitSize
);
607 maRulerDev
.SetLineColor( maTextColor
);
608 maRulerDev
.SetFillColor( maSplitColor
);
609 maRulerDev
.DrawEllipse( Rectangle( aPos
, aSize
) );
610 maRulerDev
.DrawPixel( Point( GetX( nPos
), GetHeight() - 2 ) );
614 void ScCsvRuler::ImplEraseSplit( sal_Int32 nPos
)
616 if( IsVisibleSplitPos( nPos
) )
618 ImplInvertCursor( GetRulerCursorPos() );
619 Point
aPos( GetX( nPos
) - mnSplitSize
/ 2, 0 );
620 Size
aSize( mnSplitSize
, GetHeight() );
621 maRulerDev
.DrawOutDev( aPos
, aSize
, aPos
, aSize
, maBackgrDev
);
622 ImplInvertCursor( GetRulerCursorPos() );
626 void ScCsvRuler::ImplDrawRulerDev()
628 maRulerDev
.DrawOutDev( Point(), maWinSize
, Point(), maWinSize
, maBackgrDev
);
629 ImplInvertCursor( GetRulerCursorPos() );
631 sal_uInt32 nFirst
= maSplits
.LowerBound( GetFirstVisPos() );
632 sal_uInt32 nLast
= maSplits
.UpperBound( GetLastVisPos() );
633 if( (nFirst
!= CSV_VEC_NOTFOUND
) && (nLast
!= CSV_VEC_NOTFOUND
) )
634 for( sal_uInt32 nIndex
= nFirst
; nIndex
<= nLast
; ++nIndex
)
635 ImplDrawSplit( GetSplitPos( nIndex
) );
638 void ScCsvRuler::ImplInvertCursor( sal_Int32 nPos
)
640 if( IsVisibleSplitPos( nPos
) )
642 ImplInvertRect( maRulerDev
, Rectangle( Point( GetX( nPos
) - 1, 0 ), Size( 3, GetHeight() - 1 ) ) );
643 if( HasSplit( nPos
) )
644 ImplDrawSplit( nPos
);
648 void ScCsvRuler::ImplDrawTrackingRect()
651 InvertTracking( Rectangle( 0, 0, GetWidth() - 1, GetHeight() - 2 ),
652 SHOWTRACK_SMALL
| SHOWTRACK_WINDOW
);
655 void ScCsvRuler::ImplSetMousePointer( sal_Int32 nPos
)
657 SetPointer( Pointer( HasSplit( nPos
) ? POINTER_HSPLIT
: POINTER_ARROW
) );
661 // accessibility ==============================================================
663 ScAccessibleCsvControl
* ScCsvRuler::ImplCreateAccessible()
665 return new ScAccessibleCsvRuler( *this );
669 // ============================================================================
671 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */