Get the style color and number just once
[LibreOffice.git] / svtools / source / brwbox / datwin.cxx
bloba05c3e3f149cc7871af7b4c601ee08d4baa9a47d
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 "datwin.hxx"
21 #include <o3tl/numeric.hxx>
22 #include <svtools/brwhead.hxx>
23 #include <svtools/scrolladaptor.hxx>
24 #include <utility>
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();
63 if ( !bOldTransp )
65 aFont.SetTransparent( true );
66 rDev.SetFont( aFont );
69 Color aOldColor = rDev.GetTextColor();
70 if (m_bDrawDisabled)
71 rDev.SetTextColor(rSettings.GetDisableColor());
73 rDev.DrawText( Point(
74 ( aInnerRect.Left() + aInnerRect.Right() ) / 2 - ( rDev.GetTextWidth(aVal) / 2 ),
75 aInnerRect.Top() ), aVal );
77 // restore settings
78 if ( !bOldTransp )
80 aFont.SetTransparent(false);
81 rDev.SetFont( aFont );
83 if (m_bDrawDisabled)
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 )
93 : _nId( nItemId ),
94 _nWidth( nWidthPixel ),
95 _aTitle(std::move( aTitle )),
96 _bFrozen( false )
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;
119 else
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 )
132 if ( _nId == 0 )
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 );
139 rDev.DrawLine(
140 Point( rPos.X(), rPos.Y()+rBox.GetDataRowHeight()-1 ),
141 Point( rPos.X() + Width() - 1, rPos.Y()+rBox.GetDataRowHeight()-1 ) );
142 rDev.DrawLine(
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,
148 tools::Rectangle(
149 Point( rPos.X() + 2, rPos.Y() + 2 ),
150 Size( Width()-1, rBox.GetDataRowHeight()-1 ) ),
151 GetId(),
152 BrowseBox::BrowserColumnAccess() );
154 else
156 // paint data column
157 tools::Long nWidth = Width() == LONG_MAX ? rBox.GetDataWindow().GetSizePixel().Width() : Width();
159 rBox.DoPaintField( rDev,
160 tools::Rectangle(
161 Point( rPos.X() + MIN_COLUMNWIDTH, rPos.Y() ),
162 Size( nWidth-2*MIN_COLUMNWIDTH, rBox.GetDataRowHeight()-1 ) ),
163 GetId(),
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 )
181 ,bInDtor( false )
182 ,aMouseTimer("BrowserDataWin aMouseTimer")
183 ,bInPaint( false )
184 ,bInCommand( false )
185 ,bNoHScroll( false )
186 ,bNoVScroll( false )
187 ,bAutoHScroll(false)
188 ,bAutoVScroll(false)
189 ,bUpdateMode( true )
190 ,bAutoSizeLastCol(false)
191 ,bResizeOnPaint( false )
192 ,bUpdateOnUnlock( false )
193 ,bInUpdateScrollbars( false )
194 ,bHadRecursion( false )
195 ,bCallingDropCallback( false )
196 ,nUpdateLock( 0 )
197 ,nCursorHidden( 0 )
198 ,m_nDragRowDividerLimit( 0 )
199 ,m_nDragRowDividerOffset( 0 )
201 aMouseTimer.SetInvokeHandler( LINK( this, BrowserDataWin, RepeatedMouseMove ) );
202 aMouseTimer.SetTimeout( 100 );
205 BrowserDataWin::~BrowserDataWin()
207 disposeOnce();
210 void BrowserDataWin::dispose()
212 bInDtor = true;
214 aInvalidRegion.clear();
215 pHeaderBar.clear();
216 DragSourceHelper::dispose();
217 DropTargetHelper::dispose();
218 Control::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()
246 if ( !nUpdateLock )
247 Control::PaintImmediately();
248 else
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);
259 Invalidate();
260 InitSettings_Impl(GetParent());
261 GetParent()->Invalidate();
262 GetParent()->Resize();
264 else
265 Control::DataChanged( rDCEvt );
269 void BrowserDataWin::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
271 if (!nUpdateLock && GetUpdateMode())
273 if (bInPaint)
275 aInvalidRegion.emplace_back(rRect);
276 return;
278 bInPaint = true;
279 GetParent()->PaintData(*this, rRenderContext, rRect);
280 bInPaint = false;
281 DoOutstandingInvalidations();
283 else
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
300 ? -1
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;
307 size_t nCol;
308 for ( nCol = 0;
309 nCol < pBox->mvCols.size() && nColX < GetSizePixel().Width();
310 ++nCol )
311 if ( pBox->mvCols[ nCol ]->IsFrozen() || nCol >= pBox->nFirstCol )
313 nColX += pBox->mvCols[ nCol ]->Width();
314 if ( nMouseX < nColX )
315 break;
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;
342 return nReturn;
346 sal_Int8 BrowserDataWin::ExecuteDrop( const ExecuteDropEvent& _rEvt )
348 bCallingDropCallback = true;
349 sal_Int8 nReturn = GetParent()->ExecuteDrop( BrowserExecuteDropEvent( this, _rEvt ) );
350 bCallingDropCallback = false;
351 return nReturn;
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 ) ) )
374 return;
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) )
382 bInCommand = true;
383 MouseButtonDown( aMouseEvt );
384 if( bInDtor )
385 return;
386 MouseButtonUp( aMouseEvt );
387 if( bInDtor )
388 return;
389 bInCommand = false;
392 aEventPos.AdjustY(GetParent()->GetTitleHeight() );
393 CommandEvent aEvt( aEventPos, rEvt.GetCommand(),
394 rEvt.IsMouseEvent(), rEvt.GetEventData() );
395 bInCommand = true;
396 GetParent()->Command( aEvt );
397 if( bInDtor )
398 return;
399 bInCommand = false;
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 )
416 return false;
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() );
431 return;
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 )
443 return;
444 aLastMousePos = aNewPos;
446 // transform to a BrowseEvent
447 BrowserMouseEvent aBrowserEvent( this, rEvt );
448 GetParent()->MouseMove( aBrowserEvent );
450 // pointer shape
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 ) )
461 // repeat the event
462 aRepeatEvt = rEvt;
463 aMouseTimer.Start();
465 else
466 // killing old repeat-event
467 if ( aMouseTimer.IsActive() )
468 aMouseTimer.Stop();
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
484 MouseMove( rEvt );
486 // actual button up handling
487 ReleaseMouse();
488 if ( aMouseTimer.IsActive() )
489 aMouseTimer.Stop();
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 );
514 StartTracking();
518 void BrowserDataWin::Tracking( const TrackingEvent& rTEvt )
520 if ( !GetParent()->bRowDividerDrag )
521 return;
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() )
530 HideTracking();
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();
546 else
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 ):
577 pWin(pWindow),
578 aRect(rRect),
579 nRow(nAbsRow),
580 nCol(nColumn),
581 nColId(nColumnId)
586 BrowserMouseEvent::BrowserMouseEvent( BrowserDataWin *pWindow,
587 const MouseEvent& rEvt ):
588 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 ):
597 MouseEvent(rEvt),
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 )
621 return;
623 bUpdateMode = bMode;
624 if ( bMode )
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();
644 if (!pRegion)
645 aInvalidRegion.emplace_back( Point( 0, 0 ), GetOutputSizePixel() );
646 else
647 aInvalidRegion.emplace_back( pRegion->GetBoundRect() );
649 else
650 Window::ImplInvalidate( pRegion, nFlags );
653 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */