/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
// tdf#115941 because some platforms have things like overlay scrollbars, take a max // of a statusbar height and a scrollbar height as the control area height
// (we can't ask the scrollbars for their size cause if we're zoomed they still have to be // resized - which is done in UpdateScrollbars) return std::max(aStatusBarHeight->GetSizePixel().Height(), nScrollBarSize);
}
BrowseBox::BrowseBox( vcl::Window* pParent, WinBits nBits, BrowserMode nMode )
:Control( pParent, nBits | WB_3DLOOK )
,DragSourceHelper( this )
,DropTargetHelper( this )
,aHScroll( VclPtr<ScrollAdaptor>::Create(this, true) ) // see NavigationBar ctor, here we just want to know its height
,aStatusBarHeight(VclPtr<MeasureStatusBar>::Create(this))
,m_nCornerHeight(0)
,m_nCornerWidth(0)
,m_nActualCornerWidth(0)
,m_bNavigationBar(false)
{
bMultiSelection = false;
pColSel = nullptr;
pVScroll = nullptr;
pDataWin = VclPtr<BrowserDataWin>::Create( this ).get();
InitSettings_Impl( this );
InitSettings_Impl( pDataWin );
#if OSL_DEBUG_LEVEL > 0
OSL_ENSURE( ColCount() == 0 || mvCols[0]->GetId() != HandleColumnId , "BrowseBox::InsertHandleColumn: there is already a handle column" );
{ for (autoconst & col : mvCols)
OSL_ENSURE( col->GetId() != HandleColumnId, "BrowseBox::InsertHandleColumn: there is a non-Handle column with handle ID" );
} #endif
void BrowseBox::FreezeColumn( sal_uInt16 nItemId )
{ // get the position in the current array
size_t nItemPos = GetColumnPos( nItemId ); if ( nItemPos >= mvCols.size() ) // not available! return;
// doesn't the state change? if ( mvCols[ nItemPos ]->IsFrozen() ) return;
// remark the column selection
sal_uInt16 nSelectedColId = ToggleSelectedColumn();
// to be moved? if ( nItemPos != 0 && !mvCols[ nItemPos-1 ]->IsFrozen() )
{ // move to the right of the last frozen column
sal_uInt16 nFirstScrollable = FrozenColCount();
std::unique_ptr<BrowserColumn> pColumn = std::move(mvCols[ nItemPos ]);
mvCols.erase( mvCols.begin() + nItemPos );
nItemPos = nFirstScrollable;
mvCols.insert( mvCols.begin() + nItemPos, std::move(pColumn) );
}
// adjust the number of the first scrollable and visible column if ( nFirstCol <= nItemPos )
nFirstCol = nItemPos + 1;
// toggle the freeze-state of the column
mvCols[ nItemPos ]->Freeze();
// remember the column selection
SetToggledSelectedColumn(nSelectedColId);
}
void BrowseBox::SetColumnPos( sal_uInt16 nColumnId, sal_uInt16 nPos )
{ // never set pos of the handle column if ( nColumnId == HandleColumnId ) return;
// get the position in the current array
sal_uInt16 nOldPos = GetColumnPos( nColumnId ); if ( nOldPos >= mvCols.size() ) // not available! return;
// does the state change? if (nOldPos == nPos) return;
// remark the column selection
sal_uInt16 nSelectedColId = ToggleSelectedColumn();
// determine old column area
Size aDataWinSize( pDataWin->GetSizePixel() ); if ( pDataWin->pHeaderBar )
aDataWinSize.AdjustHeight(pDataWin->pHeaderBar->GetSizePixel().Height() );
// OV // In AutoSizeLastColumn(), we call SetColumnWidth with nWidth==0xffff. // Thus, check here, if the width has actually changed. if( nOldWidth == nWidth ) return;
// do we want to display the change immediately? bool bUpdate = GetUpdateMode() &&
( mvCols[ nItemPos ]->IsFrozen() || nItemPos >= nFirstCol );
if ( pDataWin->pHeaderBar )
pDataWin->pHeaderBar->Clear( );
// correct vertical scrollbar
UpdateScrollbars();
// trigger repaint if necessary if ( GetUpdateMode() )
{
pDataWin->Invalidate();
Control::Invalidate();
}
if ( !isAccessibleAlive() ) return;
if ( mvCols.size() == nOldCount ) return;
// all columns should be removed, so we remove the column header bar and append it again // to avoid to notify every column remove
commitBrowseBoxEvent(
AccessibleEventId::CHILD,
Any(),
Any(getAccessibleHeaderBar(AccessibleBrowseBoxObjType::ColumnHeaderBar))
);
// and now append it again
commitBrowseBoxEvent(
AccessibleEventId::CHILD,
Any(getAccessibleHeaderBar(AccessibleBrowseBoxObjType::ColumnHeaderBar)),
Any()
);
// notify a table model change
commitTableEvent(
AccessibleEventId::TABLE_MODEL_CHANGED,
Any ( AccessibleTableModelChange( COLUMNS_REMOVED,
-1,
-1,
0,
nOldCount
)
),
Any()
);
}
const OUString & BrowseBox::GetColumnTitle( sal_uInt16 nId ) const
{
sal_uInt16 nItemPos = GetColumnPos( nId ); if ( nItemPos >= mvCols.size() ) return EMPTY_OUSTRING; return mvCols[ nItemPos ]->Title();
}
// scrolling one column to the right? if ( nCols == 1 )
{ // update internal value and scrollbar
++nFirstCol;
aHScroll->SetThumbPos( nFirstCol - FrozenColCount() );
// scroll the header bar area (if there is no dedicated HeaderBar control) if ( !pDataWin->pHeaderBar && nTitleLines )
{ // actually scroll
Scroll( -nDelta, 0, aScrollRect, SCROLL_FLAGS );
// invalidate the area of the column which was scrolled out to the left hand side
tools::Rectangle aInvalidateRect( aScrollRect );
aInvalidateRect.SetLeft( nFrozenWidth );
aInvalidateRect.SetRight( nFrozenWidth + nDelta - 1 );
Invalidate( aInvalidateRect );
}
// scroll the data-area
aScrollRect.SetBottom( pDataWin->GetOutputSizePixel().Height() );
// invalidate the area of the column which was scrolled out to the left hand side
aScrollRect.SetLeft( nFrozenWidth );
aScrollRect.SetRight( nFrozenWidth + nDelta - 1 );
pDataWin->Invalidate( aScrollRect );
}
}
// scrolling one column to the left? elseif ( nCols == -1 )
{
--nFirstCol;
aHScroll->SetThumbPos( nFirstCol - FrozenColCount() );
// scroll the header bar area (if there is no dedicated HeaderBar control) if ( !pDataWin->pHeaderBar && nTitleLines )
{
Scroll( nDelta, 0, aScrollRect, SCROLL_FLAGS );
}
// compute new top row again (nTopRow might have changed!)
nTmpMin = std::min( static_cast<tools::Long>(nTopRow + nRows), static_cast<tools::Long>(nRowCount - 1) );
tools::Rectangle aRect; if ( nColId == BROWSER_INVALIDID ) // invalidate the whole row
aRect = tools::Rectangle( Point( 0, (nRow-nTopRow) * GetDataRowHeight() ),
Size( pDataWin->GetSizePixel().Width(), GetDataRowHeight() ) ); else
{ // invalidate the specific field
aRect = GetFieldRectPixel( nRow, nColId, false );
}
pDataWin->Invalidate( aRect );
}
void BrowseBox::Clear()
{ // adjust the total number of rows
DoHideCursor();
sal_Int32 nOldRowCount = nRowCount;
nRowCount = 0; if(bMultiSelection)
{
assert(uRow.pSel);
uRow.pSel->Reset();
} else
uRow.nSel = BROWSER_ENDOFSELECTION;
nCurRow = BROWSER_ENDOFSELECTION;
nTopRow = 0;
nCurColId = 0;
// nFirstCol may not be reset, else the scrolling code will become confused. // nFirstCol may only be changed when adding or deleting columns // nFirstCol = 0; -> wrong!
aHScroll->SetThumbPos( 0 );
pVScroll->SetThumbPos( 0 );
// all rows should be removed, so we remove the row header bar and append it again // to avoid to notify every row remove if ( nOldRowCount == nRowCount ) return;
// and now append it again
commitBrowseBoxEvent(
AccessibleEventId::CHILD,
Any(getAccessibleHeaderBar(AccessibleBrowseBoxObjType::RowHeaderBar)),
Any()
);
// notify a table model change
commitTableEvent(
AccessibleEventId::TABLE_MODEL_CHANGED,
Any( AccessibleTableModelChange(ROWS_REMOVED,
0,
nOldRowCount,
-1,
-1)
),
Any()
);
}
if ( bDoPaint )
{ // hide cursor and selection
SAL_INFO("svtools", "BrowseBox::HideCursor " << this );
ToggleSelection();
DoHideCursor();
}
// adjust total row count
nRowCount -= nNumRows; if (nRowCount < 0) nRowCount = 0;
sal_Int32 nOldCurRow = nCurRow;
// adjust the selection if ( bMultiSelection ) // uRow.pSel->Remove( nRow, nNumRows ); for ( tools::Long i = 0; i < nNumRows; i++ )
uRow.pSel->Remove( nRow ); elseif ( nRow < uRow.nSel && uRow.nSel >= nNumRows )
uRow.nSel -= nNumRows; elseif ( nRow <= uRow.nSel )
uRow.nSel = BROWSER_ENDOFSELECTION;
// adjust the cursor if ( nRowCount == 0 ) // don't compare nRowCount with nNumRows as nNumRows already was subtracted from nRowCount
nCurRow = BROWSER_ENDOFSELECTION; elseif ( nRow < nCurRow )
{
nCurRow -= std::min( nCurRow - nRow, nNumRows ); // with the above nCurRow points a) to the first row after the removed block or b) to the same line // as before, but moved up nNumRows // case a) needs an additional correction if the last n lines were deleted, as 'the first row after the // removed block' is an invalid position then // FS - 09/28/99 - 68429 if (nCurRow == nRowCount)
--nCurRow;
} elseif( nRow == nCurRow && nCurRow == nRowCount )
nCurRow = nRowCount-1;
// is the deleted row visible?
Size aSz = pDataWin->GetOutputSizePixel(); if ( nRow >= nTopRow &&
nRow <= nTopRow + aSz.Height() / GetDataRowHeight() )
{ if ( bDoPaint )
{ // scroll up the rows behind the deleted row // if there are Rows behind if (nRow < nRowCount)
{
tools::Long nY = (nRow-nTopRow) * GetDataRowHeight();
pDataWin->GetOutDev()->SetClipRegion(); if( pDataWin->GetBackground().IsScrollable() )
{
pDataWin->Scroll( 0, - static_cast<short>(GetDataRowHeight()) * nNumRows,
tools::Rectangle( Point( 0, nY ), Size( aSz.Width(),
aSz.Height() - nY + nNumRows*GetDataRowHeight() ) ),
SCROLL_FLAGS );
} else
pDataWin->Window::Invalidate( InvalidateFlags::NoChildren );
} else
{ // Repaint the Rect of the deleted row
tools::Rectangle aRect(
Point( 0, (nRow-nTopRow)*GetDataRowHeight() ),
Size( pDataWin->GetSizePixel().Width(),
nNumRows * GetDataRowHeight() ) );
pDataWin->Invalidate( aRect );
}
}
} // is the deleted row above of the visible area? elseif ( nRow < nTopRow )
nTopRow = nTopRow >= nNumRows ? nTopRow-nNumRows : 0;
if ( bDoPaint )
{ // reshow cursor and selection
ToggleSelection();
SAL_INFO("svtools", "BrowseBox::ShowCursor " << this );
DoShowCursor();
// adjust the vertical scrollbar
UpdateScrollbars();
AutoSizeLastColumn();
}
if ( isAccessibleAlive() )
{ if ( nRowCount == 0 )
{ // all columns should be removed, so we remove the column header bar and append it again // to avoid to notify every column remove
commitBrowseBoxEvent(
AccessibleEventId::CHILD,
Any(),
Any(getAccessibleHeaderBar(AccessibleBrowseBoxObjType::RowHeaderBar))
);
// and now append it again
commitBrowseBoxEvent(
AccessibleEventId::CHILD,
Any(getAccessibleHeaderBar(AccessibleBrowseBoxObjType::RowHeaderBar)),
Any()
);
commitBrowseBoxEvent(
AccessibleEventId::CHILD,
Any(),
Any(getAccessibleTable())
);
// and now append it again
commitBrowseBoxEvent(
AccessibleEventId::CHILD,
Any(getAccessibleTable()),
Any()
);
} else
{
commitTableEvent(
AccessibleEventId::TABLE_MODEL_CHANGED,
Any( AccessibleTableModelChange(
ROWS_REMOVED,
nRow,
nRow + nNumRows,
-1,
-1
)
),
Any()
);
for (tools::Long i = nRow+1 ; i <= (nRow+nNumRows) ; ++i)
{
commitHeaderBarEvent(
AccessibleEventId::CHILD,
Any(),
Any( CreateAccessibleRowHeader( i ) ), false
);
}
}
}
DoShowCursor(); if (!bRowColMove)
{ //try to move to nCurRow, nColId
CursorMoveAttempt aAttempt(nCurRow, nColId, bScrolled); //Detect if we are already in a call to BrowseBox::GoToColumnId //but the attempt is impossible and we are simply recursing //into BrowseBox::GoToColumnId with the same impossible to //fulfill conditions if (m_aGotoStack.empty() || aAttempt != m_aGotoStack.top())
{
m_aGotoStack.push(aAttempt);
CursorMoved();
m_aGotoStack.pop();
}
} returntrue;
} returntrue;
}
void BrowseBox::MakeFieldVisible
(
sal_Int32 nRow, // line number of the field (starting with 0)
sal_uInt16 nColId // column ID of the field
)
/* [Description]
Makes visible the field described in 'nRow' and 'nColId' by scrolling accordingly.
*/
{ if (!pDataWin) return;
Size aTestSize = pDataWin->GetSizePixel();
if ( !bBootstrapped || aTestSize.IsEmpty() ) return;
// is it visible already? bool bVisible = IsFieldVisible( nRow, nColId, true/*bComplete*/ ); if ( bVisible ) return;
// calculate column position, field rectangle and painting area
sal_uInt16 nColPos = GetColumnPos( nColId );
tools::Rectangle aFieldRect = GetFieldRectPixel( nRow, nColId, false );
tools::Rectangle aDataRect( Point(0, 0), pDataWin->GetSizePixel() );
// positioned outside on the left? if ( nColPos >= FrozenColCount() && nColPos < nFirstCol ) // => scroll to the right
ScrollColumns( nColPos - nFirstCol );
// while outside on the right while ( aDataRect.Right() < aFieldRect.Right() )
{ // => scroll to the left if ( ScrollColumns( 1 ) != 1 ) // no more need to scroll break;
aFieldRect = GetFieldRectPixel( nRow, nColId, false );
}
// positioned outside above? if ( nRow < nTopRow ) // scroll further to the bottom
ScrollRows( nRow - nTopRow );
// positioned outside below?
sal_Int32 nBottomRow = nTopRow + GetVisibleRows(); // decrement nBottomRow to make it the number of the last visible line // (count starts with 0!). // Example: BrowseBox contains exactly one entry. nBottomRow := 0 + 1 - 1 if( nBottomRow )
nBottomRow--;
if ( nRow > nBottomRow ) // scroll further to the top
ScrollRows( nRow - nBottomRow );
}
// get the visible area
tools::Rectangle aOutRect( Point(0, 0), pDataWin->GetOutputSizePixel() );
if ( bCompletely ) // test if the field is completely visible return aOutRect.Contains( aRect ); else // test if the field is partly of completely visible return !aOutRect.Intersection( aRect ).IsEmpty();
}
// accumulate the widths of the visible columns
tools::Long nColX = 0; for ( size_t nCol = 0; nCol < mvCols.size(); ++nCol )
{
BrowserColumn *pCol = mvCols[ nCol ].get(); if ( pCol->IsFrozen() || nCol >= nFirstCol )
nColX += pCol->Width();
if ( nColX > nX ) return nCol;
}
return BROWSER_INVALIDID;
}
bool BrowseBox::ReserveControlArea(sal_uInt16 nWidth)
{ if (nWidth != nControlAreaWidth)
{
OSL_ENSURE(nWidth,"Control area of 0 is not allowed, Use USHRT_MAX instead!");
nControlAreaWidth = nWidth;
UpdateScrollbars(); returntrue;
} returnfalse;
}
tools::Rectangle BrowseBox::GetControlArea() const
{ auto nHeight = aHScroll->GetSizePixel().Height(); auto nEndRight = aHScroll->GetPosPixel().X();
bHideSelect = ((nMode & BrowserMode::HIDESELECT) == BrowserMode::HIDESELECT); // default: do not hide the cursor at all (untaken scrolling and such)
bHideCursor = TRISTATE_FALSE;
// create a headerbar. what happens, if a headerbar has to be created and // there already are columns? if ( BrowserMode::HEADERBAR_NEW == ( nMode & BrowserMode::HEADERBAR_NEW ) )
{ if (!pDataWin->pHeaderBar)
pDataWin->pHeaderBar = CreateHeaderBar( this );
} else
{
pDataWin->pHeaderBar.disposeAndClear();
}
if ( bColumnCursor )
{ if (!pColSel)
pColSel.reset(new MultiSelection);
pColSel->SetTotalRange( Range( 0, mvCols.size()-1 ) );
} else
{
pColSel.reset();
}
if ( bMultiSelection )
{ if ( pOldRowSel )
uRow.pSel = pOldRowSel; else
uRow.pSel = new MultiSelection;
} else
{
uRow.nSel = nOldRowSel; delete pOldRowSel;
}
This virtual method is always called before the cursor is moved directly. By means of 'return false', we avoid doing this if e.g. a record contradicts any rules.
This method is not called, if the cursor movement results from removing or deleting a row/column (thus, in cases where only a "cursor correction" happens).
The base implementation currently always returns true.
*/
tools::Long BrowseBox::GetTitleHeight() const
{
tools::Long nHeight; // ask the header bar for the text height (if possible), as the header bar's font is adjusted with // our (and the header's) zoom factor
HeaderBar* pHeaderBar = pDataWin->pHeaderBar; if ( pHeaderBar )
nHeight = pHeaderBar->GetTextHeight(); else
nHeight = GetTextHeight();
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.