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 // coverity[ tainted_data_return : FALSE ] version 2023.12.2
59 OUString aVal
= rDev
.GetEllipsisString(aText
,aInnerRect
.GetWidth() - 2*MIN_COLUMNWIDTH
);
61 vcl::Font
aFont( rDev
.GetFont() );
62 bool bOldTransp
= aFont
.IsTransparent();
65 aFont
.SetTransparent( true );
66 rDev
.SetFont( aFont
);
69 Color aOldColor
= rDev
.GetTextColor();
71 rDev
.SetTextColor(rSettings
.GetDisableColor());
74 ( aInnerRect
.Left() + aInnerRect
.Right() ) / 2 - ( rDev
.GetTextWidth(aVal
) / 2 ),
75 aInnerRect
.Top() ), aVal
);
80 aFont
.SetTransparent(false);
81 rDev
.SetFont( aFont
);
84 rDev
.SetTextColor(aOldColor
);
87 rDev
.SetLineColor( aOldLineColor
);
88 rDev
.SetFillColor( aOldFillColor
);
91 BrowserColumn::BrowserColumn( sal_uInt16 nItemId
,
92 OUString aTitle
, tools::Long nWidthPixel
, const Fraction
& rCurrentZoom
)
94 _nWidth( nWidthPixel
),
95 _aTitle(std::move( aTitle
)),
98 double n
= static_cast<double>(_nWidth
);
99 n
*= static_cast<double>(rCurrentZoom
.GetDenominator());
100 if (!rCurrentZoom
.GetNumerator())
101 throw o3tl::divide_by_zero();
102 n
/= static_cast<double>(rCurrentZoom
.GetNumerator());
103 _nOriginalWidth
= n
>0 ? static_cast<tools::Long
>(n
+0.5) : -static_cast<tools::Long
>(-n
+0.5);
106 BrowserColumn::~BrowserColumn()
110 void BrowserColumn::SetWidth(tools::Long nNewWidthPixel
, const Fraction
& rCurrentZoom
)
112 _nWidth
= nNewWidthPixel
;
113 // Avoid overflow when called with LONG_MAX from
114 // BrowseBox::AutoSizeLastColumn:
115 if (_nWidth
== LONG_MAX
)
117 _nOriginalWidth
= _nWidth
;
121 double n
= static_cast<double>(_nWidth
);
122 n
*= static_cast<double>(rCurrentZoom
.GetDenominator());
123 if (!rCurrentZoom
.GetNumerator())
124 throw o3tl::divide_by_zero();
125 n
/= static_cast<double>(rCurrentZoom
.GetNumerator());
126 _nOriginalWidth
= n
>0 ? static_cast<tools::Long
>(n
+0.5) : -static_cast<tools::Long
>(-n
+0.5);
130 void BrowserColumn::Draw( BrowseBox
const & rBox
, OutputDevice
& rDev
, const Point
& rPos
)
134 // paint handle column
135 ButtonFrame( rPos
, Size( Width()-1, rBox
.GetDataRowHeight()-1 ),
136 u
""_ustr
, false ).Draw( rDev
);
137 Color aOldLineColor
= rDev
.GetLineColor();
138 rDev
.SetLineColor( COL_BLACK
);
140 Point( rPos
.X(), rPos
.Y()+rBox
.GetDataRowHeight()-1 ),
141 Point( rPos
.X() + Width() - 1, rPos
.Y()+rBox
.GetDataRowHeight()-1 ) );
143 Point( rPos
.X() + Width() - 1, rPos
.Y() ),
144 Point( rPos
.X() + Width() - 1, rPos
.Y()+rBox
.GetDataRowHeight()-1 ) );
145 rDev
.SetLineColor( aOldLineColor
);
147 rBox
.DoPaintField( rDev
,
149 Point( rPos
.X() + 2, rPos
.Y() + 2 ),
150 Size( Width()-1, rBox
.GetDataRowHeight()-1 ) ),
152 BrowseBox::BrowserColumnAccess() );
157 tools::Long nWidth
= Width() == LONG_MAX
? rBox
.GetDataWindow().GetSizePixel().Width() : Width();
159 rBox
.DoPaintField( rDev
,
161 Point( rPos
.X() + MIN_COLUMNWIDTH
, rPos
.Y() ),
162 Size( nWidth
-2*MIN_COLUMNWIDTH
, rBox
.GetDataRowHeight()-1 ) ),
164 BrowseBox::BrowserColumnAccess() );
169 void BrowserColumn::ZoomChanged(const Fraction
& rNewZoom
)
171 double n(_nOriginalWidth
* rNewZoom
);
172 _nWidth
= n
>0 ? static_cast<tools::Long
>(n
+0.5) : -static_cast<tools::Long
>(-n
+0.5);
176 BrowserDataWin::BrowserDataWin( BrowseBox
* pParent
)
177 :Control( pParent
, WB_CLIPCHILDREN
)
178 ,DragSourceHelper( this )
179 ,DropTargetHelper( this )
180 ,pHeaderBar( nullptr )
182 ,aMouseTimer("BrowserDataWin aMouseTimer")
190 ,bAutoSizeLastCol(false)
191 ,bResizeOnPaint( false )
192 ,bUpdateOnUnlock( false )
193 ,bInUpdateScrollbars( false )
194 ,bHadRecursion( false )
195 ,bCallingDropCallback( false )
198 ,m_nDragRowDividerLimit( 0 )
199 ,m_nDragRowDividerOffset( 0 )
201 aMouseTimer
.SetInvokeHandler( LINK( this, BrowserDataWin
, RepeatedMouseMove
) );
202 aMouseTimer
.SetTimeout( 100 );
205 BrowserDataWin::~BrowserDataWin()
210 void BrowserDataWin::dispose()
214 aInvalidRegion
.clear();
216 DragSourceHelper::dispose();
217 DropTargetHelper::dispose();
221 void BrowserDataWin::LeaveUpdateLock()
223 if ( !--nUpdateLock
)
225 DoOutstandingInvalidations();
226 if (bUpdateOnUnlock
)
228 Control::PaintImmediately();
229 bUpdateOnUnlock
= false;
234 void InitSettings_Impl(vcl::Window
* pWin
)
236 const StyleSettings
& rStyleSettings
= pWin
->GetSettings().GetStyleSettings();
238 pWin
->ApplyControlFont(*pWin
->GetOutDev(), rStyleSettings
.GetFieldFont());
239 pWin
->ApplyControlForeground(*pWin
->GetOutDev(), rStyleSettings
.GetWindowTextColor());
240 pWin
->ApplyControlBackground(*pWin
->GetOutDev(), rStyleSettings
.GetWindowColor());
244 void BrowserDataWin::Update()
247 Control::PaintImmediately();
249 bUpdateOnUnlock
= true;
253 void BrowserDataWin::DataChanged( const DataChangedEvent
& rDCEvt
)
255 if ( (rDCEvt
.GetType() == DataChangedEventType::SETTINGS
) &&
256 (rDCEvt
.GetFlags() & AllSettingsFlags::STYLE
) )
258 InitSettings_Impl(this);
260 InitSettings_Impl(GetParent());
261 GetParent()->Invalidate();
262 GetParent()->Resize();
265 Control::DataChanged( rDCEvt
);
269 void BrowserDataWin::Paint(vcl::RenderContext
& rRenderContext
, const tools::Rectangle
& rRect
)
271 if (!nUpdateLock
&& GetUpdateMode())
275 aInvalidRegion
.emplace_back(rRect
);
279 GetParent()->PaintData(*this, rRenderContext
, rRect
);
281 DoOutstandingInvalidations();
285 aInvalidRegion
.emplace_back(rRect
);
289 BrowseBox
* BrowserDataWin::GetParent() const
291 return static_cast<BrowseBox
*>(Window::GetParent());
294 BrowseEvent
BrowserDataWin::CreateBrowseEvent( const Point
& rPosPixel
)
296 BrowseBox
*pBox
= GetParent();
298 // seek to row under mouse
299 sal_Int32 nRelRow
= rPosPixel
.Y() < 0
301 : rPosPixel
.Y() / pBox
->GetDataRowHeight();
302 sal_Int32 nRow
= nRelRow
< 0 ? -1 : nRelRow
+ pBox
->nTopRow
;
304 // find column under mouse
305 tools::Long nMouseX
= rPosPixel
.X();
306 tools::Long nColX
= 0;
309 nCol
< pBox
->mvCols
.size() && nColX
< GetSizePixel().Width();
311 if ( pBox
->mvCols
[ nCol
]->IsFrozen() || nCol
>= pBox
->nFirstCol
)
313 nColX
+= pBox
->mvCols
[ nCol
]->Width();
314 if ( nMouseX
< nColX
)
317 sal_uInt16 nColId
= BROWSER_INVALIDID
;
318 if ( nCol
< pBox
->mvCols
.size() )
319 nColId
= pBox
->mvCols
[ nCol
]->GetId();
321 // compute the field rectangle and field relative MouseEvent
322 tools::Rectangle aFieldRect
;
323 if ( nCol
< pBox
->mvCols
.size() )
325 nColX
-= pBox
->mvCols
[ nCol
]->Width();
326 aFieldRect
= tools::Rectangle(
327 Point( nColX
, nRelRow
* pBox
->GetDataRowHeight() ),
328 Size( pBox
->mvCols
[ nCol
]->Width(),
329 pBox
->GetDataRowHeight() ) );
332 // assemble and return the BrowseEvent
333 return BrowseEvent( this, nRow
, nCol
, nColId
, aFieldRect
);
337 sal_Int8
BrowserDataWin::AcceptDrop( const AcceptDropEvent
& _rEvt
)
339 bCallingDropCallback
= true;
340 sal_Int8 nReturn
= GetParent()->AcceptDrop( BrowserAcceptDropEvent( this, _rEvt
) );
341 bCallingDropCallback
= false;
346 sal_Int8
BrowserDataWin::ExecuteDrop( const ExecuteDropEvent
& _rEvt
)
348 bCallingDropCallback
= true;
349 sal_Int8 nReturn
= GetParent()->ExecuteDrop( BrowserExecuteDropEvent( this, _rEvt
) );
350 bCallingDropCallback
= false;
355 void BrowserDataWin::StartDrag( sal_Int8 _nAction
, const Point
& _rPosPixel
)
357 if ( !GetParent()->bRowDividerDrag
)
359 Point
aEventPos( _rPosPixel
);
360 aEventPos
.AdjustY(GetParent()->GetTitleHeight() );
361 GetParent()->StartDrag( _nAction
, aEventPos
);
366 void BrowserDataWin::Command( const CommandEvent
& rEvt
)
368 // scroll mouse event?
369 BrowseBox
*pBox
= GetParent();
370 if ( ( (rEvt
.GetCommand() == CommandEventId::Wheel
) ||
371 (rEvt
.GetCommand() == CommandEventId::StartAutoScroll
) ||
372 (rEvt
.GetCommand() == CommandEventId::AutoScroll
) ) &&
373 ( HandleScrollCommand( rEvt
, pBox
->aHScroll
.get(), pBox
->pVScroll
) ) )
376 Point
aEventPos( rEvt
.GetMousePosPixel() );
377 sal_Int32 nRow
= pBox
->GetRowAtYPosPixel( aEventPos
.Y(), false);
378 MouseEvent
aMouseEvt( aEventPos
, 1, MouseEventModifiers::SELECT
, MOUSE_LEFT
);
379 if ( CommandEventId::ContextMenu
== rEvt
.GetCommand() && rEvt
.IsMouseEvent() &&
380 nRow
< pBox
->GetRowCount() && !pBox
->IsRowSelected(nRow
) )
383 MouseButtonDown( aMouseEvt
);
386 MouseButtonUp( aMouseEvt
);
392 aEventPos
.AdjustY(GetParent()->GetTitleHeight() );
393 CommandEvent
aEvt( aEventPos
, rEvt
.GetCommand(),
394 rEvt
.IsMouseEvent(), rEvt
.GetEventData() );
396 GetParent()->Command( aEvt
);
401 if ( CommandEventId::StartDrag
== rEvt
.GetCommand() )
402 MouseButtonUp( aMouseEvt
);
404 Control::Command( rEvt
);
408 bool BrowserDataWin::ImplRowDividerHitTest( const BrowserMouseEvent
& _rEvent
) const
410 if ( ! ( GetParent()->IsInteractiveRowHeightEnabled()
411 && ( _rEvent
.GetRow() >= 0 )
412 && ( _rEvent
.GetRow() < GetParent()->GetRowCount() )
413 && ( _rEvent
.GetColumnId() == BrowseBox::HandleColumnId
)
418 tools::Long nDividerDistance
= GetParent()->GetDataRowHeight() - ( _rEvent
.GetPosPixel().Y() % GetParent()->GetDataRowHeight() );
419 return ( nDividerDistance
<= 4 );
423 void BrowserDataWin::MouseButtonDown( const MouseEvent
& rEvt
)
425 aLastMousePos
= OutputToScreenPixel( rEvt
.GetPosPixel() );
427 BrowserMouseEvent
aBrowserEvent( this, rEvt
);
428 if ( ( aBrowserEvent
.GetClicks() == 1 ) && ImplRowDividerHitTest( aBrowserEvent
) )
430 StartRowDividerDrag( aBrowserEvent
.GetPosPixel() );
434 GetParent()->MouseButtonDown( BrowserMouseEvent( this, rEvt
) );
438 void BrowserDataWin::MouseMove( const MouseEvent
& rEvt
)
440 // avoid pseudo MouseMoves
441 Point aNewPos
= OutputToScreenPixel( rEvt
.GetPosPixel() );
442 if ( aNewPos
== aLastMousePos
)
444 aLastMousePos
= aNewPos
;
446 // transform to a BrowseEvent
447 BrowserMouseEvent
aBrowserEvent( this, rEvt
);
448 GetParent()->MouseMove( aBrowserEvent
);
451 PointerStyle ePointerStyle
= PointerStyle::Arrow
;
452 if ( ImplRowDividerHitTest( aBrowserEvent
) )
453 ePointerStyle
= PointerStyle::VSizeBar
;
454 SetPointer( ePointerStyle
);
456 // dragging out of the visible area?
457 if ( rEvt
.IsLeft() &&
458 ( rEvt
.GetPosPixel().Y() > GetSizePixel().Height() ||
459 rEvt
.GetPosPixel().Y() < 0 ) )
466 // killing old repeat-event
467 if ( aMouseTimer
.IsActive() )
472 IMPL_LINK_NOARG(BrowserDataWin
, RepeatedMouseMove
, Timer
*, void)
474 GetParent()->MouseMove( BrowserMouseEvent( this, aRepeatEvt
) );
477 void BrowserDataWin::MouseButtonUp( const MouseEvent
& rEvt
)
479 // avoid pseudo MouseMoves
480 Point aNewPos
= OutputToScreenPixel( rEvt
.GetPosPixel() );
481 aLastMousePos
= aNewPos
;
483 // simulate a move to the current position
486 // actual button up handling
488 if ( aMouseTimer
.IsActive() )
490 GetParent()->MouseButtonUp( BrowserMouseEvent( this, rEvt
) );
494 void BrowserDataWin::StartRowDividerDrag( const Point
& _rStartPos
)
496 tools::Long nDataRowHeight
= GetParent()->GetDataRowHeight();
497 // the exact separation pos of the two rows
498 tools::Long nDragRowDividerCurrentPos
= _rStartPos
.Y();
499 if ( ( nDragRowDividerCurrentPos
% nDataRowHeight
) > nDataRowHeight
/ 2 )
500 nDragRowDividerCurrentPos
+= nDataRowHeight
;
501 nDragRowDividerCurrentPos
/= nDataRowHeight
;
502 nDragRowDividerCurrentPos
*= nDataRowHeight
;
504 m_nDragRowDividerOffset
= nDragRowDividerCurrentPos
- _rStartPos
.Y();
506 m_nDragRowDividerLimit
= nDragRowDividerCurrentPos
- nDataRowHeight
;
508 GetParent()->bRowDividerDrag
= true;
509 GetParent()->ImplStartTracking();
511 tools::Rectangle
aDragSplitRect( 0, m_nDragRowDividerLimit
, GetOutputSizePixel().Width(), nDragRowDividerCurrentPos
);
512 ShowTracking( aDragSplitRect
);
518 void BrowserDataWin::Tracking( const TrackingEvent
& rTEvt
)
520 if ( !GetParent()->bRowDividerDrag
)
523 Point aMousePos
= rTEvt
.GetMouseEvent().GetPosPixel();
524 // stop resizing at our bottom line
525 if ( aMousePos
.Y() > GetOutputSizePixel().Height() )
526 aMousePos
.setY( GetOutputSizePixel().Height() );
528 if ( rTEvt
.IsTrackingEnded() )
531 GetParent()->bRowDividerDrag
= false;
532 GetParent()->ImplEndTracking();
534 if ( !rTEvt
.IsTrackingCanceled() )
536 tools::Long nNewRowHeight
= aMousePos
.Y() + m_nDragRowDividerOffset
- m_nDragRowDividerLimit
;
538 // care for minimum row height
539 if ( nNewRowHeight
< GetParent()->QueryMinimumRowHeight() )
540 nNewRowHeight
= GetParent()->QueryMinimumRowHeight();
542 GetParent()->SetDataRowHeight( nNewRowHeight
);
543 GetParent()->RowHeightChanged();
548 tools::Long nDragRowDividerCurrentPos
= aMousePos
.Y() + m_nDragRowDividerOffset
;
550 // care for minimum row height
551 if ( nDragRowDividerCurrentPos
< m_nDragRowDividerLimit
+ GetParent()->QueryMinimumRowHeight() )
552 nDragRowDividerCurrentPos
= m_nDragRowDividerLimit
+ GetParent()->QueryMinimumRowHeight();
554 tools::Rectangle
aDragSplitRect( 0, m_nDragRowDividerLimit
, GetOutputSizePixel().Width(), nDragRowDividerCurrentPos
);
555 ShowTracking( aDragSplitRect
);
560 void BrowserDataWin::KeyInput( const KeyEvent
& rEvt
)
562 // pass to parent window
563 if ( !GetParent()->ProcessKey( rEvt
) )
564 Control::KeyInput( rEvt
);
568 void BrowserDataWin::RequestHelp( const HelpEvent
& rHEvt
)
570 GetParent()->RequestHelp( rHEvt
);
574 BrowseEvent::BrowseEvent( vcl::Window
* pWindow
,
575 sal_Int32 nAbsRow
, sal_uInt16 nColumn
, sal_uInt16 nColumnId
,
576 const tools::Rectangle
& rRect
):
586 BrowserMouseEvent::BrowserMouseEvent( BrowserDataWin
*pWindow
,
587 const MouseEvent
& rEvt
):
589 BrowseEvent( pWindow
->CreateBrowseEvent( rEvt
.GetPosPixel() ) )
594 BrowserMouseEvent::BrowserMouseEvent( vcl::Window
*pWindow
, const MouseEvent
& rEvt
,
595 sal_Int32 nAbsRow
, sal_uInt16 nColumn
, sal_uInt16 nColumnId
,
596 const tools::Rectangle
& rRect
):
598 BrowseEvent( pWindow
, nAbsRow
, nColumn
, nColumnId
, rRect
)
603 BrowserAcceptDropEvent::BrowserAcceptDropEvent( BrowserDataWin
*pWindow
, const AcceptDropEvent
& rEvt
)
604 :AcceptDropEvent(rEvt
)
605 ,BrowseEvent( pWindow
->CreateBrowseEvent( rEvt
.maPosPixel
) )
610 BrowserExecuteDropEvent::BrowserExecuteDropEvent( BrowserDataWin
*pWindow
, const ExecuteDropEvent
& rEvt
)
611 :ExecuteDropEvent(rEvt
)
612 ,BrowseEvent( pWindow
->CreateBrowseEvent( rEvt
.maPosPixel
) )
617 void BrowserDataWin::SetUpdateMode( bool bMode
)
619 DBG_ASSERT( !bUpdateMode
|| aInvalidRegion
.empty(), "invalid region not empty" );
620 if ( bMode
== bUpdateMode
)
625 DoOutstandingInvalidations();
629 void BrowserDataWin::DoOutstandingInvalidations()
631 for (const auto& rRect
: aInvalidRegion
)
633 vcl::Region
aRegion(rRect
);
634 Control::ImplInvalidate( &aRegion
, InvalidateFlags::NONE
);
636 aInvalidRegion
.clear();
639 void BrowserDataWin::ImplInvalidate( const vcl::Region
* pRegion
, InvalidateFlags nFlags
)
641 if ( !GetUpdateMode() )
643 aInvalidRegion
.clear();
645 aInvalidRegion
.emplace_back( Point( 0, 0 ), GetOutputSizePixel() );
647 aInvalidRegion
.emplace_back( pRegion
->GetBoundRect() );
650 Window::ImplInvalidate( pRegion
, nFlags
);
653 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */