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 .
21 #include <o3tl/numeric.hxx>
22 #include <svtools/brwhead.hxx>
23 #include <svtools/scrolladaptor.hxx>
25 #include <vcl/commandevent.hxx>
26 #include <vcl/help.hxx>
27 #include <vcl/settings.hxx>
28 #include <vcl/ptrstyle.hxx>
29 #include <tools/debug.hxx>
30 #include <tools/fract.hxx>
32 void ButtonFrame::Draw( OutputDevice
& rDev
)
34 Color aOldFillColor
= rDev
.GetFillColor();
35 Color aOldLineColor
= rDev
.GetLineColor();
37 const StyleSettings
&rSettings
= rDev
.GetSettings().GetStyleSettings();
38 Color
aColLight( rSettings
.GetLightColor() );
39 Color
aColShadow( rSettings
.GetShadowColor() );
40 Color
aColFace( rSettings
.GetFaceColor() );
42 rDev
.SetLineColor( aColFace
);
43 rDev
.SetFillColor( aColFace
);
44 rDev
.DrawRect( aRect
);
46 if( rDev
.GetOutDevType() != OUTDEV_WINDOW
)
48 rDev
.SetLineColor( aColLight
);
49 rDev
.DrawLine( aRect
.TopLeft(), Point( aRect
.Right(), aRect
.Top() ) );
50 rDev
.DrawLine( aRect
.TopLeft(), Point( aRect
.Left(), aRect
.Bottom() - 1 ) );
51 rDev
.SetLineColor( aColShadow
);
52 rDev
.DrawLine( aRect
.BottomRight(), Point( aRect
.Right(), aRect
.Top() ) );
53 rDev
.DrawLine( aRect
.BottomRight(), Point( aRect
.Left(), aRect
.Bottom() ) );
56 if ( !aText
.isEmpty() )
58 OUString aVal
= rDev
.GetEllipsisString(aText
,aInnerRect
.GetWidth() - 2*MIN_COLUMNWIDTH
);
60 vcl::Font
aFont( rDev
.GetFont() );
61 bool bOldTransp
= aFont
.IsTransparent();
64 aFont
.SetTransparent( true );
65 rDev
.SetFont( aFont
);
68 Color aOldColor
= rDev
.GetTextColor();
70 rDev
.SetTextColor(rSettings
.GetDisableColor());
73 ( aInnerRect
.Left() + aInnerRect
.Right() ) / 2 - ( rDev
.GetTextWidth(aVal
) / 2 ),
74 aInnerRect
.Top() ), aVal
);
79 aFont
.SetTransparent(false);
80 rDev
.SetFont( aFont
);
83 rDev
.SetTextColor(aOldColor
);
86 rDev
.SetLineColor( aOldLineColor
);
87 rDev
.SetFillColor( aOldFillColor
);
90 BrowserColumn::BrowserColumn( sal_uInt16 nItemId
,
91 OUString aTitle
, sal_uLong nWidthPixel
, const Fraction
& rCurrentZoom
)
93 _nWidth( nWidthPixel
),
94 _aTitle(std::move( aTitle
)),
97 double n
= static_cast<double>(_nWidth
);
98 n
*= static_cast<double>(rCurrentZoom
.GetDenominator());
99 if (!rCurrentZoom
.GetNumerator())
100 throw o3tl::divide_by_zero();
101 n
/= static_cast<double>(rCurrentZoom
.GetNumerator());
102 _nOriginalWidth
= n
>0 ? static_cast<tools::Long
>(n
+0.5) : -static_cast<tools::Long
>(-n
+0.5);
105 BrowserColumn::~BrowserColumn()
109 void BrowserColumn::SetWidth(sal_uLong nNewWidthPixel
, const Fraction
& rCurrentZoom
)
111 _nWidth
= nNewWidthPixel
;
112 // Avoid overflow when called with LONG_MAX from
113 // BrowseBox::AutoSizeLastColumn:
114 if (_nWidth
== LONG_MAX
)
116 _nOriginalWidth
= _nWidth
;
120 double n
= static_cast<double>(_nWidth
);
121 n
*= static_cast<double>(rCurrentZoom
.GetDenominator());
122 if (!rCurrentZoom
.GetNumerator())
123 throw o3tl::divide_by_zero();
124 n
/= static_cast<double>(rCurrentZoom
.GetNumerator());
125 _nOriginalWidth
= n
>0 ? static_cast<tools::Long
>(n
+0.5) : -static_cast<tools::Long
>(-n
+0.5);
129 void BrowserColumn::Draw( BrowseBox
const & rBox
, OutputDevice
& rDev
, const Point
& rPos
)
133 // paint handle column
134 ButtonFrame( rPos
, Size( Width()-1, rBox
.GetDataRowHeight()-1 ),
135 "", false ).Draw( rDev
);
136 Color aOldLineColor
= rDev
.GetLineColor();
137 rDev
.SetLineColor( COL_BLACK
);
139 Point( rPos
.X(), rPos
.Y()+rBox
.GetDataRowHeight()-1 ),
140 Point( rPos
.X() + Width() - 1, rPos
.Y()+rBox
.GetDataRowHeight()-1 ) );
142 Point( rPos
.X() + Width() - 1, rPos
.Y() ),
143 Point( rPos
.X() + Width() - 1, rPos
.Y()+rBox
.GetDataRowHeight()-1 ) );
144 rDev
.SetLineColor( aOldLineColor
);
146 rBox
.DoPaintField( rDev
,
148 Point( rPos
.X() + 2, rPos
.Y() + 2 ),
149 Size( Width()-1, rBox
.GetDataRowHeight()-1 ) ),
151 BrowseBox::BrowserColumnAccess() );
156 tools::Long nWidth
= Width() == LONG_MAX
? rBox
.GetDataWindow().GetSizePixel().Width() : Width();
158 rBox
.DoPaintField( rDev
,
160 Point( rPos
.X() + MIN_COLUMNWIDTH
, rPos
.Y() ),
161 Size( nWidth
-2*MIN_COLUMNWIDTH
, rBox
.GetDataRowHeight()-1 ) ),
163 BrowseBox::BrowserColumnAccess() );
168 void BrowserColumn::ZoomChanged(const Fraction
& rNewZoom
)
170 double n(_nOriginalWidth
* rNewZoom
);
171 _nWidth
= n
>0 ? static_cast<tools::Long
>(n
+0.5) : -static_cast<tools::Long
>(-n
+0.5);
175 BrowserDataWin::BrowserDataWin( BrowseBox
* pParent
)
176 :Control( pParent
, WB_CLIPCHILDREN
)
177 ,DragSourceHelper( this )
178 ,DropTargetHelper( this )
179 ,pHeaderBar( nullptr )
181 ,aMouseTimer("BrowserDataWin aMouseTimer")
189 ,bAutoSizeLastCol(false)
190 ,bResizeOnPaint( false )
191 ,bUpdateOnUnlock( false )
192 ,bInUpdateScrollbars( false )
193 ,bHadRecursion( false )
194 ,bCallingDropCallback( false )
197 ,m_nDragRowDividerLimit( 0 )
198 ,m_nDragRowDividerOffset( 0 )
200 aMouseTimer
.SetInvokeHandler( LINK( this, BrowserDataWin
, RepeatedMouseMove
) );
201 aMouseTimer
.SetTimeout( 100 );
204 BrowserDataWin::~BrowserDataWin()
209 void BrowserDataWin::dispose()
213 aInvalidRegion
.clear();
215 DragSourceHelper::dispose();
216 DropTargetHelper::dispose();
220 void BrowserDataWin::LeaveUpdateLock()
222 if ( !--nUpdateLock
)
224 DoOutstandingInvalidations();
225 if (bUpdateOnUnlock
)
227 Control::PaintImmediately();
228 bUpdateOnUnlock
= false;
233 void InitSettings_Impl(vcl::Window
* pWin
)
235 const StyleSettings
& rStyleSettings
= pWin
->GetSettings().GetStyleSettings();
237 pWin
->ApplyControlFont(*pWin
->GetOutDev(), rStyleSettings
.GetFieldFont());
238 pWin
->ApplyControlForeground(*pWin
->GetOutDev(), rStyleSettings
.GetWindowTextColor());
239 pWin
->ApplyControlBackground(*pWin
->GetOutDev(), rStyleSettings
.GetWindowColor());
243 void BrowserDataWin::Update()
246 Control::PaintImmediately();
248 bUpdateOnUnlock
= true;
252 void BrowserDataWin::DataChanged( const DataChangedEvent
& rDCEvt
)
254 if ( (rDCEvt
.GetType() == DataChangedEventType::SETTINGS
) &&
255 (rDCEvt
.GetFlags() & AllSettingsFlags::STYLE
) )
257 InitSettings_Impl(this);
259 InitSettings_Impl(GetParent());
260 GetParent()->Invalidate();
261 GetParent()->Resize();
264 Control::DataChanged( rDCEvt
);
268 void BrowserDataWin::Paint(vcl::RenderContext
& rRenderContext
, const tools::Rectangle
& rRect
)
270 if (!nUpdateLock
&& GetUpdateMode())
274 aInvalidRegion
.emplace_back(rRect
);
278 GetParent()->PaintData(*this, rRenderContext
, rRect
);
280 DoOutstandingInvalidations();
284 aInvalidRegion
.emplace_back(rRect
);
288 BrowseBox
* BrowserDataWin::GetParent() const
290 return static_cast<BrowseBox
*>(Window::GetParent());
293 BrowseEvent
BrowserDataWin::CreateBrowseEvent( const Point
& rPosPixel
)
295 BrowseBox
*pBox
= GetParent();
297 // seek to row under mouse
298 sal_Int32 nRelRow
= rPosPixel
.Y() < 0
300 : rPosPixel
.Y() / pBox
->GetDataRowHeight();
301 sal_Int32 nRow
= nRelRow
< 0 ? -1 : nRelRow
+ pBox
->nTopRow
;
303 // find column under mouse
304 tools::Long nMouseX
= rPosPixel
.X();
305 tools::Long nColX
= 0;
308 nCol
< pBox
->mvCols
.size() && nColX
< GetSizePixel().Width();
310 if ( pBox
->mvCols
[ nCol
]->IsFrozen() || nCol
>= pBox
->nFirstCol
)
312 nColX
+= pBox
->mvCols
[ nCol
]->Width();
313 if ( nMouseX
< nColX
)
316 sal_uInt16 nColId
= BROWSER_INVALIDID
;
317 if ( nCol
< pBox
->mvCols
.size() )
318 nColId
= pBox
->mvCols
[ nCol
]->GetId();
320 // compute the field rectangle and field relative MouseEvent
321 tools::Rectangle aFieldRect
;
322 if ( nCol
< pBox
->mvCols
.size() )
324 nColX
-= pBox
->mvCols
[ nCol
]->Width();
325 aFieldRect
= tools::Rectangle(
326 Point( nColX
, nRelRow
* pBox
->GetDataRowHeight() ),
327 Size( pBox
->mvCols
[ nCol
]->Width(),
328 pBox
->GetDataRowHeight() ) );
331 // assemble and return the BrowseEvent
332 return BrowseEvent( this, nRow
, nCol
, nColId
, aFieldRect
);
336 sal_Int8
BrowserDataWin::AcceptDrop( const AcceptDropEvent
& _rEvt
)
338 bCallingDropCallback
= true;
339 sal_Int8 nReturn
= GetParent()->AcceptDrop( BrowserAcceptDropEvent( this, _rEvt
) );
340 bCallingDropCallback
= false;
345 sal_Int8
BrowserDataWin::ExecuteDrop( const ExecuteDropEvent
& _rEvt
)
347 bCallingDropCallback
= true;
348 sal_Int8 nReturn
= GetParent()->ExecuteDrop( BrowserExecuteDropEvent( this, _rEvt
) );
349 bCallingDropCallback
= false;
354 void BrowserDataWin::StartDrag( sal_Int8 _nAction
, const Point
& _rPosPixel
)
356 if ( !GetParent()->bRowDividerDrag
)
358 Point
aEventPos( _rPosPixel
);
359 aEventPos
.AdjustY(GetParent()->GetTitleHeight() );
360 GetParent()->StartDrag( _nAction
, aEventPos
);
365 void BrowserDataWin::Command( const CommandEvent
& rEvt
)
367 // scroll mouse event?
368 BrowseBox
*pBox
= GetParent();
369 if ( ( (rEvt
.GetCommand() == CommandEventId::Wheel
) ||
370 (rEvt
.GetCommand() == CommandEventId::StartAutoScroll
) ||
371 (rEvt
.GetCommand() == CommandEventId::AutoScroll
) ) &&
372 ( HandleScrollCommand( rEvt
, pBox
->aHScroll
.get(), pBox
->pVScroll
) ) )
375 Point
aEventPos( rEvt
.GetMousePosPixel() );
376 sal_Int32 nRow
= pBox
->GetRowAtYPosPixel( aEventPos
.Y(), false);
377 MouseEvent
aMouseEvt( aEventPos
, 1, MouseEventModifiers::SELECT
, MOUSE_LEFT
);
378 if ( CommandEventId::ContextMenu
== rEvt
.GetCommand() && rEvt
.IsMouseEvent() &&
379 nRow
< pBox
->GetRowCount() && !pBox
->IsRowSelected(nRow
) )
382 MouseButtonDown( aMouseEvt
);
385 MouseButtonUp( aMouseEvt
);
391 aEventPos
.AdjustY(GetParent()->GetTitleHeight() );
392 CommandEvent
aEvt( aEventPos
, rEvt
.GetCommand(),
393 rEvt
.IsMouseEvent(), rEvt
.GetEventData() );
395 GetParent()->Command( aEvt
);
400 if ( CommandEventId::StartDrag
== rEvt
.GetCommand() )
401 MouseButtonUp( aMouseEvt
);
403 Control::Command( rEvt
);
407 bool BrowserDataWin::ImplRowDividerHitTest( const BrowserMouseEvent
& _rEvent
) const
409 if ( ! ( GetParent()->IsInteractiveRowHeightEnabled()
410 && ( _rEvent
.GetRow() >= 0 )
411 && ( _rEvent
.GetRow() < GetParent()->GetRowCount() )
412 && ( _rEvent
.GetColumnId() == BrowseBox::HandleColumnId
)
417 tools::Long nDividerDistance
= GetParent()->GetDataRowHeight() - ( _rEvent
.GetPosPixel().Y() % GetParent()->GetDataRowHeight() );
418 return ( nDividerDistance
<= 4 );
422 void BrowserDataWin::MouseButtonDown( const MouseEvent
& rEvt
)
424 aLastMousePos
= OutputToScreenPixel( rEvt
.GetPosPixel() );
426 BrowserMouseEvent
aBrowserEvent( this, rEvt
);
427 if ( ( aBrowserEvent
.GetClicks() == 1 ) && ImplRowDividerHitTest( aBrowserEvent
) )
429 StartRowDividerDrag( aBrowserEvent
.GetPosPixel() );
433 GetParent()->MouseButtonDown( BrowserMouseEvent( this, rEvt
) );
437 void BrowserDataWin::MouseMove( const MouseEvent
& rEvt
)
439 // avoid pseudo MouseMoves
440 Point aNewPos
= OutputToScreenPixel( rEvt
.GetPosPixel() );
441 if ( aNewPos
== aLastMousePos
)
443 aLastMousePos
= aNewPos
;
445 // transform to a BrowseEvent
446 BrowserMouseEvent
aBrowserEvent( this, rEvt
);
447 GetParent()->MouseMove( aBrowserEvent
);
450 PointerStyle ePointerStyle
= PointerStyle::Arrow
;
451 if ( ImplRowDividerHitTest( aBrowserEvent
) )
452 ePointerStyle
= PointerStyle::VSizeBar
;
453 SetPointer( ePointerStyle
);
455 // dragging out of the visible area?
456 if ( rEvt
.IsLeft() &&
457 ( rEvt
.GetPosPixel().Y() > GetSizePixel().Height() ||
458 rEvt
.GetPosPixel().Y() < 0 ) )
465 // killing old repeat-event
466 if ( aMouseTimer
.IsActive() )
471 IMPL_LINK_NOARG(BrowserDataWin
, RepeatedMouseMove
, Timer
*, void)
473 GetParent()->MouseMove( BrowserMouseEvent( this, aRepeatEvt
) );
476 void BrowserDataWin::MouseButtonUp( const MouseEvent
& rEvt
)
478 // avoid pseudo MouseMoves
479 Point aNewPos
= OutputToScreenPixel( rEvt
.GetPosPixel() );
480 aLastMousePos
= aNewPos
;
482 // simulate a move to the current position
485 // actual button up handling
487 if ( aMouseTimer
.IsActive() )
489 GetParent()->MouseButtonUp( BrowserMouseEvent( this, rEvt
) );
493 void BrowserDataWin::StartRowDividerDrag( const Point
& _rStartPos
)
495 tools::Long nDataRowHeight
= GetParent()->GetDataRowHeight();
496 // the exact separation pos of the two rows
497 tools::Long nDragRowDividerCurrentPos
= _rStartPos
.Y();
498 if ( ( nDragRowDividerCurrentPos
% nDataRowHeight
) > nDataRowHeight
/ 2 )
499 nDragRowDividerCurrentPos
+= nDataRowHeight
;
500 nDragRowDividerCurrentPos
/= nDataRowHeight
;
501 nDragRowDividerCurrentPos
*= nDataRowHeight
;
503 m_nDragRowDividerOffset
= nDragRowDividerCurrentPos
- _rStartPos
.Y();
505 m_nDragRowDividerLimit
= nDragRowDividerCurrentPos
- nDataRowHeight
;
507 GetParent()->bRowDividerDrag
= true;
508 GetParent()->ImplStartTracking();
510 tools::Rectangle
aDragSplitRect( 0, m_nDragRowDividerLimit
, GetOutputSizePixel().Width(), nDragRowDividerCurrentPos
);
511 ShowTracking( aDragSplitRect
);
517 void BrowserDataWin::Tracking( const TrackingEvent
& rTEvt
)
519 if ( !GetParent()->bRowDividerDrag
)
522 Point aMousePos
= rTEvt
.GetMouseEvent().GetPosPixel();
523 // stop resizing at our bottom line
524 if ( aMousePos
.Y() > GetOutputSizePixel().Height() )
525 aMousePos
.setY( GetOutputSizePixel().Height() );
527 if ( rTEvt
.IsTrackingEnded() )
530 GetParent()->bRowDividerDrag
= false;
531 GetParent()->ImplEndTracking();
533 if ( !rTEvt
.IsTrackingCanceled() )
535 tools::Long nNewRowHeight
= aMousePos
.Y() + m_nDragRowDividerOffset
- m_nDragRowDividerLimit
;
537 // care for minimum row height
538 if ( nNewRowHeight
< GetParent()->QueryMinimumRowHeight() )
539 nNewRowHeight
= GetParent()->QueryMinimumRowHeight();
541 GetParent()->SetDataRowHeight( nNewRowHeight
);
542 GetParent()->RowHeightChanged();
547 tools::Long nDragRowDividerCurrentPos
= aMousePos
.Y() + m_nDragRowDividerOffset
;
549 // care for minimum row height
550 if ( nDragRowDividerCurrentPos
< m_nDragRowDividerLimit
+ GetParent()->QueryMinimumRowHeight() )
551 nDragRowDividerCurrentPos
= m_nDragRowDividerLimit
+ GetParent()->QueryMinimumRowHeight();
553 tools::Rectangle
aDragSplitRect( 0, m_nDragRowDividerLimit
, GetOutputSizePixel().Width(), nDragRowDividerCurrentPos
);
554 ShowTracking( aDragSplitRect
);
559 void BrowserDataWin::KeyInput( const KeyEvent
& rEvt
)
561 // pass to parent window
562 if ( !GetParent()->ProcessKey( rEvt
) )
563 Control::KeyInput( rEvt
);
567 void BrowserDataWin::RequestHelp( const HelpEvent
& rHEvt
)
569 GetParent()->RequestHelp( rHEvt
);
573 BrowseEvent::BrowseEvent( vcl::Window
* pWindow
,
574 sal_Int32 nAbsRow
, sal_uInt16 nColumn
, sal_uInt16 nColumnId
,
575 const tools::Rectangle
& rRect
):
585 BrowserMouseEvent::BrowserMouseEvent( BrowserDataWin
*pWindow
,
586 const MouseEvent
& rEvt
):
588 BrowseEvent( pWindow
->CreateBrowseEvent( rEvt
.GetPosPixel() ) )
593 BrowserMouseEvent::BrowserMouseEvent( vcl::Window
*pWindow
, const MouseEvent
& rEvt
,
594 sal_Int32 nAbsRow
, sal_uInt16 nColumn
, sal_uInt16 nColumnId
,
595 const tools::Rectangle
& rRect
):
597 BrowseEvent( pWindow
, nAbsRow
, nColumn
, nColumnId
, rRect
)
602 BrowserAcceptDropEvent::BrowserAcceptDropEvent( BrowserDataWin
*pWindow
, const AcceptDropEvent
& rEvt
)
603 :AcceptDropEvent(rEvt
)
604 ,BrowseEvent( pWindow
->CreateBrowseEvent( rEvt
.maPosPixel
) )
609 BrowserExecuteDropEvent::BrowserExecuteDropEvent( BrowserDataWin
*pWindow
, const ExecuteDropEvent
& rEvt
)
610 :ExecuteDropEvent(rEvt
)
611 ,BrowseEvent( pWindow
->CreateBrowseEvent( rEvt
.maPosPixel
) )
616 void BrowserDataWin::SetUpdateMode( bool bMode
)
618 DBG_ASSERT( !bUpdateMode
|| aInvalidRegion
.empty(), "invalid region not empty" );
619 if ( bMode
== bUpdateMode
)
624 DoOutstandingInvalidations();
628 void BrowserDataWin::DoOutstandingInvalidations()
630 for (const auto& rRect
: aInvalidRegion
)
631 Control::Invalidate( rRect
);
632 aInvalidRegion
.clear();
635 void BrowserDataWin::Invalidate( InvalidateFlags nFlags
)
637 if ( !GetUpdateMode() )
639 aInvalidRegion
.clear();
640 aInvalidRegion
.emplace_back( Point( 0, 0 ), GetOutputSizePixel() );
643 Window::Invalidate( nFlags
);
647 void BrowserDataWin::Invalidate( const tools::Rectangle
& rRect
, InvalidateFlags nFlags
)
649 if ( !GetUpdateMode() )
650 aInvalidRegion
.emplace_back( rRect
);
652 Window::Invalidate( rRect
, nFlags
);
655 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */