diff options
Diffstat (limited to 'svtools/source/brwbox')
-rw-r--r-- | svtools/source/brwbox/brwbox1.cxx | 2347 | ||||
-rw-r--r-- | svtools/source/brwbox/brwbox2.cxx | 1972 | ||||
-rw-r--r-- | svtools/source/brwbox/brwbox3.cxx | 559 | ||||
-rw-r--r-- | svtools/source/brwbox/brwhead.cxx | 107 | ||||
-rw-r--r-- | svtools/source/brwbox/brwimpl.hxx | 80 | ||||
-rw-r--r-- | svtools/source/brwbox/datwin.cxx | 691 | ||||
-rw-r--r-- | svtools/source/brwbox/datwin.hxx | 189 | ||||
-rw-r--r-- | svtools/source/brwbox/ebbcontrols.cxx | 566 | ||||
-rw-r--r-- | svtools/source/brwbox/editbrowsebox.cxx | 1326 | ||||
-rw-r--r-- | svtools/source/brwbox/editbrowsebox2.cxx | 200 | ||||
-rw-r--r-- | svtools/source/brwbox/editbrowseboximpl.hxx | 35 |
11 files changed, 8072 insertions, 0 deletions
diff --git a/svtools/source/brwbox/brwbox1.cxx b/svtools/source/brwbox/brwbox1.cxx new file mode 100644 index 000000000..ee3922582 --- /dev/null +++ b/svtools/source/brwbox/brwbox1.cxx @@ -0,0 +1,2347 @@ +/* -*- 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 . + */ + +#include <svtools/brwbox.hxx> +#include <svtools/brwhead.hxx> +#include <o3tl/numeric.hxx> +#include <o3tl/safeint.hxx> +#include "datwin.hxx" +#include <tools/debug.hxx> +#include <tools/fract.hxx> +#include <sal/log.hxx> +#include <vcl/scrbar.hxx> +#include <vcl/status.hxx> + +#include <algorithm> +#include <com/sun/star/accessibility/AccessibleTableModelChange.hpp> +#include <com/sun/star/accessibility/AccessibleTableModelChangeType.hpp> +#include <com/sun/star/accessibility/AccessibleEventId.hpp> +#include <tools/multisel.hxx> +#include "brwimpl.hxx" + + +#define SCROLL_FLAGS (ScrollFlags::Clip | ScrollFlags::NoChildren) + +using namespace com::sun::star::accessibility::AccessibleEventId; +using namespace com::sun::star::accessibility::AccessibleTableModelChangeType; +using com::sun::star::accessibility::AccessibleTableModelChange; +using namespace ::com::sun::star::uno; +using namespace svt; + +namespace +{ + void disposeAndClearHeaderCell(::svt::BrowseBoxImpl::THeaderCellMap& _rHeaderCell) + { + ::std::for_each( + _rHeaderCell.begin(), + _rHeaderCell.end(), + ::svt::BrowseBoxImpl::THeaderCellMapFunctorDispose() + ); + _rHeaderCell.clear(); + } +} + +void BrowseBox::ConstructImpl( BrowserMode nMode ) +{ + SAL_INFO("svtools", "BrowseBox:ConstructImpl " << this ); + bMultiSelection = false; + pColSel = nullptr; + pVScroll = nullptr; + pDataWin = VclPtr<BrowserDataWin>::Create( this ).get(); + m_pImpl.reset( new ::svt::BrowseBoxImpl() ); + + InitSettings_Impl( this ); + InitSettings_Impl( pDataWin ); + + bBootstrapped = false; + nDataRowHeight = 0; + nTitleLines = 1; + nFirstCol = 0; + nTopRow = 0; + nCurRow = BROWSER_ENDOFSELECTION; + nCurColId = 0; + bResizing = false; + bSelect = false; + bSelecting = false; + bScrolling = false; + bSelectionIsVisible = false; + bNotToggleSel = false; + bRowDividerDrag = false; + bHit = false; + mbInteractiveRowHeight = false; + bHideSelect = false; + bHideCursor = TRISTATE_FALSE; + nRowCount = 0; + m_bFocusOnlyCursor = true; + m_aCursorColor = COL_TRANSPARENT; + m_nCurrentMode = BrowserMode::NONE; + nControlAreaWidth = USHRT_MAX; + uRow.nSel = BROWSER_ENDOFSELECTION; + + aHScroll->SetLineSize(1); + aHScroll->SetScrollHdl( LINK( this, BrowseBox, ScrollHdl ) ); + pDataWin->Show(); + + SetMode( nMode ); + bSelectionIsVisible = bKeepHighlight; + bHasFocus = HasChildPathFocus(); + pDataWin->nCursorHidden = + ( bHasFocus ? 0 : 1 ) + ( GetUpdateMode() ? 0 : 1 ); +} + +BrowseBox::BrowseBox( vcl::Window* pParent, WinBits nBits, BrowserMode nMode ) + :Control( pParent, nBits | WB_3DLOOK ) + ,DragSourceHelper( this ) + ,DropTargetHelper( this ) + ,aHScroll( VclPtr<ScrollBar>::Create(this, WB_HSCROLL) ) + ,aStatusBar( VclPtr<StatusBar>::Create(this) ) +{ + ConstructImpl( nMode ); +} + +BrowseBox::~BrowseBox() +{ + disposeOnce(); +} + +void BrowseBox::dispose() +{ + SAL_INFO("svtools", "BrowseBox:dispose " << this ); + + if ( m_pImpl->m_pAccessible ) + { + disposeAndClearHeaderCell(m_pImpl->m_aColHeaderCellMap); + disposeAndClearHeaderCell(m_pImpl->m_aRowHeaderCellMap); + m_pImpl->m_pAccessible->dispose(); + } + + Hide(); + pDataWin->pHeaderBar.disposeAndClear(); + pDataWin->pCornerWin.disposeAndClear(); + pDataWin.disposeAndClear(); + pVScroll.disposeAndClear(); + aHScroll.disposeAndClear(); + aStatusBar.disposeAndClear(); + + // free columns-space + mvCols.clear(); + pColSel.reset(); + if ( bMultiSelection ) + delete uRow.pSel; + DragSourceHelper::dispose(); + DropTargetHelper::dispose(); + Control::dispose(); +} + + +short BrowseBox::GetCursorHideCount() const +{ + return pDataWin->nCursorHidden; +} + + +void BrowseBox::DoShowCursor( const char * ) +{ + if (!pDataWin) + return; + short nHiddenCount = --pDataWin->nCursorHidden; + if (PaintCursorIfHiddenOnce()) + { + if (1 == nHiddenCount) + DrawCursor(); + } + else + { + if (0 == nHiddenCount) + DrawCursor(); + } +} + + +void BrowseBox::DoHideCursor( const char * ) +{ + short nHiddenCount = ++pDataWin->nCursorHidden; + if (PaintCursorIfHiddenOnce()) + { + if (2 == nHiddenCount) + DrawCursor(); + } + else + { + if (1 == nHiddenCount) + DrawCursor(); + } +} + + +void BrowseBox::SetRealRowCount( const OUString &rRealRowCount ) +{ + pDataWin->aRealRowCount = rRealRowCount; +} + + +void BrowseBox::SetFont( const vcl::Font& rNewFont ) +{ + pDataWin->SetFont( rNewFont ); + ImpGetDataRowHeight(); +} + +const vcl::Font& BrowseBox::GetFont() const +{ + return pDataWin->GetFont(); +} + +sal_uLong BrowseBox::GetDefaultColumnWidth( const OUString& _rText ) const +{ + return pDataWin->GetTextWidth( _rText ) + pDataWin->GetTextWidth(OUString('0')) * 4; +} + + +void BrowseBox::InsertHandleColumn( sal_uLong nWidth ) +{ + +#if OSL_DEBUG_LEVEL > 0 + OSL_ENSURE( ColCount() == 0 || mvCols[0]->GetId() != HandleColumnId , "BrowseBox::InsertHandleColumn: there is already a handle column" ); + { + for (auto const & col : mvCols) + OSL_ENSURE( col->GetId() != HandleColumnId, "BrowseBox::InsertHandleColumn: there is a non-Handle column with handle ID" ); + } +#endif + + mvCols.insert( mvCols.begin(), std::unique_ptr<BrowserColumn>(new BrowserColumn( 0, OUString(), nWidth, GetZoom() )) ); + FreezeColumn( 0 ); + + // adjust headerbar + if ( pDataWin->pHeaderBar ) + { + pDataWin->pHeaderBar->SetPosSizePixel( + Point(nWidth, 0), + Size( GetOutputSizePixel().Width() - nWidth, GetTitleHeight() ) + ); + } + + ColumnInserted( 0 ); +} + + +void BrowseBox::InsertDataColumn( sal_uInt16 nItemId, const OUString& rText, + long nWidth, HeaderBarItemBits nBits, sal_uInt16 nPos ) +{ + + OSL_ENSURE( nItemId != HandleColumnId, "BrowseBox::InsertDataColumn: nItemId is HandleColumnId" ); + OSL_ENSURE( nItemId != BROWSER_INVALIDID, "BrowseBox::InsertDataColumn: nItemId is reserved value BROWSER_INVALIDID" ); + +#if OSL_DEBUG_LEVEL > 0 + { + for (auto const& col : mvCols) + OSL_ENSURE( col->GetId() != nItemId, "BrowseBox::InsertDataColumn: duplicate column Id" ); + } +#endif + + if ( nPos < mvCols.size() ) + { + mvCols.emplace( mvCols.begin() + nPos, new BrowserColumn( nItemId, rText, nWidth, GetZoom() ) ); + } + else + { + mvCols.emplace_back( new BrowserColumn( nItemId, rText, nWidth, GetZoom() ) ); + } + if ( nCurColId == 0 ) + nCurColId = nItemId; + + if ( pDataWin->pHeaderBar ) + { + // Handle column not in the header bar + sal_uInt16 nHeaderPos = nPos; + if (nHeaderPos != HEADERBAR_APPEND && GetColumnId(0) == HandleColumnId ) + nHeaderPos--; + pDataWin->pHeaderBar->InsertItem( + nItemId, rText, nWidth, nBits, nHeaderPos ); + } + ColumnInserted( nPos ); +} + +sal_uInt16 BrowseBox::ToggleSelectedColumn() +{ + sal_uInt16 nSelectedColId = BROWSER_INVALIDID; + if ( pColSel && pColSel->GetSelectCount() ) + { + DoHideCursor( "ToggleSelectedColumn" ); + ToggleSelection(); + long nSelected = pColSel->FirstSelected(); + if (nSelected != static_cast<long>(SFX_ENDOFSELECTION)) + nSelectedColId = mvCols[nSelected]->GetId(); + pColSel->SelectAll(false); + } + return nSelectedColId; +} + +void BrowseBox::SetToggledSelectedColumn(sal_uInt16 _nSelectedColumnId) +{ + if ( pColSel && _nSelectedColumnId != BROWSER_INVALIDID ) + { + pColSel->Select( GetColumnPos( _nSelectedColumnId ) ); + ToggleSelection(); + SAL_INFO("svtools", "BrowseBox::SetToggledSelectedColumn " << this ); + DoShowCursor( "SetToggledSelectedColumn" ); + } +} + +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(); + + // align the scrollbar-range + UpdateScrollbars(); + + // repaint + Control::Invalidate(); + pDataWin->Invalidate(); + + // 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() ); + + tools::Rectangle aFromRect( GetFieldRect( nColumnId) ); + aFromRect.AdjustRight(2*MIN_COLUMNWIDTH ); + + sal_uInt16 nNextPos = nOldPos + 1; + if ( nOldPos > nPos ) + nNextPos = nOldPos - 1; + + BrowserColumn *pNextCol = mvCols[ nNextPos ].get(); + tools::Rectangle aNextRect(GetFieldRect( pNextCol->GetId() )); + + // move column internally + { + std::unique_ptr<BrowserColumn> pTemp = std::move(mvCols[nOldPos]); + mvCols.erase( mvCols.begin() + nOldPos ); + mvCols.insert( mvCols.begin() + nPos, std::move(pTemp) ); + } + + // determine new column area + tools::Rectangle aToRect( GetFieldRect( nColumnId ) ); + aToRect.AdjustRight(2*MIN_COLUMNWIDTH ); + + // do scroll, let redraw + if( pDataWin->GetBackground().IsScrollable() ) + { + long nScroll = -aFromRect.GetWidth(); + tools::Rectangle aScrollArea; + if ( nOldPos > nPos ) + { + long nFrozenWidth = GetFrozenWidth(); + if ( aToRect.Left() < nFrozenWidth ) + aToRect.SetLeft( nFrozenWidth ); + aScrollArea = tools::Rectangle(Point(aToRect.Left(),0), + Point(aNextRect.Right(),aDataWinSize.Height())); + nScroll *= -1; // reverse direction + } + else + aScrollArea = tools::Rectangle(Point(aNextRect.Left(),0), + Point(aToRect.Right(),aDataWinSize.Height())); + + pDataWin->Scroll( nScroll, 0, aScrollArea ); + aToRect.SetTop( 0 ); + aToRect.SetBottom( aScrollArea.Bottom() ); + Invalidate( aToRect ); + } + else + pDataWin->Window::Invalidate( InvalidateFlags::NoChildren ); + + // adjust header bar positions + if ( pDataWin->pHeaderBar ) + { + sal_uInt16 nNewPos = nPos; + if ( GetColumnId(0) == HandleColumnId ) + --nNewPos; + pDataWin->pHeaderBar->MoveItem(nColumnId,nNewPos); + } + // remember the column selection + SetToggledSelectedColumn(nSelectedColId); + + if ( !isAccessibleAlive() ) + return; + + commitTableEvent( + TABLE_MODEL_CHANGED, + makeAny( AccessibleTableModelChange( + DELETE, + 0, + GetRowCount(), + nOldPos, + nOldPos + ) + ), + Any() + ); + + commitTableEvent( + TABLE_MODEL_CHANGED, + makeAny( AccessibleTableModelChange( + INSERT, + 0, + GetRowCount(), + nPos, + nPos + ) + ), + Any() + ); + +} + + +void BrowseBox::SetColumnTitle( sal_uInt16 nItemId, const OUString& rTitle ) +{ + + // never set title of the handle-column + if ( nItemId == HandleColumnId ) + return; + + // get the position in the current array + sal_uInt16 nItemPos = GetColumnPos( nItemId ); + if ( nItemPos >= mvCols.size() ) + // not available! + return; + + // does the state change? + BrowserColumn *pCol = mvCols[ nItemPos ].get(); + if ( pCol->Title() == rTitle ) + return; + + OUString sOld(pCol->Title()); + + pCol->Title() = rTitle; + + // adjust headerbar column + if ( pDataWin->pHeaderBar ) + pDataWin->pHeaderBar->SetItemText( nItemId, rTitle ); + else + { + // redraw visible columns + if ( GetUpdateMode() && ( pCol->IsFrozen() || nItemPos > nFirstCol ) ) + Invalidate( tools::Rectangle( Point(0,0), + Size( GetOutputSizePixel().Width(), GetTitleHeight() ) ) ); + } + + if ( isAccessibleAlive() ) + { + commitTableEvent( TABLE_COLUMN_DESCRIPTION_CHANGED, + makeAny( rTitle ), + makeAny( sOld ) + ); + } +} + + +void BrowseBox::SetColumnWidth( sal_uInt16 nItemId, sal_uLong nWidth ) +{ + + // get the position in the current array + size_t nItemPos = GetColumnPos( nItemId ); + if ( nItemPos >= mvCols.size() ) + return; + + // does the state change? + if ( !(nWidth >= LONG_MAX || mvCols[ nItemPos ]->Width() != nWidth) ) + return; + + long nOldWidth = mvCols[ nItemPos ]->Width(); + + // adjust last column, if necessary + if ( IsVisible() && nItemPos == mvCols.size() - 1 ) + { + long nMaxWidth = pDataWin->GetSizePixel().Width(); + nMaxWidth -= pDataWin->bAutoSizeLastCol + ? GetFieldRect(nItemId).Left() + : GetFrozenWidth(); + if ( pDataWin->bAutoSizeLastCol || nWidth > o3tl::make_unsigned(nMaxWidth) ) + { + nWidth = nMaxWidth > 16 ? nMaxWidth : nOldWidth; + } + } + + // OV + // In AutoSizeLastColumn(), we call SetColumnWidth with nWidth==0xffff. + // Thus, check here, if the width has actually changed. + if( static_cast<sal_uLong>(nOldWidth) == nWidth ) + return; + + // do we want to display the change immediately? + bool bUpdate = GetUpdateMode() && + ( mvCols[ nItemPos ]->IsFrozen() || nItemPos >= nFirstCol ); + + if ( bUpdate ) + { + // Selection hidden + DoHideCursor( "SetColumnWidth" ); + ToggleSelection(); + //!pDataWin->Update(); + //!Control::Update(); + } + + // set width + mvCols[ nItemPos ]->SetWidth(nWidth, GetZoom()); + + // scroll and invalidate + if ( bUpdate ) + { + // get X-Pos of the column changed + long nX = 0; + for ( size_t nCol = 0; nCol < nItemPos; ++nCol ) + { + BrowserColumn *pCol = mvCols[ nCol ].get(); + if ( pCol->IsFrozen() || nCol >= nFirstCol ) + nX += pCol->Width(); + } + + // actually scroll+invalidate + pDataWin->SetClipRegion(); + bool bSelVis = bSelectionIsVisible; + bSelectionIsVisible = false; + if( GetBackground().IsScrollable() ) + { + + tools::Rectangle aScrRect( nX + std::min( static_cast<sal_uLong>(nOldWidth), nWidth ), 0, + GetSizePixel().Width() , // the header is longer than the datawin + pDataWin->GetPosPixel().Y() - 1 ); + Control::Scroll( nWidth-nOldWidth, 0, aScrRect, SCROLL_FLAGS ); + aScrRect.SetBottom( pDataWin->GetSizePixel().Height() ); + pDataWin->Scroll( nWidth-nOldWidth, 0, aScrRect, SCROLL_FLAGS ); + tools::Rectangle aInvRect( nX, 0, nX + std::max( nWidth, static_cast<sal_uLong>(nOldWidth) ), USHRT_MAX ); + Control::Invalidate( aInvRect, InvalidateFlags::NoChildren ); + pDataWin->Invalidate( aInvRect ); + } + else + { + Control::Invalidate( InvalidateFlags::NoChildren ); + pDataWin->Window::Invalidate( InvalidateFlags::NoChildren ); + } + + + //!pDataWin->Update(); + //!Control::Update(); + bSelectionIsVisible = bSelVis; + ToggleSelection(); + DoShowCursor( "SetColumnWidth" ); + } + UpdateScrollbars(); + + // adjust headerbar column + if ( pDataWin->pHeaderBar ) + pDataWin->pHeaderBar->SetItemSize( + nItemId ? nItemId : USHRT_MAX - 1, nWidth ); + + // adjust last column + if ( nItemPos != mvCols.size() - 1 ) + AutoSizeLastColumn(); +} + + +void BrowseBox::AutoSizeLastColumn() +{ + if ( pDataWin->bAutoSizeLastCol && + pDataWin->GetUpdateMode() ) + { + sal_uInt16 nId = GetColumnId( static_cast<sal_uInt16>(mvCols.size()) - 1 ); + SetColumnWidth( nId, LONG_MAX ); + ColumnResized( nId ); + } +} + + +void BrowseBox::RemoveColumn( sal_uInt16 nItemId ) +{ + + // get column position + sal_uInt16 nPos = GetColumnPos(nItemId); + if ( nPos >= ColCount() ) + // not available + return; + + // correct column selection + if ( pColSel ) + pColSel->Remove( nPos ); + + // correct column cursor + if ( nCurColId == nItemId ) + nCurColId = 0; + + // delete column + mvCols.erase( mvCols.begin() + nPos ); + if ( nFirstCol >= nPos && nFirstCol > FrozenColCount() ) + { + OSL_ENSURE(nFirstCol > 0,"FirstCol must be greater zero!"); + --nFirstCol; + } + + // handlecolumn not in headerbar + if (nItemId) + { + if ( pDataWin->pHeaderBar ) + pDataWin->pHeaderBar->RemoveItem( nItemId ); + } + else + { + // adjust headerbar + if ( pDataWin->pHeaderBar ) + { + pDataWin->pHeaderBar->SetPosSizePixel( + Point(0, 0), + Size( GetOutputSizePixel().Width(), GetTitleHeight() ) + ); + } + } + + // correct vertical scrollbar + UpdateScrollbars(); + + // trigger repaint, if necessary + if ( GetUpdateMode() ) + { + pDataWin->Invalidate(); + Control::Invalidate(); + if ( pDataWin->bAutoSizeLastCol && nPos ==ColCount() ) + SetColumnWidth( GetColumnId( nPos - 1 ), LONG_MAX ); + } + + if ( !isAccessibleAlive() ) + return; + + commitTableEvent( + TABLE_MODEL_CHANGED, + makeAny( AccessibleTableModelChange( DELETE, + 0, + GetRowCount(), + nPos, + nPos + ) + ), + Any() + ); + + commitHeaderBarEvent( + CHILD, + Any(), + makeAny( CreateAccessibleColumnHeader( nPos ) ), + true + ); +} + + +void BrowseBox::RemoveColumns() +{ + size_t nOldCount = mvCols.size(); + + // remove all columns + mvCols.clear(); + + // correct column selection + if ( pColSel ) + { + pColSel->SelectAll(false); + pColSel->SetTotalRange( Range( 0, 0 ) ); + } + + // correct column cursor + nCurColId = 0; + nFirstCol = 0; + + 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( + CHILD, + Any(), + makeAny(m_pImpl->getAccessibleHeaderBar(vcl::BBTYPE_COLUMNHEADERBAR)) + ); + + // and now append it again + commitBrowseBoxEvent( + CHILD, + makeAny(m_pImpl->getAccessibleHeaderBar(vcl::BBTYPE_COLUMNHEADERBAR)), + Any() + ); + + // notify a table model change + commitTableEvent( + TABLE_MODEL_CHANGED, + makeAny ( AccessibleTableModelChange( DELETE, + 0, + GetRowCount(), + 0, + nOldCount + ) + ), + Any() + ); +} + + +OUString BrowseBox::GetColumnTitle( sal_uInt16 nId ) const +{ + + sal_uInt16 nItemPos = GetColumnPos( nId ); + if ( nItemPos >= mvCols.size() ) + return OUString(); + return mvCols[ nItemPos ]->Title(); +} + + +long BrowseBox::GetRowCount() const +{ + return nRowCount; +} + + +sal_uInt16 BrowseBox::ColCount() const +{ + + return static_cast<sal_uInt16>(mvCols.size()); +} + + +long BrowseBox::ImpGetDataRowHeight() const +{ + + BrowseBox *pThis = const_cast<BrowseBox*>(this); + pThis->nDataRowHeight = pThis->CalcReverseZoom(pDataWin->GetTextHeight() + 2); + pThis->Resize(); + pDataWin->Invalidate(); + return nDataRowHeight; +} + + +void BrowseBox::SetDataRowHeight( long nPixel ) +{ + + nDataRowHeight = CalcReverseZoom(nPixel); + Resize(); + pDataWin->Invalidate(); +} + + +void BrowseBox::SetTitleLines( sal_uInt16 nLines ) +{ + + nTitleLines = nLines; +} + + +long BrowseBox::ScrollColumns( long nCols ) +{ + + if ( nFirstCol + nCols < 0 || + nFirstCol + nCols >= static_cast<long>(mvCols.size()) ) + return 0; + + // implicitly hides cursor while scrolling + StartScroll(); + bScrolling = true; + bool bScrollable = pDataWin->GetBackground().IsScrollable(); + bool bInvalidateView = false; + + // scrolling one column to the right? + if ( nCols == 1 ) + { + // update internal value and scrollbar + ++nFirstCol; + aHScroll->SetThumbPos( nFirstCol - FrozenColCount() ); + + if ( !bScrollable ) + { + bInvalidateView = true; + } + else + { + long nDelta = mvCols[ nFirstCol-1 ]->Width(); + long nFrozenWidth = GetFrozenWidth(); + + tools::Rectangle aScrollRect( Point( nFrozenWidth + nDelta, 0 ), + Size ( GetOutputSizePixel().Width() - nFrozenWidth - nDelta, + GetTitleHeight() - 1 + ) ); + + // 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() ); + + // actually scroll + pDataWin->Scroll( -nDelta, 0, aScrollRect, SCROLL_FLAGS ); + + // 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? + else if ( nCols == -1 ) + { + --nFirstCol; + aHScroll->SetThumbPos( nFirstCol - FrozenColCount() ); + + if ( !bScrollable ) + { + bInvalidateView = true; + } + else + { + long nDelta = mvCols[ nFirstCol ]->Width(); + long nFrozenWidth = GetFrozenWidth(); + + tools::Rectangle aScrollRect( Point( nFrozenWidth, 0 ), + Size ( GetOutputSizePixel().Width() - nFrozenWidth, + GetTitleHeight() - 1 + ) ); + + // scroll the header bar area (if there is no dedicated HeaderBar control) + if ( !pDataWin->pHeaderBar && nTitleLines ) + { + Scroll( nDelta, 0, aScrollRect, SCROLL_FLAGS ); + } + + // scroll the data-area + aScrollRect.SetBottom( pDataWin->GetOutputSizePixel().Height() ); + pDataWin->Scroll( nDelta, 0, aScrollRect, SCROLL_FLAGS ); + } + } + else + { + if ( GetUpdateMode() ) + { + Invalidate( tools::Rectangle( + Point( GetFrozenWidth(), 0 ), + Size( GetOutputSizePixel().Width(), GetTitleHeight() ) ) ); + pDataWin->Invalidate( tools::Rectangle( + Point( GetFrozenWidth(), 0 ), + pDataWin->GetSizePixel() ) ); + } + + nFirstCol = nFirstCol + static_cast<sal_uInt16>(nCols); + aHScroll->SetThumbPos( nFirstCol - FrozenColCount() ); + } + + // adjust external headerbar, if necessary + if ( pDataWin->pHeaderBar ) + { + long nWidth = 0; + for ( size_t nCol = 0; + nCol < mvCols.size() && nCol < nFirstCol; + ++nCol ) + { + // not the handle column + if ( mvCols[ nCol ]->GetId() ) + nWidth += mvCols[ nCol ]->Width(); + } + + pDataWin->pHeaderBar->SetOffset( nWidth ); + } + + if( bInvalidateView ) + { + Control::Invalidate( InvalidateFlags::NoChildren ); + pDataWin->Window::Invalidate( InvalidateFlags::NoChildren ); + } + + // implicitly show cursor after scrolling + if ( nCols ) + { + pDataWin->Update(); + PaintImmediately(); + } + bScrolling = false; + EndScroll(); + + return nCols; +} + + +long BrowseBox::ScrollRows( long nRows ) +{ + // compute new top row + long nTmpMin = std::min( static_cast<long>(nTopRow + nRows), static_cast<long>(nRowCount - 1) ); + + long nNewTopRow = std::max<long>( nTmpMin, 0 ); + + if ( nNewTopRow == nTopRow ) + return 0; + + sal_uInt16 nVisibleRows = + static_cast<sal_uInt16>(pDataWin->GetOutputSizePixel().Height() / GetDataRowHeight() + 1); + + VisibleRowsChanged(nNewTopRow, nVisibleRows); + + // compute new top row again (nTopRow might have changed!) + nTmpMin = std::min( static_cast<long>(nTopRow + nRows), static_cast<long>(nRowCount - 1) ); + + nNewTopRow = std::max<long>( nTmpMin, 0 ); + + StartScroll(); + + // scroll area on screen and/or repaint + long nDeltaY = GetDataRowHeight() * ( nNewTopRow - nTopRow ); + long nOldTopRow = nTopRow; + nTopRow = nNewTopRow; + + if ( GetUpdateMode() ) + { + pVScroll->SetRange( Range( 0L, nRowCount ) ); + pVScroll->SetThumbPos( nTopRow ); + + if( pDataWin->GetBackground().IsScrollable() && + std::abs( nDeltaY ) > 0 && + std::abs( nDeltaY ) < pDataWin->GetSizePixel().Height() ) + { + pDataWin->Scroll( 0, static_cast<short>(-nDeltaY), SCROLL_FLAGS ); + } + else + pDataWin->Invalidate(); + + if ( nTopRow - nOldTopRow ) + pDataWin->Update(); + } + + EndScroll(); + + return nTopRow - nOldTopRow; +} + + +void BrowseBox::RowModified( long nRow, sal_uInt16 nColId ) +{ + + if ( !GetUpdateMode() ) + return; + + 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( "Clear" ); + long 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 ); + + Invalidate(); + UpdateScrollbars(); + SetNoSelection(); + DoShowCursor( "Clear" ); + CursorMoved(); + + if ( !isAccessibleAlive() ) + return; + + // 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; + + commitBrowseBoxEvent( + CHILD, + Any(), + makeAny( m_pImpl->getAccessibleHeaderBar( vcl::BBTYPE_ROWHEADERBAR ) ) + ); + + // and now append it again + commitBrowseBoxEvent( + CHILD, + makeAny( m_pImpl->getAccessibleHeaderBar( vcl::BBTYPE_ROWHEADERBAR ) ), + Any() + ); + + // notify a table model change + commitTableEvent( + TABLE_MODEL_CHANGED, + makeAny( AccessibleTableModelChange( DELETE, + 0, + nOldRowCount, + 0, + GetColumnCount()) + ), + Any() + ); +} + +void BrowseBox::RowInserted( long nRow, long nNumRows, bool bDoPaint, bool bKeepSelection ) +{ + + if (nRow < 0) + nRow = 0; + else if (nRow > nRowCount) // maximal = nRowCount + nRow = nRowCount; + + if ( nNumRows <= 0 ) + return; + + // adjust total row count + bool bLastRow = nRow >= nRowCount; + nRowCount += nNumRows; + + DoHideCursor( "RowInserted" ); + + // must we paint the new rows? + long nOldCurRow = nCurRow; + Size aSz = pDataWin->GetOutputSizePixel(); + if ( bDoPaint && nRow >= nTopRow && + nRow <= nTopRow + aSz.Height() / GetDataRowHeight() ) + { + long nY = (nRow-nTopRow) * GetDataRowHeight(); + if ( !bLastRow ) + { + // scroll down the rows behind the new row + pDataWin->SetClipRegion(); + if( pDataWin->GetBackground().IsScrollable() ) + { + pDataWin->Scroll( 0, GetDataRowHeight() * nNumRows, + tools::Rectangle( Point( 0, nY ), + Size( aSz.Width(), aSz.Height() - nY ) ), + SCROLL_FLAGS ); + } + else + pDataWin->Window::Invalidate( InvalidateFlags::NoChildren ); + } + else + // scroll would cause a repaint, so we must explicitly invalidate + pDataWin->Invalidate( tools::Rectangle( Point( 0, nY ), + Size( aSz.Width(), nNumRows * GetDataRowHeight() ) ) ); + } + + // correct top row if necessary + if ( nRow < nTopRow ) + nTopRow += nNumRows; + + // adjust the selection + if ( bMultiSelection ) + uRow.pSel->Insert( nRow, nNumRows ); + else if ( uRow.nSel != BROWSER_ENDOFSELECTION && nRow <= uRow.nSel ) + uRow.nSel += nNumRows; + + // adjust the cursor + if ( nCurRow == BROWSER_ENDOFSELECTION ) + GoToRow( 0, false, bKeepSelection ); + else if ( nRow <= nCurRow ) + { + nCurRow += nNumRows; + GoToRow( nCurRow, false, bKeepSelection ); + } + + // adjust the vertical scrollbar + if ( bDoPaint ) + { + UpdateScrollbars(); + AutoSizeLastColumn(); + } + + DoShowCursor( "RowInserted" ); + // notify accessible that rows were inserted + if ( isAccessibleAlive() ) + { + commitTableEvent( + TABLE_MODEL_CHANGED, + makeAny( AccessibleTableModelChange( + INSERT, + nRow, + nRow + nNumRows, + 0, + GetColumnCount() + ) + ), + Any() + ); + + for (long i = nRow+1 ; i <= nRowCount ; ++i) + { + commitHeaderBarEvent( + CHILD, + makeAny( CreateAccessibleRowHeader( i ) ), + Any(), + false + ); + } + } + + if ( nCurRow != nOldCurRow ) + CursorMoved(); + + DBG_ASSERT(nRowCount > 0,"BrowseBox: nRowCount <= 0"); + DBG_ASSERT(nCurRow >= 0,"BrowseBox: nCurRow < 0"); + DBG_ASSERT(nCurRow < nRowCount,"nCurRow >= nRowCount"); +} + + +void BrowseBox::RowRemoved( long nRow, long nNumRows, bool bDoPaint ) +{ + + if ( nRow < 0 ) + nRow = 0; + else if ( nRow >= nRowCount ) + nRow = nRowCount - 1; + + if ( nNumRows <= 0 ) + return; + + if ( nRowCount <= 0 ) + return; + + if ( bDoPaint ) + { + // hide cursor and selection + SAL_INFO("svtools", "BrowseBox::HideCursor " << this ); + ToggleSelection(); + DoHideCursor( "RowRemoved" ); + } + + // adjust total row count + nRowCount -= nNumRows; + if (nRowCount < 0) nRowCount = 0; + long nOldCurRow = nCurRow; + + // adjust the selection + if ( bMultiSelection ) + // uRow.pSel->Remove( nRow, nNumRows ); + for ( long i = 0; i < nNumRows; i++ ) + uRow.pSel->Remove( nRow ); + else if ( nRow < uRow.nSel && uRow.nSel >= nNumRows ) + uRow.nSel -= nNumRows; + else if ( 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; + else if ( 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; + } + else if( 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) + { + long nY = (nRow-nTopRow) * GetDataRowHeight(); + pDataWin->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? + else if ( nRow < nTopRow ) + nTopRow = nTopRow >= nNumRows ? nTopRow-nNumRows : 0; + + if ( bDoPaint ) + { + // reshow cursor and selection + ToggleSelection(); + SAL_INFO("svtools", "BrowseBox::ShowCursor " << this ); + DoShowCursor( "RowRemoved" ); + + // 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( + CHILD, + Any(), + makeAny( m_pImpl->getAccessibleHeaderBar( vcl::BBTYPE_ROWHEADERBAR ) ) + ); + + // and now append it again + commitBrowseBoxEvent( + CHILD, + makeAny(m_pImpl->getAccessibleHeaderBar(vcl::BBTYPE_ROWHEADERBAR)), + Any() + ); + commitBrowseBoxEvent( + CHILD, + Any(), + makeAny( m_pImpl->getAccessibleTable() ) + ); + + // and now append it again + commitBrowseBoxEvent( + CHILD, + makeAny( m_pImpl->getAccessibleTable() ), + Any() + ); + } + else + { + commitTableEvent( + TABLE_MODEL_CHANGED, + makeAny( AccessibleTableModelChange( + DELETE, + nRow, + nRow + nNumRows, + 0, + GetColumnCount() + ) + ), + Any() + ); + + for (long i = nRow+1 ; i <= (nRow+nNumRows) ; ++i) + { + commitHeaderBarEvent( + CHILD, + Any(), + makeAny( CreateAccessibleRowHeader( i ) ), + false + ); + } + } + } + + if ( nOldCurRow != nCurRow ) + CursorMoved(); + + DBG_ASSERT(nRowCount >= 0,"BrowseBox: nRowCount < 0"); + DBG_ASSERT(nCurRow >= 0 || nRowCount == 0,"BrowseBox: nCurRow < 0 && nRowCount != 0"); + DBG_ASSERT(nCurRow < nRowCount,"nCurRow >= nRowCount"); +} + + +bool BrowseBox::GoToRow( long nRow) +{ + return GoToRow(nRow, false); +} + + +bool BrowseBox::GoToRow( long nRow, bool bRowColMove, bool bKeepSelection ) +{ + + long nOldCurRow = nCurRow; + + // nothing to do? + if ( nRow == nCurRow && ( bMultiSelection || uRow.nSel == nRow ) ) + return true; + + // out of range? + if ( nRow < 0 || nRow >= nRowCount ) + return false; + + // not allowed? + if ( !bRowColMove && !IsCursorMoveAllowed( nRow, nCurColId ) ) + return false; + + // compute the last visible row + Size aSz( pDataWin->GetSizePixel() ); + sal_uInt16 nVisibleRows = sal_uInt16( aSz.Height() / GetDataRowHeight() - 1 ); + long nLastRow = nTopRow + nVisibleRows; + + // suspend Updates + pDataWin->EnterUpdateLock(); + + // remove old highlight, if necessary + if ( !bMultiSelection && !bKeepSelection ) + ToggleSelection(); + DoHideCursor( "GoToRow" ); + + // must we scroll? + bool bWasVisible = bSelectionIsVisible; + if (! bMultiSelection) + { + if( !bKeepSelection ) + bSelectionIsVisible = false; + } + if ( nRow < nTopRow ) + ScrollRows( nRow - nTopRow ); + else if ( nRow > nLastRow ) + ScrollRows( nRow - nLastRow ); + bSelectionIsVisible = bWasVisible; + + // adjust cursor (selection) and thumb + if ( GetUpdateMode() ) + pVScroll->SetThumbPos( nTopRow ); + + // relative positioning (because nCurRow might have changed in the meantime)! + if (nCurRow != BROWSER_ENDOFSELECTION ) + nCurRow = nCurRow + (nRow - nOldCurRow); + + // make sure that the current position is valid + if (nCurRow == BROWSER_ENDOFSELECTION && nRowCount > 0) + nCurRow = 0; + else if ( nCurRow >= nRowCount ) + nCurRow = nRowCount - 1; + aSelRange = Range( nCurRow, nCurRow ); + + // display new highlight if necessary + if ( !bMultiSelection && !bKeepSelection ) + uRow.nSel = nRow; + + // resume Updates + pDataWin->LeaveUpdateLock(); + + // Cursor+Highlight + if ( !bMultiSelection && !bKeepSelection) + ToggleSelection(); + DoShowCursor( "GoToRow" ); + if ( !bRowColMove && nOldCurRow != nCurRow ) + CursorMoved(); + + if ( !bMultiSelection && !bKeepSelection ) + { + if ( !bSelecting ) + Select(); + else + bSelect = true; + } + return true; +} + + +bool BrowseBox::GoToColumnId( sal_uInt16 nColId) +{ + return GoToColumnId(nColId, true); +} + + +bool BrowseBox::GoToColumnId( sal_uInt16 nColId, bool bMakeVisible, bool bRowColMove) +{ + if (!bColumnCursor) + return false; + + // allowed? + if (!bRowColMove && !IsCursorMoveAllowed( nCurRow, nColId ) ) + return false; + + if ( nColId != nCurColId || (bMakeVisible && !IsFieldVisible(nCurRow, nColId, true))) + { + sal_uInt16 nNewPos = GetColumnPos(nColId); + BrowserColumn* pColumn = (nNewPos < mvCols.size()) ? mvCols[ nNewPos ].get() : nullptr; + DBG_ASSERT( pColumn, "no column object - invalid id?" ); + if ( !pColumn ) + return false; + + DoHideCursor( "GoToColumnId" ); + nCurColId = nColId; + + bool bScrolled = false; + + sal_uInt16 nFirstPos = nFirstCol; + sal_uInt16 nWidth = static_cast<sal_uInt16>(pColumn->Width()); + sal_uInt16 nLastPos = GetColumnAtXPosPixel( + pDataWin->GetSizePixel().Width()-nWidth ); + sal_uInt16 nFrozen = FrozenColCount(); + if ( bMakeVisible && nLastPos && + nNewPos >= nFrozen && ( nNewPos < nFirstPos || nNewPos > nLastPos ) ) + { + if ( nNewPos < nFirstPos ) + ScrollColumns( nNewPos-nFirstPos ); + else if ( nNewPos > nLastPos ) + ScrollColumns( nNewPos-nLastPos ); + bScrolled = true; + } + + DoShowCursor( "GoToColumnId" ); + 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(); + } + } + return true; + } + return true; +} + + +bool BrowseBox::GoToRowColumnId( long nRow, sal_uInt16 nColId ) +{ + + // out of range? + if ( nRow < 0 || nRow >= nRowCount ) + return false; + + if (!bColumnCursor) + return false; + + // nothing to do ? + if ( nRow == nCurRow && ( bMultiSelection || uRow.nSel == nRow ) && + nColId == nCurColId && IsFieldVisible(nCurRow, nColId, true)) + return true; + + // allowed? + if (!IsCursorMoveAllowed(nRow, nColId)) + return false; + + DoHideCursor( "GoToRowColumnId" ); + bool bMoved = GoToRow(nRow, true) && GoToColumnId(nColId, true, true); + DoShowCursor( "GoToRowColumnId" ); + + if (bMoved) + CursorMoved(); + + return bMoved; +} + + +void BrowseBox::SetNoSelection() +{ + + // is there no selection + if ( ( !pColSel || !pColSel->GetSelectCount() ) && + ( ( !bMultiSelection && uRow.nSel == BROWSER_ENDOFSELECTION ) || + ( bMultiSelection && !uRow.pSel->GetSelectCount() ) ) ) + // nothing to do + return; + + SAL_INFO("svtools", "BrowseBox::HideCursor " << this ); + ToggleSelection(); + + // unselect all + if ( bMultiSelection ) + uRow.pSel->SelectAll(false); + else + uRow.nSel = BROWSER_ENDOFSELECTION; + if ( pColSel ) + pColSel->SelectAll(false); + if ( !bSelecting ) + Select(); + else + bSelect = true; + + // restore screen + SAL_INFO("svtools", "BrowseBox::ShowCursor " << this ); + + if ( isAccessibleAlive() ) + { + commitTableEvent( + SELECTION_CHANGED, + Any(), + Any() + ); + } +} + + +void BrowseBox::SelectAll() +{ + + if ( !bMultiSelection ) + return; + + SAL_INFO("svtools", "BrowseBox::HideCursor " << this ); + ToggleSelection(); + + // select all rows + if ( pColSel ) + pColSel->SelectAll(false); + uRow.pSel->SelectAll(); + + // don't highlight handle column + BrowserColumn *pFirstCol = mvCols[ 0 ].get(); + long nOfsX = pFirstCol->GetId() ? 0 : pFirstCol->Width(); + + // highlight the row selection + if ( !bHideSelect ) + { + tools::Rectangle aHighlightRect; + sal_uInt16 nVisibleRows = + static_cast<sal_uInt16>(pDataWin->GetOutputSizePixel().Height() / GetDataRowHeight() + 1); + for ( long nRow = std::max<long>( nTopRow, uRow.pSel->FirstSelected() ); + nRow != BROWSER_ENDOFSELECTION && nRow < nTopRow + nVisibleRows; + nRow = uRow.pSel->NextSelected() ) + aHighlightRect.Union( tools::Rectangle( + Point( nOfsX, (nRow-nTopRow)*GetDataRowHeight() ), + Size( pDataWin->GetSizePixel().Width(), GetDataRowHeight() ) ) ); + pDataWin->Invalidate( aHighlightRect ); + } + + if ( !bSelecting ) + Select(); + else + bSelect = true; + + // restore screen + SAL_INFO("svtools", "BrowseBox::ShowCursor " << this ); + + if ( !isAccessibleAlive() ) + return; + + commitTableEvent( + SELECTION_CHANGED, + Any(), + Any() + ); + commitHeaderBarEvent( + SELECTION_CHANGED, + Any(), + Any(), + true + ); // column header event + + commitHeaderBarEvent( + SELECTION_CHANGED, + Any(), + Any(), + false + ); // row header event +} + + +void BrowseBox::SelectRow( long nRow, bool _bSelect, bool bExpand ) +{ + + if ( !bMultiSelection ) + { + // deselecting is impossible, selecting via cursor + if ( _bSelect ) + GoToRow(nRow, false); + return; + } + + SAL_INFO("svtools", "BrowseBox::HideCursor " << this ); + + // remove old selection? + if ( !bExpand || !bMultiSelection ) + { + ToggleSelection(); + if ( bMultiSelection ) + uRow.pSel->SelectAll(false); + else + uRow.nSel = BROWSER_ENDOFSELECTION; + if ( pColSel ) + pColSel->SelectAll(false); + } + + // set new selection + if ( !bHideSelect + && ( ( bMultiSelection + && uRow.pSel->GetTotalRange().Max() >= nRow + && uRow.pSel->Select( nRow, _bSelect ) + ) + || ( !bMultiSelection + && ( uRow.nSel = nRow ) != BROWSER_ENDOFSELECTION ) + ) + ) + { + // don't highlight handle column + BrowserColumn *pFirstCol = mvCols[ 0 ].get(); + long nOfsX = pFirstCol->GetId() ? 0 : pFirstCol->Width(); + + // highlight only newly selected part + tools::Rectangle aRect( + Point( nOfsX, (nRow-nTopRow)*GetDataRowHeight() ), + Size( pDataWin->GetSizePixel().Width(), GetDataRowHeight() ) ); + pDataWin->Invalidate( aRect ); + } + + if ( !bSelecting ) + Select(); + else + bSelect = true; + + // restore screen + SAL_INFO("svtools", "BrowseBox::ShowCursor " << this ); + + if ( !isAccessibleAlive() ) + return; + + commitTableEvent( + SELECTION_CHANGED, + Any(), + Any() + ); + commitHeaderBarEvent( + SELECTION_CHANGED, + Any(), + Any(), + false + ); // row header event +} + + +long BrowseBox::GetSelectRowCount() const +{ + + return bMultiSelection ? uRow.pSel->GetSelectCount() : + uRow.nSel == BROWSER_ENDOFSELECTION ? 0 : 1; +} + + +void BrowseBox::SelectColumnPos( sal_uInt16 nNewColPos, bool _bSelect, bool bMakeVisible ) +{ + + if ( !bColumnCursor || nNewColPos == BROWSER_INVALIDID ) + return; + + if ( !bMultiSelection ) + { + if ( _bSelect ) + GoToColumnId( mvCols[ nNewColPos ]->GetId(), bMakeVisible ); + return; + } + else + { + if ( !GoToColumnId( mvCols[ nNewColPos ]->GetId(), bMakeVisible ) ) + return; + } + + SAL_INFO("svtools", "BrowseBox::HideCursor " << this ); + ToggleSelection(); + if ( bMultiSelection ) + uRow.pSel->SelectAll(false); + else + uRow.nSel = BROWSER_ENDOFSELECTION; + pColSel->SelectAll(false); + + if ( pColSel->Select( nNewColPos, _bSelect ) ) + { + // GoToColumnId( mvCols->GetObject(nNewColPos)->GetId(), bMakeVisible ); + + // only highlight painted areas + pDataWin->Update(); + tools::Rectangle aFieldRectPix( GetFieldRectPixel( nCurRow, nCurColId, false ) ); + tools::Rectangle aRect( + Point( aFieldRectPix.Left() - MIN_COLUMNWIDTH, 0 ), + Size( mvCols[ nNewColPos ]->Width(), + pDataWin->GetOutputSizePixel().Height() ) ); + pDataWin->Invalidate( aRect ); + if ( !bSelecting ) + Select(); + else + bSelect = true; + + if ( isAccessibleAlive() ) + { + commitTableEvent( + SELECTION_CHANGED, + Any(), + Any() + ); + commitHeaderBarEvent( + SELECTION_CHANGED, + Any(), + Any(), + true + ); // column header event + } + } + + // restore screen + SAL_INFO("svtools", "BrowseBox::ShowCursor " << this ); +} + + +sal_uInt16 BrowseBox::GetSelectColumnCount() const +{ + + // while bAutoSelect (==!pColSel), 1 if any rows (yes rows!) else none + return pColSel ? static_cast<sal_uInt16>(pColSel->GetSelectCount()) : + nCurRow >= 0 ? 1 : 0; +} + + +long BrowseBox::FirstSelectedColumn( ) const +{ + return pColSel ? pColSel->FirstSelected() : BROWSER_ENDOFSELECTION; +} + + +long BrowseBox::FirstSelectedRow() +{ + + return bMultiSelection ? uRow.pSel->FirstSelected() : uRow.nSel; +} + + +long BrowseBox::NextSelectedRow() +{ + + return bMultiSelection ? uRow.pSel->NextSelected() : BROWSER_ENDOFSELECTION; +} + + +long BrowseBox::LastSelectedRow() +{ + + return bMultiSelection ? uRow.pSel->LastSelected() : uRow.nSel; +} + + +bool BrowseBox::IsRowSelected( long nRow ) const +{ + + return bMultiSelection ? uRow.pSel->IsSelected(nRow) : nRow == uRow.nSel; +} + + +bool BrowseBox::IsColumnSelected( sal_uInt16 nColumnId ) const +{ + + return pColSel ? pColSel->IsSelected( GetColumnPos(nColumnId) ) : + nCurColId == nColumnId; +} + + +void BrowseBox::MakeFieldVisible +( + long 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? + long 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 ); +} + + +bool BrowseBox::IsFieldVisible( long nRow, sal_uInt16 nColumnId, + bool bCompletely ) const +{ + + // hidden by frozen column? + sal_uInt16 nColPos = GetColumnPos( nColumnId ); + if ( nColPos >= FrozenColCount() && nColPos < nFirstCol ) + return false; + + tools::Rectangle aRect( ImplFieldRectPixel( nRow, nColumnId ) ); + if ( aRect.IsEmpty() ) + return false; + + // get the visible area + tools::Rectangle aOutRect( Point(0, 0), pDataWin->GetOutputSizePixel() ); + + if ( bCompletely ) + // test if the field is completely visible + return aOutRect.IsInside( aRect ); + else + // test if the field is partly of completely visible + return !aOutRect.Intersection( aRect ).IsEmpty(); +} + + +tools::Rectangle BrowseBox::GetFieldRectPixel( long nRow, sal_uInt16 nColumnId, + bool bRelToBrowser) const +{ + + // get the rectangle relative to DataWin + tools::Rectangle aRect( ImplFieldRectPixel( nRow, nColumnId ) ); + if ( aRect.IsEmpty() ) + return aRect; + + // adjust relative to BrowseBox's output area + Point aTopLeft( aRect.TopLeft() ); + if ( bRelToBrowser ) + { + aTopLeft = pDataWin->OutputToScreenPixel( aTopLeft ); + aTopLeft = ScreenToOutputPixel( aTopLeft ); + } + + return tools::Rectangle( aTopLeft, aRect.GetSize() ); +} + + +tools::Rectangle BrowseBox::GetRowRectPixel( long nRow ) const +{ + + // get the rectangle relative to DataWin + tools::Rectangle aRect; + if ( nTopRow > nRow ) + // row is above visible area + return aRect; + aRect = tools::Rectangle( + Point( 0, GetDataRowHeight() * (nRow-nTopRow) ), + Size( pDataWin->GetOutputSizePixel().Width(), GetDataRowHeight() ) ); + if ( aRect.TopLeft().Y() > pDataWin->GetOutputSizePixel().Height() ) + // row is below visible area + return aRect; + + // adjust relative to BrowseBox's output area + Point aTopLeft( aRect.TopLeft() ); + aTopLeft = pDataWin->OutputToScreenPixel( aTopLeft ); + aTopLeft = ScreenToOutputPixel( aTopLeft ); + + return tools::Rectangle( aTopLeft, aRect.GetSize() ); +} + + +tools::Rectangle BrowseBox::ImplFieldRectPixel( long nRow, sal_uInt16 nColumnId ) const +{ + + // compute the X-coordinate relative to DataWin by accumulation + long nColX = 0; + sal_uInt16 nFrozenCols = FrozenColCount(); + size_t nCol; + for ( nCol = 0; + nCol < mvCols.size() && mvCols[ nCol ]->GetId() != nColumnId; + ++nCol ) + if ( mvCols[ nCol ]->IsFrozen() || nCol >= nFirstCol ) + nColX += mvCols[ nCol ]->Width(); + + if ( nCol >= mvCols.size() || ( nCol >= nFrozenCols && nCol < nFirstCol ) ) + return tools::Rectangle(); + + // compute the Y-coordinate relative to DataWin + long nRowY = GetDataRowHeight(); + if ( nRow != BROWSER_ENDOFSELECTION ) // #105497# OJ + nRowY = ( nRow - nTopRow ) * GetDataRowHeight(); + + // assemble the Rectangle relative to DataWin + return tools::Rectangle( + Point( nColX + MIN_COLUMNWIDTH, nRowY ), + Size( (mvCols[nCol]->Width() == LONG_MAX + ? LONG_MAX - (nColX + MIN_COLUMNWIDTH) : mvCols[ nCol ]->Width() - 2*MIN_COLUMNWIDTH), + GetDataRowHeight() - 1 ) ); +} + + +long BrowseBox::GetRowAtYPosPixel( long nY, bool bRelToBrowser ) const +{ + + // compute the Y-coordinate + if ( bRelToBrowser ) + { + Point aDataTopLeft = pDataWin->OutputToScreenPixel( Point(0, 0) ); + Point aTopLeft = OutputToScreenPixel( Point(0, 0) ); + nY -= aDataTopLeft.Y() - aTopLeft.Y(); + } + + // no row there (e.g. in the header) + if ( nY < 0 || nY >= pDataWin->GetOutputSizePixel().Height() ) + return -1; + + return nY / GetDataRowHeight() + nTopRow; +} + + +tools::Rectangle BrowseBox::GetFieldRect( sal_uInt16 nColumnId ) const +{ + + return GetFieldRectPixel( nCurRow, nColumnId ); +} + + +sal_uInt16 BrowseBox::GetColumnAtXPosPixel( long nX ) const +{ + + // accumulate the widths of the visible columns + 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(); + return true; + } + return false; +} + +tools::Rectangle BrowseBox::GetControlArea() const +{ + auto nHeight = aHScroll->GetSizePixel().Height(); + return tools::Rectangle( + Point( 0, GetOutputSizePixel().Height() - nHeight ), + Size( GetOutputSizePixel().Width() - aHScroll->GetSizePixel().Width(), + nHeight ) ); +} + +void BrowseBox::SetMode( BrowserMode nMode ) +{ + + pDataWin->bAutoHScroll = BrowserMode::AUTO_HSCROLL == ( nMode & BrowserMode::AUTO_HSCROLL ); + pDataWin->bAutoVScroll = BrowserMode::AUTO_VSCROLL == ( nMode & BrowserMode::AUTO_VSCROLL ); + pDataWin->bNoHScroll = BrowserMode::NO_HSCROLL == ( nMode & BrowserMode::NO_HSCROLL ); + pDataWin->bNoVScroll = BrowserMode::NO_VSCROLL == ( nMode & BrowserMode::NO_VSCROLL ); + + DBG_ASSERT( !( pDataWin->bAutoHScroll && pDataWin->bNoHScroll ), + "BrowseBox::SetMode: AutoHScroll *and* NoHScroll?" ); + DBG_ASSERT( !( pDataWin->bAutoVScroll && pDataWin->bNoVScroll ), + "BrowseBox::SetMode: AutoVScroll *and* NoVScroll?" ); + if ( pDataWin->bAutoHScroll ) + pDataWin->bNoHScroll = false; + if ( pDataWin->bAutoVScroll ) + pDataWin->bNoVScroll = false; + + if ( pDataWin->bNoHScroll ) + aHScroll->Hide(); + + nControlAreaWidth = USHRT_MAX; + + long nOldRowSel = bMultiSelection ? uRow.pSel->FirstSelected() : uRow.nSel; + MultiSelection *pOldRowSel = bMultiSelection ? uRow.pSel : nullptr; + + pVScroll.disposeAndClear(); + + bMultiSelection = bool( nMode & BrowserMode::MULTISELECTION ); + bColumnCursor = bool( nMode & BrowserMode::COLUMNSELECTION ); + bKeepHighlight = bool( nMode & BrowserMode::KEEPHIGHLIGHT ); + + bHideSelect = ((nMode & BrowserMode::HIDESELECT) == BrowserMode::HIDESELECT); + // default: do not hide the cursor at all (untaken scrolling and such) + bHideCursor = TRISTATE_FALSE; + + if ( BrowserMode::HIDECURSOR == ( nMode & BrowserMode::HIDECURSOR ) ) + { + bHideCursor = TRISTATE_TRUE; + } + + m_bFocusOnlyCursor = ((nMode & BrowserMode::CURSOR_WO_FOCUS) == BrowserMode::NONE); + + bHLines = ( nMode & BrowserMode::HLINES ) == BrowserMode::HLINES; + bVLines = ( nMode & BrowserMode::VLINES ) == BrowserMode::VLINES; + + constexpr WinBits nVScrollWinBits = WB_VSCROLL; + pVScroll = ( nMode & BrowserMode::TRACKING_TIPS ) == BrowserMode::TRACKING_TIPS + ? VclPtr<BrowserScrollBar>::Create( this, nVScrollWinBits, pDataWin.get() ) + : VclPtr<ScrollBar>::Create( this, nVScrollWinBits ); + pVScroll->SetLineSize( 1 ); + pVScroll->SetPageSize(1); + pVScroll->SetScrollHdl( LINK( this, BrowseBox, ScrollHdl ) ); + + pDataWin->bAutoSizeLastCol = + BrowserMode::AUTOSIZE_LASTCOL == ( nMode & BrowserMode::AUTOSIZE_LASTCOL ); + + // 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; + } + + if ( bBootstrapped ) + { + StateChanged( StateChangedType::InitShow ); + if ( bMultiSelection && !pOldRowSel && + nOldRowSel != BROWSER_ENDOFSELECTION ) + uRow.pSel->Select( nOldRowSel ); + } + + if ( pDataWin ) + pDataWin->Invalidate(); + + // no cursor on handle column + if ( nCurColId == HandleColumnId ) + nCurColId = GetColumnId( 1 ); + + m_nCurrentMode = nMode; +} + + +void BrowseBox::VisibleRowsChanged( long, sal_uInt16 ) +{ + + // old behavior: automatically correct NumRows: + if ( nRowCount < GetRowCount() ) + { + RowInserted(nRowCount,GetRowCount() - nRowCount, false); + } + else if ( nRowCount > GetRowCount() ) + { + RowRemoved(nRowCount-(nRowCount - GetRowCount()),nRowCount - GetRowCount(), false); + } +} + + +bool BrowseBox::IsCursorMoveAllowed( long, sal_uInt16 ) const + +/* [Description] + + 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. +*/ + +{ + return true; +} + + +long BrowseBox::GetDataRowHeight() const +{ + return CalcZoom(nDataRowHeight ? nDataRowHeight : ImpGetDataRowHeight()); +} + + +VclPtr<BrowserHeader> BrowseBox::CreateHeaderBar( BrowseBox* pParent ) +{ + VclPtr<BrowserHeader> pNewBar = VclPtr<BrowserHeader>::Create( pParent ); + pNewBar->SetStartDragHdl( LINK( this, BrowseBox, StartDragHdl ) ); + return pNewBar; +} + +void BrowseBox::SetHeaderBar( BrowserHeader* pHeaderBar ) +{ + pDataWin->pHeaderBar.disposeAndClear(); + pDataWin->pHeaderBar = pHeaderBar; + pDataWin->pHeaderBar->SetStartDragHdl( LINK( this, BrowseBox, StartDragHdl ) ); +} + +long BrowseBox::GetTitleHeight() const +{ + 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(); + + return nTitleLines ? nTitleLines * nHeight + 4 : 0; +} + +long BrowseBox::CalcReverseZoom(long nVal) +{ + if (IsZoom()) + { + const Fraction& rZoom = GetZoom(); + double n = static_cast<double>(nVal); + n *= static_cast<double>(rZoom.GetDenominator()); + if (!rZoom.GetNumerator()) + throw o3tl::divide_by_zero(); + n /= static_cast<double>(rZoom.GetNumerator()); + nVal = n>0 ? static_cast<long>(n + 0.5) : -static_cast<long>(-n + 0.5); + } + + return nVal; +} + +void BrowseBox::CursorMoved() +{ + // before implementing more here, please adjust the EditBrowseBox + + if ( isAccessibleAlive() && HasFocus() ) + commitTableEvent( + ACTIVE_DESCENDANT_CHANGED, + makeAny( CreateAccessibleCell( GetCurRow(),GetColumnPos( GetCurColumnId() ) ) ), + Any() + ); +} + +void BrowseBox::LoseFocus() +{ + SAL_INFO("svtools", "BrowseBox::LoseFocus " << this ); + + if ( bHasFocus ) + { + SAL_INFO("svtools", "BrowseBox::HideCursor " << this ); + DoHideCursor( "LoseFocus" ); + + if ( !bKeepHighlight ) + { + ToggleSelection(); + bSelectionIsVisible = false; + } + + bHasFocus = false; + } + Control::LoseFocus(); +} + + +void BrowseBox::GetFocus() +{ + SAL_INFO("svtools", "BrowseBox::GetFocus " << this ); + + if ( !bHasFocus ) + { + if ( !bSelectionIsVisible ) + { + bSelectionIsVisible = true; + if ( bBootstrapped ) + ToggleSelection(); + } + + bHasFocus = true; + DoShowCursor( "GetFocus" ); + } + Control::GetFocus(); +} + + +sal_uInt16 BrowseBox::GetVisibleRows() const +{ + return static_cast<sal_uInt16>((pDataWin->GetOutputSizePixel().Height() - 1 )/ GetDataRowHeight() + 1); +} + +vcl::Window& BrowseBox::GetDataWindow() const +{ + return *pDataWin; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svtools/source/brwbox/brwbox2.cxx b/svtools/source/brwbox/brwbox2.cxx new file mode 100644 index 000000000..e9e725149 --- /dev/null +++ b/svtools/source/brwbox/brwbox2.cxx @@ -0,0 +1,1972 @@ +/* -*- 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 . + */ + +#include <sal/log.hxx> +#include <o3tl/safeint.hxx> +#include <osl/diagnose.h> +#include <tools/debug.hxx> +#include <svtools/brwbox.hxx> +#include "datwin.hxx" +#include <svtools/colorcfg.hxx> +#include <vcl/commandevent.hxx> +#include <vcl/ptrstyle.hxx> +#include <vcl/scrbar.hxx> +#include <vcl/settings.hxx> +#include <vcl/status.hxx> + +#include <tools/multisel.hxx> +#include <tools/fract.hxx> +#include <algorithm> +#include <memory> + +using namespace ::com::sun::star::datatransfer; + + +void BrowseBox::StartDrag( sal_Int8 /* _nAction */, const Point& /* _rPosPixel */ ) +{ + // not interested in this event +} + + +sal_Int8 BrowseBox::AcceptDrop( const AcceptDropEvent& _rEvt ) +{ + AcceptDropEvent aTransformed( _rEvt ); + aTransformed.maPosPixel = pDataWin->ScreenToOutputPixel( OutputToScreenPixel( _rEvt.maPosPixel ) ); + return pDataWin->AcceptDrop( aTransformed ); +} + + +sal_Int8 BrowseBox::ExecuteDrop( const ExecuteDropEvent& _rEvt ) +{ + ExecuteDropEvent aTransformed( _rEvt ); + aTransformed.maPosPixel = pDataWin->ScreenToOutputPixel( OutputToScreenPixel( _rEvt.maPosPixel ) ); + return pDataWin->ExecuteDrop( aTransformed ); +} + + +sal_Int8 BrowseBox::AcceptDrop( const BrowserAcceptDropEvent& ) +{ + // not interested in this event + return DND_ACTION_NONE; +} + + +sal_Int8 BrowseBox::ExecuteDrop( const BrowserExecuteDropEvent& ) +{ + // not interested in this event + return DND_ACTION_NONE; +} + + +const DataFlavorExVector& BrowseBox::GetDataFlavors() const +{ + if (pDataWin->bCallingDropCallback) + return pDataWin->GetDataFlavorExVector(); + return GetDataFlavorExVector(); +} + + +bool BrowseBox::IsDropFormatSupported( SotClipboardFormatId _nFormat ) +{ + if ( pDataWin->bCallingDropCallback ) + return pDataWin->IsDropFormatSupported( _nFormat ); + + return DropTargetHelper::IsDropFormatSupported( _nFormat ); +} + + +void BrowseBox::Command( const CommandEvent& rEvt ) +{ + if ( !pDataWin->bInCommand ) + Control::Command( rEvt ); +} + + +void BrowseBox::StateChanged( StateChangedType nStateChange ) +{ + Control::StateChanged( nStateChange ); + + if ( StateChangedType::Mirroring == nStateChange ) + { + pDataWin->EnableRTL( IsRTLEnabled() ); + + HeaderBar* pHeaderBar = pDataWin->pHeaderBar; + if ( pHeaderBar ) + pHeaderBar->EnableRTL( IsRTLEnabled() ); + aHScroll->EnableRTL( IsRTLEnabled() ); + if( pVScroll ) + pVScroll->EnableRTL( IsRTLEnabled() ); + Resize(); + } + else if ( StateChangedType::InitShow == nStateChange ) + { + bBootstrapped = true; // must be set first! + + Resize(); + if ( bMultiSelection ) + uRow.pSel->SetTotalRange( Range( 0, nRowCount - 1 ) ); + if ( nRowCount == 0 ) + nCurRow = BROWSER_ENDOFSELECTION; + else if ( nCurRow == BROWSER_ENDOFSELECTION ) + nCurRow = 0; + + + if ( HasFocus() ) + { + bSelectionIsVisible = true; + bHasFocus = true; + } + UpdateScrollbars(); + AutoSizeLastColumn(); + CursorMoved(); + } + else if (StateChangedType::Zoom == nStateChange) + { + pDataWin->SetZoom(GetZoom()); + HeaderBar* pHeaderBar = pDataWin->pHeaderBar; + if (pHeaderBar) + pHeaderBar->SetZoom(GetZoom()); + + // let the columns calculate their new widths and adjust the header bar + for (auto & pCol : mvCols) + { + pCol->ZoomChanged(GetZoom()); + if ( pHeaderBar ) + pHeaderBar->SetItemSize( pCol->GetId(), pCol->Width() ); + } + + // all our controls have to be repositioned + Resize(); + } + else if (StateChangedType::Enable == nStateChange) + { + // do we have a handle column? + bool bHandleCol = !mvCols.empty() && (0 == mvCols[ 0 ]->GetId()); + // do we have a header bar? + bool bHeaderBar = (nullptr != pDataWin->pHeaderBar.get()); + + if ( nTitleLines + && ( !bHeaderBar + || bHandleCol + ) + ) + // we draw the text in our header bar in a color dependent on the enabled state. So if this state changed + // -> redraw + Invalidate(tools::Rectangle(Point(0, 0), Size(GetOutputSizePixel().Width(), GetTitleHeight() - 1))); + } +} + + +void BrowseBox::Select() +{ +} + + +void BrowseBox::DoubleClick( const BrowserMouseEvent & ) +{ +} + + +long BrowseBox::QueryMinimumRowHeight() +{ + return CalcZoom( 5 ); +} + + +void BrowseBox::ImplStartTracking() +{ +} + + +void BrowseBox::ImplEndTracking() +{ +} + + +void BrowseBox::RowHeightChanged() +{ +} + + +void BrowseBox::ColumnResized( sal_uInt16 ) +{ +} + + +void BrowseBox::ColumnMoved( sal_uInt16 ) +{ +} + + +void BrowseBox::StartScroll() +{ + DoHideCursor( "StartScroll" ); +} + + +void BrowseBox::EndScroll() +{ + UpdateScrollbars(); + AutoSizeLastColumn(); + DoShowCursor( "EndScroll" ); +} + + +void BrowseBox::ToggleSelection() +{ + + // selection highlight-toggling allowed? + if ( bHideSelect ) + return; + if ( bNotToggleSel || !IsUpdateMode() || !bSelectionIsVisible ) + return; + + // only highlight painted areas! + bNotToggleSel = true; + + // accumulate areas of rows to highlight + std::vector<tools::Rectangle> aHighlightList; + long nLastRowInRect = 0; // for the CFront + + // don't highlight handle column + BrowserColumn *pFirstCol = mvCols.empty() ? nullptr : mvCols[ 0 ].get(); + long nOfsX = (!pFirstCol || pFirstCol->GetId()) ? 0 : pFirstCol->Width(); + + // accumulate old row selection + long nBottomRow = nTopRow + + pDataWin->GetOutputSizePixel().Height() / GetDataRowHeight(); + if ( nBottomRow > GetRowCount() && GetRowCount() ) + nBottomRow = GetRowCount(); + for ( long nRow = bMultiSelection ? uRow.pSel->FirstSelected() : uRow.nSel; + nRow != BROWSER_ENDOFSELECTION && nRow <= nBottomRow; + nRow = bMultiSelection ? uRow.pSel->NextSelected() : BROWSER_ENDOFSELECTION ) + { + if ( nRow < nTopRow ) + continue; + + tools::Rectangle aAddRect( + Point( nOfsX, (nRow-nTopRow)*GetDataRowHeight() ), + Size( pDataWin->GetSizePixel().Width(), GetDataRowHeight() ) ); + if ( !aHighlightList.empty() && nLastRowInRect == ( nRow - 1 ) ) + aHighlightList[ 0 ].Union( aAddRect ); + else + aHighlightList.emplace( aHighlightList.begin(), aAddRect ); + nLastRowInRect = nRow; + } + + // unhighlight the old selection (if any) + while ( !aHighlightList.empty() ) + { + pDataWin->Invalidate( aHighlightList.back() ); + aHighlightList.pop_back(); + } + + // unhighlight old column selection (if any) + for ( long nColId = pColSel ? pColSel->FirstSelected() : BROWSER_ENDOFSELECTION; + nColId != BROWSER_ENDOFSELECTION; + nColId = pColSel->NextSelected() ) + { + tools::Rectangle aRect( GetFieldRectPixel(nCurRow, + mvCols[ nColId ]->GetId(), + false ) ); + aRect.AdjustLeft( -(MIN_COLUMNWIDTH) ); + aRect.AdjustRight(MIN_COLUMNWIDTH ); + aRect.SetTop( 0 ); + aRect.SetBottom( pDataWin->GetOutputSizePixel().Height() ); + pDataWin->Invalidate( aRect ); + } + + bNotToggleSel = false; +} + + +void BrowseBox::DrawCursor() +{ + bool bReallyHide = false; + if ( bHideCursor == TRISTATE_INDET ) + { + if ( !GetSelectRowCount() && !GetSelectColumnCount() ) + bReallyHide = true; + } + else if ( bHideCursor == TRISTATE_TRUE ) + { + bReallyHide = true; + } + + bReallyHide |= !bSelectionIsVisible || !IsUpdateMode() || bScrolling || nCurRow < 0; + + if (PaintCursorIfHiddenOnce()) + bReallyHide |= ( GetCursorHideCount() > 1 ); + else + bReallyHide |= ( GetCursorHideCount() > 0 ); + + // no cursor on handle column + if ( nCurColId == HandleColumnId ) + nCurColId = GetColumnId(1); + + // calculate cursor rectangle + tools::Rectangle aCursor; + if ( bColumnCursor ) + { + aCursor = GetFieldRectPixel( nCurRow, nCurColId, false ); + aCursor.AdjustLeft( -(MIN_COLUMNWIDTH) ); + aCursor.AdjustRight(1 ); + aCursor.AdjustBottom(1 ); + } + else + aCursor = tools::Rectangle( + Point( ( !mvCols.empty() && mvCols[ 0 ]->GetId() == 0 ) ? + mvCols[ 0 ]->Width() : 0, + (nCurRow - nTopRow) * GetDataRowHeight() + 1 ), + Size( pDataWin->GetOutputSizePixel().Width() + 1, + GetDataRowHeight() - 2 ) ); + if ( bHLines ) + { + if ( !bMultiSelection ) + aCursor.AdjustTop( -1 ); + aCursor.AdjustBottom( -1 ); + } + + if (m_aCursorColor == COL_TRANSPARENT) + { + // on these platforms, the StarView focus works correctly + if ( bReallyHide ) + static_cast<Control*>(pDataWin.get())->HideFocus(); + else + static_cast<Control*>(pDataWin.get())->ShowFocus( aCursor ); + } + else + { + Color rCol = bReallyHide ? pDataWin->GetFillColor() : m_aCursorColor; + Color aOldFillColor = pDataWin->GetFillColor(); + Color aOldLineColor = pDataWin->GetLineColor(); + pDataWin->SetFillColor(); + pDataWin->SetLineColor( rCol ); + pDataWin->DrawRect( aCursor ); + pDataWin->SetLineColor( aOldLineColor ); + pDataWin->SetFillColor( aOldFillColor ); + } +} + + +sal_uLong BrowseBox::GetColumnWidth( sal_uInt16 nId ) const +{ + + sal_uInt16 nItemPos = GetColumnPos( nId ); + if ( nItemPos >= mvCols.size() ) + return 0; + return mvCols[ nItemPos ]->Width(); +} + + +sal_uInt16 BrowseBox::GetColumnId( sal_uInt16 nPos ) const +{ + + if ( nPos >= mvCols.size() ) + return BROWSER_INVALIDID; + return mvCols[ nPos ]->GetId(); +} + + +sal_uInt16 BrowseBox::GetColumnPos( sal_uInt16 nId ) const +{ + for ( size_t nPos = 0; nPos < mvCols.size(); ++nPos ) + if ( mvCols[ nPos ]->GetId() == nId ) + return nPos; + return BROWSER_INVALIDID; +} + + +bool BrowseBox::IsFrozen( sal_uInt16 nColumnId ) const +{ + for (auto const & pCol : mvCols) + if ( pCol->GetId() == nColumnId ) + return pCol->IsFrozen(); + return false; +} + + +void BrowseBox::ExpandRowSelection( const BrowserMouseEvent& rEvt ) +{ + + DoHideCursor( "ExpandRowSelection" ); + + // expand the last selection + if ( bMultiSelection ) + { + Range aJustifiedRange( aSelRange ); + aJustifiedRange.Justify(); + + bool bSelectThis = ( bSelect != aJustifiedRange.IsInside( rEvt.GetRow() ) ); + + if ( aJustifiedRange.IsInside( rEvt.GetRow() ) ) + { + // down and up + while ( rEvt.GetRow() < aSelRange.Max() ) + { // ZTC/Mac bug - don't put these statements together! + SelectRow( aSelRange.Max(), bSelectThis ); + --aSelRange.Max(); + } + while ( rEvt.GetRow() > aSelRange.Max() ) + { // ZTC/Mac bug - don't put these statements together! + SelectRow( aSelRange.Max(), bSelectThis ); + ++aSelRange.Max(); + } + } + else + { + // up and down + bool bOldSelecting = bSelecting; + bSelecting = true; + while ( rEvt.GetRow() < aSelRange.Max() ) + { // ZTC/Mac bug - don't put these statements together! + --aSelRange.Max(); + if ( !IsRowSelected( aSelRange.Max() ) ) + { + SelectRow( aSelRange.Max(), bSelectThis ); + bSelect = true; + } + } + while ( rEvt.GetRow() > aSelRange.Max() ) + { // ZTC/Mac bug - don't put these statements together! + ++aSelRange.Max(); + if ( !IsRowSelected( aSelRange.Max() ) ) + { + SelectRow( aSelRange.Max(), bSelectThis ); + bSelect = true; + } + } + bSelecting = bOldSelecting; + if ( bSelect ) + Select(); + } + } + else + if (!IsRowSelected(rEvt.GetRow())) + SelectRow( rEvt.GetRow() ); + + GoToRow( rEvt.GetRow(), false ); + DoShowCursor( "ExpandRowSelection" ); +} + + +void BrowseBox::Resize() +{ + if ( !bBootstrapped && IsReallyVisible() ) + BrowseBox::StateChanged( StateChangedType::InitShow ); + if ( mvCols.empty() ) + { + pDataWin->bResizeOnPaint = true; + return; + } + pDataWin->bResizeOnPaint = false; + + // calc the size of the scrollbars + sal_uLong nSBSize = GetBarHeight(); + if (IsZoom()) + nSBSize = static_cast<sal_uLong>(nSBSize * static_cast<double>(GetZoom())); + + DoHideCursor( "Resize" ); + sal_uInt16 nOldVisibleRows = 0; + //fdo#42694, post #i111125# GetDataRowHeight() can be 0 + if (GetDataRowHeight()) + nOldVisibleRows = static_cast<sal_uInt16>(pDataWin->GetOutputSizePixel().Height() / GetDataRowHeight() + 1); + + // did we need a horizontal scroll bar or is there a Control Area? + if ( !pDataWin->bNoHScroll && + ( ( mvCols.size() - FrozenColCount() ) > 1 ) ) + aHScroll->Show(); + else + aHScroll->Hide(); + + // calculate the size of the data window + long nDataHeight = GetOutputSizePixel().Height() - GetTitleHeight(); + if ( aHScroll->IsVisible() || ( nControlAreaWidth != USHRT_MAX ) ) + nDataHeight -= nSBSize; + + long nDataWidth = GetOutputSizePixel().Width(); + if ( pVScroll->IsVisible() ) + nDataWidth -= nSBSize; + + // adjust position and size of data window + pDataWin->SetPosSizePixel( + Point( 0, GetTitleHeight() ), + Size( nDataWidth, nDataHeight ) ); + + sal_uInt16 nVisibleRows = 0; + + if (GetDataRowHeight()) + nVisibleRows = static_cast<sal_uInt16>(pDataWin->GetOutputSizePixel().Height() / GetDataRowHeight() + 1); + + // TopRow is unchanged, but the number of visible lines has changed. + if ( nVisibleRows != nOldVisibleRows ) + VisibleRowsChanged(nTopRow, nVisibleRows); + + UpdateScrollbars(); + + // Control-Area + tools::Rectangle aInvalidArea( GetControlArea() ); + aInvalidArea.SetRight( GetOutputSizePixel().Width() ); + aInvalidArea.SetLeft( 0 ); + Invalidate( aInvalidArea ); + + // external header-bar + HeaderBar* pHeaderBar = pDataWin->pHeaderBar; + if ( pHeaderBar ) + { + // take the handle column into account + BrowserColumn *pFirstCol = mvCols[ 0 ].get(); + long nOfsX = pFirstCol->GetId() ? 0 : pFirstCol->Width(); + pHeaderBar->SetPosSizePixel( Point( nOfsX, 0 ), Size( GetOutputSizePixel().Width() - nOfsX, GetTitleHeight() ) ); + } + + AutoSizeLastColumn(); // adjust last column width + DoShowCursor( "Resize" ); +} + + +void BrowseBox::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) +{ + + // initializations + if (!bBootstrapped && IsReallyVisible()) + BrowseBox::StateChanged(StateChangedType::InitShow); + if (mvCols.empty()) + return; + + BrowserColumn *pFirstCol = mvCols[ 0 ].get(); + bool bHandleCol = pFirstCol && pFirstCol->GetId() == 0; + bool bHeaderBar = pDataWin->pHeaderBar.get() != nullptr; + + // draw delimitational lines + if (!pDataWin->bNoHScroll) + rRenderContext.DrawLine(Point(0, aHScroll->GetPosPixel().Y()), + Point(GetOutputSizePixel().Width(), + aHScroll->GetPosPixel().Y())); + + if (nTitleLines) + { + if (!bHeaderBar) + { + rRenderContext.DrawLine(Point(0, GetTitleHeight() - 1), + Point(GetOutputSizePixel().Width(), GetTitleHeight() - 1)); + } + else if (bHandleCol) + { + rRenderContext.DrawLine(Point(0, GetTitleHeight() - 1), + Point(pFirstCol->Width(), GetTitleHeight() - 1)); + } + } + + // Title Bar + // If there is a handle column and if the header bar is available, only + // take the HandleColumn into account + if (!(nTitleLines && (!bHeaderBar || bHandleCol))) + return; + + // iterate through columns to redraw + long nX = 0; + size_t nCol; + for (nCol = 0; nCol < mvCols.size() && nX < rRect.Right(); ++nCol) + { + // skip invisible columns between frozen and scrollable area + if (nCol < nFirstCol && !mvCols[nCol]->IsFrozen()) + nCol = nFirstCol; + + // only the handle column? + if (bHeaderBar && bHandleCol && nCol > 0) + break; + + BrowserColumn* pCol = mvCols[nCol].get(); + + // draw the column and increment position + if ( pCol->Width() > 4 ) + { + ButtonFrame aButtonFrame( Point( nX, 0 ), + Size( pCol->Width()-1, GetTitleHeight()-1 ), + pCol->Title(), !IsEnabled()); + aButtonFrame.Draw(rRenderContext); + rRenderContext.DrawLine(Point(nX + pCol->Width() - 1, 0), + Point(nX + pCol->Width() - 1, GetTitleHeight() - 1)); + } + else + { + rRenderContext.Push(PushFlags::FILLCOLOR); + rRenderContext.SetFillColor(COL_BLACK); + rRenderContext.DrawRect(tools::Rectangle(Point(nX, 0), Size(pCol->Width(), GetTitleHeight() - 1))); + rRenderContext.Pop(); + } + + // skip column + nX += pCol->Width(); + } + + // retouching + if ( !bHeaderBar && nCol == mvCols.size() ) + { + const StyleSettings &rSettings = rRenderContext.GetSettings().GetStyleSettings(); + Color aColFace(rSettings.GetFaceColor()); + rRenderContext.Push(PushFlags::FILLCOLOR | PushFlags::LINECOLOR); + rRenderContext.SetFillColor(aColFace); + rRenderContext.SetLineColor(aColFace); + rRenderContext.DrawRect(tools::Rectangle(Point(nX, 0), + Point(rRect.Right(), GetTitleHeight() - 2 ))); + rRenderContext.Pop(); + } +} + +void BrowseBox::Draw( OutputDevice* pDev, const Point& rPos, DrawFlags nFlags ) +{ + // we need pixel coordinates + Size aRealSize = GetSizePixel(); + Point aRealPos = pDev->LogicToPixel(rPos); + + if ((aRealSize.Width() < 3) || (aRealSize.Height() < 3)) + // we want to have two pixels frame ... + return; + + vcl::Font aFont = pDataWin->GetDrawPixelFont( pDev ); + // the 'normal' painting uses always the data window as device to output to, so we have to calc the new font + // relative to the data wins current settings + + pDev->Push(); + pDev->SetMapMode(); + pDev->SetFont( aFont ); + + // draw a frame + const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings(); + pDev->SetLineColor(rStyleSettings.GetDarkShadowColor()); + pDev->DrawLine(Point(aRealPos.X(), aRealPos.Y()), + Point(aRealPos.X(), aRealPos.Y() + aRealSize.Height() - 1)); + pDev->DrawLine(Point(aRealPos.X(), aRealPos.Y()), + Point(aRealPos.X() + aRealSize.Width() - 1, aRealPos.Y())); + pDev->SetLineColor(rStyleSettings.GetShadowColor()); + pDev->DrawLine(Point(aRealPos.X() + aRealSize.Width() - 1, aRealPos.Y() + 1), + Point(aRealPos.X() + aRealSize.Width() - 1, aRealPos.Y() + aRealSize.Height() - 1)); + pDev->DrawLine(Point(aRealPos.X() + aRealSize.Width() - 1, aRealPos.Y() + aRealSize.Height() - 1), + Point(aRealPos.X() + 1, aRealPos.Y() + aRealSize.Height() - 1)); + + HeaderBar* pBar = pDataWin->pHeaderBar; + + // we're drawing onto a foreign device, so we have to fake the DataRowHeight for the subsequent ImplPaintData + // (as it is based on the settings of our data window, not the foreign device) + if (!nDataRowHeight) + ImpGetDataRowHeight(); + long nHeightLogic = PixelToLogic(Size(0, nDataRowHeight), MapMode(MapUnit::Map10thMM)).Height(); + long nForeignHeightPixel = pDev->LogicToPixel(Size(0, nHeightLogic), MapMode(MapUnit::Map10thMM)).Height(); + + long nOriginalHeight = nDataRowHeight; + nDataRowHeight = nForeignHeightPixel; + + // this counts for the column widths, too + size_t nPos; + for ( nPos = 0; nPos < mvCols.size(); ++nPos ) + { + BrowserColumn* pCurrent = mvCols[ nPos ].get(); + + long nWidthLogic = PixelToLogic(Size(pCurrent->Width(), 0), MapMode(MapUnit::Map10thMM)).Width(); + long nForeignWidthPixel = pDev->LogicToPixel(Size(nWidthLogic, 0), MapMode(MapUnit::Map10thMM)).Width(); + + pCurrent->SetWidth(nForeignWidthPixel, GetZoom()); + if ( pBar ) + pBar->SetItemSize( pCurrent->GetId(), pCurrent->Width() ); + } + + // a smaller area for the content + aRealPos.AdjustX( 1 ); + aRealPos.AdjustY( 1 ); + aRealSize.AdjustWidth( -2 ); + aRealSize.AdjustHeight( -2 ); + + // let the header bar draw itself + if ( pBar ) + { + // the title height with respect to the font set for the given device + long nTitleHeight = PixelToLogic(Size(0, GetTitleHeight()), MapMode(MapUnit::Map10thMM)).Height(); + nTitleHeight = pDev->LogicToPixel(Size(0, nTitleHeight), MapMode(MapUnit::Map10thMM)).Height(); + + BrowserColumn* pFirstCol = !mvCols.empty() ? mvCols[ 0 ].get() : nullptr; + + Point aHeaderPos(pFirstCol && (pFirstCol->GetId() == 0) ? pFirstCol->Width() : 0, 0); + Size aHeaderSize(aRealSize.Width() - aHeaderPos.X(), nTitleHeight); + + aHeaderPos += aRealPos; + // do this before converting to logics ! + + // the header's draw expects logic coordinates, again + aHeaderPos = pDev->PixelToLogic(aHeaderPos); + + Size aOrigSize(pBar->GetSizePixel()); + pBar->SetSizePixel(aHeaderSize); + pBar->Draw(pDev, aHeaderPos, nFlags); + pBar->SetSizePixel(aOrigSize); + + // draw the "upper left cell" (the intersection between the header bar and the handle column) + if (pFirstCol && (pFirstCol->GetId() == 0) && (pFirstCol->Width() > 4)) + { + ButtonFrame aButtonFrame( aRealPos, + Size( pFirstCol->Width()-1, nTitleHeight-1 ), + pFirstCol->Title(), !IsEnabled()); + aButtonFrame.Draw( *pDev ); + + pDev->Push( PushFlags::LINECOLOR ); + pDev->SetLineColor( COL_BLACK ); + + pDev->DrawLine( Point( aRealPos.X(), aRealPos.Y() + nTitleHeight-1 ), + Point( aRealPos.X() + pFirstCol->Width() - 1, aRealPos.Y() + nTitleHeight-1 ) ); + pDev->DrawLine( Point( aRealPos.X() + pFirstCol->Width() - 1, aRealPos.Y() ), + Point( aRealPos.X() + pFirstCol->Width() - 1, aRealPos.Y() + nTitleHeight-1 ) ); + + pDev->Pop(); + } + + aRealPos.AdjustY(aHeaderSize.Height() ); + aRealSize.AdjustHeight( -(aHeaderSize.Height()) ); + } + + // draw our own content (with clipping) + vcl::Region aRegion(tools::Rectangle(aRealPos, aRealSize)); + pDev->SetClipRegion( pDev->PixelToLogic( aRegion ) ); + + // do we have to paint the background + bool bBackground = pDataWin->IsControlBackground(); + if ( bBackground ) + { + tools::Rectangle aRect( aRealPos, aRealSize ); + pDev->SetFillColor( pDataWin->GetControlBackground() ); + pDev->DrawRect( aRect ); + } + + ImplPaintData( *pDev, tools::Rectangle( aRealPos, aRealSize ), true ); + + // restore the column widths/data row height + nDataRowHeight = nOriginalHeight; + for ( nPos = 0; nPos < mvCols.size(); ++nPos ) + { + BrowserColumn* pCurrent = mvCols[ nPos ].get(); + + long nForeignWidthLogic = pDev->PixelToLogic(Size(pCurrent->Width(), 0), MapMode(MapUnit::Map10thMM)).Width(); + long nWidthPixel = LogicToPixel(Size(nForeignWidthLogic, 0), MapMode(MapUnit::Map10thMM)).Width(); + + pCurrent->SetWidth(nWidthPixel, GetZoom()); + if ( pBar ) + pBar->SetItemSize( pCurrent->GetId(), pCurrent->Width() ); + } + + pDev->Pop(); +} + +void BrowseBox::ImplPaintData(OutputDevice& _rOut, const tools::Rectangle& _rRect, bool _bForeignDevice) +{ + Point aOverallAreaPos = _bForeignDevice ? _rRect.TopLeft() : Point(0,0); + Size aOverallAreaSize = _bForeignDevice ? _rRect.GetSize() : pDataWin->GetOutputSizePixel(); + Point aOverallAreaBRPos = _bForeignDevice ? _rRect.BottomRight() : Point( aOverallAreaSize.Width(), aOverallAreaSize.Height() ); + + long nDataRowHeigt = GetDataRowHeight(); + + // compute relative rows to redraw + sal_uLong nRelTopRow = 0; + sal_uLong nRelBottomRow = aOverallAreaSize.Height(); + if (!_bForeignDevice && nDataRowHeigt) + { + nRelTopRow = (static_cast<sal_uLong>(_rRect.Top()) / nDataRowHeigt); + nRelBottomRow = static_cast<sal_uLong>(_rRect.Bottom()) / nDataRowHeigt; + } + + // cache frequently used values + Point aPos( aOverallAreaPos.X(), nRelTopRow * nDataRowHeigt + aOverallAreaPos.Y() ); + _rOut.SetLineColor( COL_WHITE ); + const AllSettings& rAllSets = _rOut.GetSettings(); + const StyleSettings &rSettings = rAllSets.GetStyleSettings(); + const Color &rHighlightTextColor = rSettings.GetHighlightTextColor(); + const Color &rHighlightFillColor = rSettings.GetHighlightColor(); + Color aOldTextColor = _rOut.GetTextColor(); + Color aOldFillColor = _rOut.GetFillColor(); + Color aOldLineColor = _rOut.GetLineColor(); + long nHLineX = 0 == mvCols[ 0 ]->GetId() ? mvCols[ 0 ]->Width() : 0; + nHLineX += aOverallAreaPos.X(); + + Color aDelimiterLineColor( ::svtools::ColorConfig().GetColorValue( ::svtools::CALCGRID ).nColor ); + + // redraw the invalid fields + for ( sal_uLong nRelRow = nRelTopRow; + nRelRow <= nRelBottomRow && static_cast<sal_uLong>(nTopRow)+nRelRow < o3tl::make_unsigned(nRowCount); + ++nRelRow, aPos.AdjustY(nDataRowHeigt ) ) + { + // get row + // check valid area, to be on the safe side: + DBG_ASSERT( static_cast<sal_uInt16>(nTopRow+nRelRow) < nRowCount, "BrowseBox::ImplPaintData: invalid seek" ); + if ( (nTopRow+long(nRelRow)) < 0 || static_cast<sal_uInt16>(nTopRow+nRelRow) >= nRowCount ) + continue; + + // prepare row + sal_uLong nRow = nTopRow+nRelRow; + if ( !SeekRow( nRow) ) { + OSL_FAIL("BrowseBox::ImplPaintData: SeekRow failed"); + } + _rOut.SetClipRegion(); + aPos.setX( aOverallAreaPos.X() ); + + + // #73325# don't paint the row outside the painting rectangle (DG) + // prepare auto-highlight + tools::Rectangle aRowRect( Point( _rRect.TopLeft().X(), aPos.Y() ), + Size( _rRect.GetSize().Width(), nDataRowHeigt ) ); + + bool bRowSelected = !bHideSelect + && IsRowSelected( nRow ); + if ( bRowSelected ) + { + _rOut.SetTextColor( rHighlightTextColor ); + _rOut.SetFillColor( rHighlightFillColor ); + _rOut.SetLineColor(); + _rOut.DrawRect( aRowRect ); + } + + // iterate through columns to redraw + size_t nCol; + for ( nCol = 0; nCol < mvCols.size(); ++nCol ) + { + // get column + BrowserColumn *pCol = mvCols[ nCol ].get(); + + // at end of invalid area + if ( aPos.X() >= _rRect.Right() ) + break; + + // skip invisible columns between frozen and scrollable area + if ( nCol < nFirstCol && !pCol->IsFrozen() ) + { + nCol = nFirstCol; + pCol = (nCol < mvCols.size() ) ? mvCols[ nCol ].get() : nullptr; + if (!pCol) + { // FS - 21.05.99 - 66325 + // actually this has been fixed elsewhere (in the right place), + // but let's make sure... + OSL_FAIL("BrowseBox::PaintData : nFirstCol is probably invalid !"); + break; + } + } + + // prepare Column-AutoHighlight + bool bColAutoHighlight = bColumnCursor + && IsColumnSelected( pCol->GetId() ); + if ( bColAutoHighlight ) + { + _rOut.SetClipRegion(); + _rOut.SetTextColor( rHighlightTextColor ); + _rOut.SetFillColor( rHighlightFillColor ); + _rOut.SetLineColor(); + tools::Rectangle aFieldRect( aPos, + Size( pCol->Width(), nDataRowHeigt ) ); + _rOut.DrawRect( aFieldRect ); + } + + if (!m_bFocusOnlyCursor && (pCol->GetId() == GetCurColumnId()) && (nRow == static_cast<sal_uLong>(GetCurRow()))) + DrawCursor(); + + // draw a single field. + // else something is drawn to, e.g. handle column + if (pCol->Width()) + { + // clip the column's output to the field area + if (_bForeignDevice) + { // (not necessary if painting onto the data window) + Size aFieldSize(pCol->Width(), nDataRowHeigt); + + if (aPos.X() + aFieldSize.Width() > aOverallAreaBRPos.X()) + aFieldSize.setWidth( aOverallAreaBRPos.X() - aPos.X() ); + + if (aPos.Y() + aFieldSize.Height() > aOverallAreaBRPos.Y() + 1) + { + // for non-handle cols we don't clip vertically : we just don't draw the cell if the line isn't completely visible + if (pCol->GetId() != 0) + continue; + aFieldSize.setHeight( aOverallAreaBRPos.Y() + 1 - aPos.Y() ); + } + + vcl::Region aClipToField(tools::Rectangle(aPos, aFieldSize)); + _rOut.SetClipRegion(aClipToField); + } + pCol->Draw( *this, _rOut, aPos ); + if (_bForeignDevice) + _rOut.SetClipRegion(); + } + + // reset Column-auto-highlight + if ( bColAutoHighlight ) + { + _rOut.SetTextColor( aOldTextColor ); + _rOut.SetFillColor( aOldFillColor ); + _rOut.SetLineColor( aOldLineColor ); + } + + // skip column + aPos.AdjustX(pCol->Width() ); + } + + // reset auto-highlight + if ( bRowSelected ) + { + _rOut.SetTextColor( aOldTextColor ); + _rOut.SetFillColor( aOldFillColor ); + _rOut.SetLineColor( aOldLineColor ); + } + + if ( bHLines ) + { + // draw horizontal delimitation lines + _rOut.SetClipRegion(); + _rOut.Push( PushFlags::LINECOLOR ); + _rOut.SetLineColor( aDelimiterLineColor ); + long nY = aPos.Y() + nDataRowHeigt - 1; + if (nY <= aOverallAreaBRPos.Y()) + _rOut.DrawLine( Point( nHLineX, nY ), + Point( bVLines + ? std::min(long(aPos.X() - 1), aOverallAreaBRPos.X()) + : aOverallAreaBRPos.X(), + nY ) ); + _rOut.Pop(); + } + } + + if (aPos.Y() > aOverallAreaBRPos.Y() + 1) + aPos.setY( aOverallAreaBRPos.Y() + 1 ); + // needed for some of the following drawing + + // retouching + _rOut.SetClipRegion(); + aOldLineColor = _rOut.GetLineColor(); + aOldFillColor = _rOut.GetFillColor(); + _rOut.SetFillColor( rSettings.GetFaceColor() ); + if ( !mvCols.empty() && ( mvCols[ 0 ]->GetId() == 0 ) && ( aPos.Y() <= _rRect.Bottom() ) ) + { + // fill rectangle gray below handle column + // DG: fill it only until the end of the drawing rect and not to the end, as this may overpaint handle columns + _rOut.SetLineColor( COL_BLACK ); + _rOut.DrawRect( tools::Rectangle( + Point( aOverallAreaPos.X() - 1, aPos.Y() - 1 ), + Point( aOverallAreaPos.X() + mvCols[ 0 ]->Width() - 1, + _rRect.Bottom() + 1) ) ); + } + _rOut.SetFillColor( aOldFillColor ); + + // draw vertical delimitational line between frozen and scrollable cols + _rOut.SetLineColor( COL_BLACK ); + long nFrozenWidth = GetFrozenWidth()-1; + _rOut.DrawLine( Point( aOverallAreaPos.X() + nFrozenWidth, aPos.Y() ), + Point( aOverallAreaPos.X() + nFrozenWidth, bHLines + ? aPos.Y() - 1 + : aOverallAreaBRPos.Y() ) ); + + // draw vertical delimitational lines? + if ( bVLines ) + { + _rOut.SetLineColor( aDelimiterLineColor ); + Point aVertPos( aOverallAreaPos.X() - 1, aOverallAreaPos.Y() ); + long nDeltaY = aOverallAreaBRPos.Y(); + for ( size_t nCol = 0; nCol < mvCols.size(); ++nCol ) + { + // get column + BrowserColumn *pCol = mvCols[ nCol ].get(); + + // skip invisible columns between frozen and scrollable area + if ( nCol < nFirstCol && !pCol->IsFrozen() ) + { + nCol = nFirstCol; + pCol = mvCols[ nCol ].get(); + } + + // skip column + aVertPos.AdjustX(pCol->Width() ); + + // at end of invalid area + // invalid area is first reached when X > Right + // and not >= + if ( aVertPos.X() > _rRect.Right() ) + break; + + // draw a single line + if ( pCol->GetId() != 0 ) + _rOut.DrawLine( aVertPos, Point( aVertPos.X(), + bHLines + ? aPos.Y() - 1 + : aPos.Y() + nDeltaY ) ); + } + } + + _rOut.SetLineColor( aOldLineColor ); +} + +void BrowseBox::PaintData( vcl::Window const & rWin, vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect ) +{ + if (!bBootstrapped && IsReallyVisible()) + BrowseBox::StateChanged(StateChangedType::InitShow); + + // initializations + if (mvCols.empty() || !rWin.IsUpdateMode()) + return; + if (pDataWin->bResizeOnPaint) + Resize(); + // MI: who was that? Window::Update(); + + ImplPaintData(rRenderContext, rRect, false); +} + +long BrowseBox::GetBarHeight() const +{ + // 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(aStatusBar->GetSizePixel().Height(), GetSettings().GetStyleSettings().GetScrollBarSize()); +} + +void BrowseBox::UpdateScrollbars() +{ + + if ( !bBootstrapped || !IsUpdateMode() ) + return; + + // protect against recursion + if ( pDataWin->bInUpdateScrollbars ) + { + pDataWin->bHadRecursion = true; + return; + } + pDataWin->bInUpdateScrollbars = true; + + // the size of the corner window (and the width of the VSB/height of the HSB) + sal_uLong nCornerSize = GetBarHeight(); + if (IsZoom()) + nCornerSize = static_cast<sal_uLong>(nCornerSize * static_cast<double>(GetZoom())); + + bool bNeedsVScroll = false; + long nMaxRows = 0; + if (GetDataRowHeight()) + { + // needs VScroll? + nMaxRows = (pDataWin->GetSizePixel().Height()) / GetDataRowHeight(); + bNeedsVScroll = pDataWin->bAutoVScroll + ? nTopRow || ( nRowCount > nMaxRows ) + : !pDataWin->bNoVScroll; + } + Size aDataWinSize = pDataWin->GetSizePixel(); + if ( !bNeedsVScroll ) + { + if ( pVScroll->IsVisible() ) + { + pVScroll->Hide(); + Size aNewSize( aDataWinSize ); + aNewSize.setWidth( GetOutputSizePixel().Width() ); + aDataWinSize = aNewSize; + } + } + else if ( !pVScroll->IsVisible() ) + { + Size aNewSize( aDataWinSize ); + aNewSize.setWidth( GetOutputSizePixel().Width() - nCornerSize ); + aDataWinSize = aNewSize; + } + + // needs HScroll? + sal_uLong nLastCol = GetColumnAtXPosPixel( aDataWinSize.Width() - 1 ); + + sal_uInt16 nFrozenCols = FrozenColCount(); + bool bNeedsHScroll = pDataWin->bAutoHScroll + ? ( nFirstCol > nFrozenCols ) || ( nLastCol <= mvCols.size() ) + : !pDataWin->bNoHScroll; + if ( !bNeedsHScroll ) + { + if ( aHScroll->IsVisible() ) + { + aHScroll->Hide(); + } + aDataWinSize.setHeight( GetOutputSizePixel().Height() - GetTitleHeight() ); + if ( nControlAreaWidth != USHRT_MAX ) + aDataWinSize.AdjustHeight( -sal_Int32(nCornerSize) ); + } + else if ( !aHScroll->IsVisible() ) + { + Size aNewSize( aDataWinSize ); + aNewSize.setHeight( GetOutputSizePixel().Height() - GetTitleHeight() - nCornerSize ); + aDataWinSize = aNewSize; + } + + // adjust position and Width of horizontal scrollbar + sal_uLong nHScrX = nControlAreaWidth == USHRT_MAX + ? 0 + : nControlAreaWidth; + + aHScroll->SetPosSizePixel( + Point( nHScrX, GetOutputSizePixel().Height() - nCornerSize ), + Size( aDataWinSize.Width() - nHScrX, nCornerSize ) ); + + // total scrollable columns + short nScrollCols = short(mvCols.size()) - static_cast<short>(nFrozenCols); + + // visible columns + short nVisibleHSize = nLastCol == BROWSER_INVALIDID + ? static_cast<short>( mvCols.size() - nFirstCol ) + : static_cast<short>( nLastCol - nFirstCol ); + + short nRange = std::max( nScrollCols, short(0) ); + aHScroll->SetVisibleSize( nVisibleHSize ); + aHScroll->SetRange( Range( 0, nRange )); + if ( bNeedsHScroll && !aHScroll->IsVisible() ) + aHScroll->Show(); + + // adjust position and height of vertical scrollbar + pVScroll->SetPageSize( nMaxRows ); + + if ( nTopRow > nRowCount ) + { + nTopRow = nRowCount - 1; + OSL_FAIL("BrowseBox: nTopRow > nRowCount"); + } + + if ( pVScroll->GetThumbPos() != nTopRow ) + pVScroll->SetThumbPos( nTopRow ); + long nVisibleSize = std::min( std::min( nRowCount, nMaxRows ), long(nRowCount-nTopRow) ); + pVScroll->SetVisibleSize( nVisibleSize ? nVisibleSize : 1 ); + pVScroll->SetRange( Range( 0, nRowCount ) ); + pVScroll->SetPosSizePixel( + Point( aDataWinSize.Width(), GetTitleHeight() ), + Size( nCornerSize, aDataWinSize.Height()) ); + long nLclDataRowHeight = GetDataRowHeight(); + if ( nLclDataRowHeight > 0 && nRowCount < long( aDataWinSize.Height() / nLclDataRowHeight ) ) + ScrollRows( -nTopRow ); + if ( bNeedsVScroll && !pVScroll->IsVisible() ) + pVScroll->Show(); + + pDataWin->SetPosSizePixel( + Point( 0, GetTitleHeight() ), + aDataWinSize ); + + // needs corner-window? + // (do that AFTER positioning BOTH scrollbars) + sal_uLong nActualCorderWidth = 0; + if (aHScroll->IsVisible() && pVScroll && pVScroll->IsVisible() ) + { + // if we have both scrollbars, the corner window fills the point of intersection of these two + nActualCorderWidth = nCornerSize; + } + else if ( !aHScroll->IsVisible() && ( nControlAreaWidth != USHRT_MAX ) ) + { + // if we have no horizontal scrollbar, but a control area, we need the corner window to + // fill the space between the control are and the right border + nActualCorderWidth = GetOutputSizePixel().Width() - nControlAreaWidth; + } + if ( nActualCorderWidth ) + { + if ( !pDataWin->pCornerWin ) + pDataWin->pCornerWin = VclPtr<ScrollBarBox>::Create( this, 0 ); + pDataWin->pCornerWin->SetPosSizePixel( + Point( GetOutputSizePixel().Width() - nActualCorderWidth, aHScroll->GetPosPixel().Y() ), + Size( nActualCorderWidth, nCornerSize ) ); + pDataWin->pCornerWin->Show(); + } + else + pDataWin->pCornerWin.disposeAndClear(); + + // scroll headerbar, if necessary + if ( pDataWin->pHeaderBar ) + { + long nWidth = 0; + for ( size_t nCol = 0; + nCol < mvCols.size() && nCol < nFirstCol; + ++nCol ) + { + // not the handle column + if ( mvCols[ nCol ]->GetId() ) + nWidth += mvCols[ nCol ]->Width(); + } + + pDataWin->pHeaderBar->SetOffset( nWidth ); + } + + pDataWin->bInUpdateScrollbars = false; + if ( pDataWin->bHadRecursion ) + { + pDataWin->bHadRecursion = false; + UpdateScrollbars(); + } +} + + +void BrowseBox::SetUpdateMode( bool bUpdate ) +{ + + bool bWasUpdate = IsUpdateMode(); + if ( bWasUpdate == bUpdate ) + return; + + Control::SetUpdateMode( bUpdate ); + // If WB_CLIPCHILDREN is st at the BrowseBox (to minimize flicker), + // the data window is not invalidated by SetUpdateMode. + if( bUpdate ) + pDataWin->Invalidate(); + pDataWin->SetUpdateMode( bUpdate ); + + + if ( bUpdate ) + { + if ( bBootstrapped ) + { + UpdateScrollbars(); + AutoSizeLastColumn(); + } + DoShowCursor( "SetUpdateMode" ); + } + else + DoHideCursor( "SetUpdateMode" ); +} + + +bool BrowseBox::GetUpdateMode() const +{ + + return pDataWin->IsUpdateMode(); +} + + +long BrowseBox::GetFrozenWidth() const +{ + + long nWidth = 0; + for ( size_t nCol = 0; + nCol < mvCols.size() && mvCols[ nCol ]->IsFrozen(); + ++nCol ) + nWidth += mvCols[ nCol ]->Width(); + return nWidth; +} + + +void BrowseBox::ColumnInserted( sal_uInt16 nPos ) +{ + if ( pColSel ) + pColSel->Insert( nPos ); + UpdateScrollbars(); +} + + +sal_uInt16 BrowseBox::FrozenColCount() const +{ + std::size_t nCol; + for ( nCol = 0; + nCol < mvCols.size() && mvCols[ nCol ]->IsFrozen(); + ++nCol ) + /* empty loop */; + return nCol; //TODO: BrowserColumns::size_type -> sal_uInt16! +} + + +IMPL_LINK(BrowseBox, ScrollHdl, ScrollBar*, pBar, void) +{ + if ( pBar->GetDelta() == 0 ) + return; + + if ( pBar == aHScroll.get() ) + ScrollColumns( aHScroll->GetDelta() ); + if ( pBar == pVScroll ) + ScrollRows( pVScroll->GetDelta() ); +} + + +IMPL_LINK( BrowseBox, StartDragHdl, HeaderBar*, pBar, void ) +{ + pBar->SetDragSize( pDataWin->GetOutputSizePixel().Height() ); +} + + +// usually only the first column was resized + +void BrowseBox::MouseButtonDown( const MouseEvent& rEvt ) +{ + + GrabFocus(); + + // only mouse events in the title-line are supported + const Point &rEvtPos = rEvt.GetPosPixel(); + if ( rEvtPos.Y() >= GetTitleHeight() ) + return; + + long nX = 0; + long nWidth = GetOutputSizePixel().Width(); + for ( size_t nCol = 0; nCol < mvCols.size() && nX < nWidth; ++nCol ) + { + // is this column visible? + BrowserColumn *pCol = mvCols[ nCol ].get(); + if ( pCol->IsFrozen() || nCol >= nFirstCol ) + { + // compute right end of column + long nR = nX + pCol->Width() - 1; + + // at the end of a column (and not handle column)? + if ( pCol->GetId() && std::abs( nR - rEvtPos.X() ) < 2 ) + { + // start resizing the column + bResizing = true; + nResizeCol = nCol; + nDragX = nResizeX = rEvtPos.X(); + SetPointer( PointerStyle::HSplit ); + CaptureMouse(); + pDataWin->DrawLine( Point( nDragX, 0 ), + Point( nDragX, pDataWin->GetSizePixel().Height() ) ); + nMinResizeX = nX + MIN_COLUMNWIDTH; + return; + } + else if ( nX < rEvtPos.X() && nR > rEvtPos.X() ) + { + MouseButtonDown( BrowserMouseEvent( + this, rEvt, -1, nCol, pCol->GetId(), tools::Rectangle() ) ); + return; + } + nX = nR + 1; + } + } + + // event occurred out of data area + if ( rEvt.IsRight() ) + pDataWin->Command( + CommandEvent( Point( 1, LONG_MAX ), CommandEventId::ContextMenu, true ) ); + else + SetNoSelection(); +} + + +void BrowseBox::MouseMove( const MouseEvent& rEvt ) +{ + SAL_INFO("svtools", "BrowseBox::MouseMove( MouseEvent )" ); + + PointerStyle aNewPointer = PointerStyle::Arrow; + + sal_uInt16 nX = 0; + for ( size_t nCol = 0; + nCol < mvCols.size() && + ( nX + mvCols[ nCol ]->Width() ) < o3tl::make_unsigned(GetOutputSizePixel().Width()); + ++nCol ) + // is this column visible? + if ( mvCols[ nCol ]->IsFrozen() || nCol >= nFirstCol ) + { + // compute right end of column + BrowserColumn *pCol = mvCols[ nCol ].get(); + sal_uInt16 nR = static_cast<sal_uInt16>(nX + pCol->Width() - 1); + + // show resize-pointer? + if ( bResizing || ( pCol->GetId() && + std::abs( static_cast<long>(nR) - rEvt.GetPosPixel().X() ) < MIN_COLUMNWIDTH ) ) + { + aNewPointer = PointerStyle::HSplit; + if ( bResizing ) + { + // delete old auxiliary line + pDataWin->HideTracking() ; + + // check allowed width and new delta + nDragX = std::max( rEvt.GetPosPixel().X(), nMinResizeX ); + long nDeltaX = nDragX - nResizeX; + sal_uInt16 nId = GetColumnId(nResizeCol); + sal_uLong nOldWidth = GetColumnWidth(nId); + nDragX = nOldWidth + nDeltaX + nResizeX - nOldWidth; + + // draw new auxiliary line + pDataWin->ShowTracking( tools::Rectangle( Point( nDragX, 0 ), + Size( 1, pDataWin->GetSizePixel().Height() ) ), + ShowTrackFlags::Split|ShowTrackFlags::TrackWindow ); + } + + } + + nX = nR + 1; + } + + SetPointer( aNewPointer ); +} + + +void BrowseBox::MouseButtonUp( const MouseEvent & rEvt ) +{ + + if ( bResizing ) + { + // delete auxiliary line + pDataWin->HideTracking(); + + // width changed? + nDragX = std::max( rEvt.GetPosPixel().X(), nMinResizeX ); + if ( (nDragX - nResizeX) != static_cast<long>(mvCols[ nResizeCol ]->Width()) ) + { + // resize column + long nMaxX = pDataWin->GetSizePixel().Width(); + nDragX = std::min( nDragX, nMaxX ); + long nDeltaX = nDragX - nResizeX; + sal_uInt16 nId = GetColumnId(nResizeCol); + SetColumnWidth( GetColumnId(nResizeCol), GetColumnWidth(nId) + nDeltaX ); + ColumnResized( nId ); + } + + // end action + SetPointer( PointerStyle::Arrow ); + ReleaseMouse(); + bResizing = false; + } + else + MouseButtonUp( BrowserMouseEvent( pDataWin, + MouseEvent( Point( rEvt.GetPosPixel().X(), + rEvt.GetPosPixel().Y() - pDataWin->GetPosPixel().Y() ), + rEvt.GetClicks(), rEvt.GetMode(), rEvt.GetButtons(), + rEvt.GetModifier() ) ) ); +} + + +static bool bExtendedMode = false; +static bool bFieldMode = false; + +void BrowseBox::MouseButtonDown( const BrowserMouseEvent& rEvt ) +{ + + GrabFocus(); + + // adjust selection while and after double-click + if ( rEvt.GetClicks() == 2 ) + { + SetNoSelection(); + if ( rEvt.GetRow() >= 0 ) + { + GoToRow( rEvt.GetRow() ); + SelectRow( rEvt.GetRow(), true, false ); + } + else + { + if ( bColumnCursor && rEvt.GetColumn() != 0 ) + { + if ( rEvt.GetColumn() < mvCols.size() ) + SelectColumnPos( rEvt.GetColumn(), true, false); + } + } + DoubleClick( rEvt ); + } + // selections + else if ( ( rEvt.GetMode() & ( MouseEventModifiers::SELECT | MouseEventModifiers::SIMPLECLICK ) ) && + ( bColumnCursor || rEvt.GetRow() >= 0 ) ) + { + if ( rEvt.GetClicks() == 1 ) + { + // initialise flags + bHit = false; + + // selection out of range? + if ( rEvt.GetRow() >= nRowCount || + rEvt.GetColumnId() == BROWSER_INVALIDID ) + { + SetNoSelection(); + return; + } + + // while selecting, no cursor + bSelecting = true; + DoHideCursor( "MouseButtonDown" ); + + // DataRow? + if ( rEvt.GetRow() >= 0 ) + { + // line selection? + if ( rEvt.GetColumnId() == HandleColumnId || !bColumnCursor ) + { + if ( bMultiSelection ) + { + // remove column-selection, if exists + if ( pColSel && pColSel->GetSelectCount() ) + { + ToggleSelection(); + if ( bMultiSelection ) + uRow.pSel->SelectAll(false); + else + uRow.nSel = BROWSER_ENDOFSELECTION; + if ( pColSel ) + pColSel->SelectAll(false); + bSelect = true; + } + + // expanding mode? + if ( rEvt.GetMode() & MouseEventModifiers::RANGESELECT ) + { + // select the further touched rows too + bSelect = true; + ExpandRowSelection( rEvt ); + return; + } + + // click in the selected area? + else if ( IsRowSelected( rEvt.GetRow() ) ) + { + // wait for Drag&Drop + bHit = true; + bExtendedMode = bool( rEvt.GetMode() & MouseEventModifiers::MULTISELECT ); + return; + } + + // extension mode? + else if ( rEvt.GetMode() & MouseEventModifiers::MULTISELECT ) + { + // determine the new selection range + // and selection/deselection + aSelRange = Range( rEvt.GetRow(), rEvt.GetRow() ); + SelectRow( rEvt.GetRow(), + !uRow.pSel->IsSelected( rEvt.GetRow() ) ); + bSelect = true; + return; + } + } + + // select directly + SetNoSelection(); + GoToRow( rEvt.GetRow() ); + SelectRow( rEvt.GetRow() ); + aSelRange = Range( rEvt.GetRow(), rEvt.GetRow() ); + bSelect = true; + } + else // Column/Field-Selection + { + // click in selected column + if ( IsColumnSelected( rEvt.GetColumn() ) || + IsRowSelected( rEvt.GetRow() ) ) + { + bHit = true; + bFieldMode = true; + return; + } + + SetNoSelection(); + GoToRowColumnId( rEvt.GetRow(), rEvt.GetColumnId() ); + bSelect = true; + } + } + else + { + if ( bMultiSelection && rEvt.GetColumnId() == HandleColumnId ) + { + // toggle all-selection + if ( uRow.pSel->GetSelectCount() > ( GetRowCount() / 2 ) ) + SetNoSelection(); + else + SelectAll(); + } + else + SelectColumnPos( GetColumnPos(rEvt.GetColumnId()), true, false); + } + + // turn cursor on again, if necessary + bSelecting = false; + DoShowCursor( "MouseButtonDown" ); + if ( bSelect ) + Select(); + } + } +} + + +void BrowseBox::MouseButtonUp( const BrowserMouseEvent &rEvt ) +{ + + // D&D was possible, but did not occur + if ( bHit ) + { + aSelRange = Range( rEvt.GetRow(), rEvt.GetRow() ); + if ( bExtendedMode ) + SelectRow( rEvt.GetRow(), false ); + else + { + SetNoSelection(); + if ( bFieldMode ) + GoToRowColumnId( rEvt.GetRow(), rEvt.GetColumnId() ); + else + { + GoToRow( rEvt.GetRow() ); + SelectRow( rEvt.GetRow() ); + } + } + bSelect = true; + bExtendedMode = false; + bFieldMode = false; + bHit = false; + } + + // activate cursor + if ( bSelecting ) + { + bSelecting = false; + DoShowCursor( "MouseButtonUp" ); + if ( bSelect ) + Select(); + } +} + + +void BrowseBox::KeyInput( const KeyEvent& rEvt ) +{ + if ( !ProcessKey( rEvt ) ) + Control::KeyInput( rEvt ); +} + + +bool BrowseBox::ProcessKey( const KeyEvent& rEvt ) +{ + + sal_uInt16 nCode = rEvt.GetKeyCode().GetCode(); + bool bShift = rEvt.GetKeyCode().IsShift(); + bool bCtrl = rEvt.GetKeyCode().IsMod1(); + bool bAlt = rEvt.GetKeyCode().IsMod2(); + + sal_uInt16 nId = BROWSER_NONE; + + if ( !bAlt && !bCtrl && !bShift ) + { + switch ( nCode ) + { + case KEY_DOWN: nId = BROWSER_CURSORDOWN; break; + case KEY_UP: nId = BROWSER_CURSORUP; break; + case KEY_HOME: nId = BROWSER_CURSORHOME; break; + case KEY_END: nId = BROWSER_CURSOREND; break; + case KEY_TAB: + if ( !bColumnCursor ) + break; + [[fallthrough]]; + case KEY_RIGHT: nId = BROWSER_CURSORRIGHT; break; + case KEY_LEFT: nId = BROWSER_CURSORLEFT; break; + case KEY_SPACE: nId = BROWSER_SELECT; break; + } + if ( BROWSER_NONE != nId ) + SetNoSelection(); + + switch ( nCode ) + { + case KEY_PAGEDOWN: nId = BROWSER_CURSORPAGEDOWN; break; + case KEY_PAGEUP: nId = BROWSER_CURSORPAGEUP; break; + } + } + + if ( !bAlt && !bCtrl && bShift ) + switch ( nCode ) + { + case KEY_DOWN: nId = BROWSER_SELECTDOWN; break; + case KEY_UP: nId = BROWSER_SELECTUP; break; + case KEY_TAB: + if ( !bColumnCursor ) + break; + nId = BROWSER_CURSORLEFT; break; + case KEY_HOME: nId = BROWSER_SELECTHOME; break; + case KEY_END: nId = BROWSER_SELECTEND; break; + } + + + if ( !bAlt && bCtrl && !bShift ) + switch ( nCode ) + { + case KEY_DOWN: nId = BROWSER_CURSORDOWN; break; + case KEY_UP: nId = BROWSER_CURSORUP; break; + case KEY_PAGEDOWN: nId = BROWSER_CURSORENDOFFILE; break; + case KEY_PAGEUP: nId = BROWSER_CURSORTOPOFFILE; break; + case KEY_HOME: nId = BROWSER_CURSORTOPOFSCREEN; break; + case KEY_END: nId = BROWSER_CURSORENDOFSCREEN; break; + case KEY_SPACE: nId = BROWSER_ENHANCESELECTION; break; + case KEY_LEFT: nId = BROWSER_MOVECOLUMNLEFT; break; + case KEY_RIGHT: nId = BROWSER_MOVECOLUMNRIGHT; break; + } + + if ( nId != BROWSER_NONE ) + Dispatch( nId ); + return nId != BROWSER_NONE; +} + + +void BrowseBox::Dispatch( sal_uInt16 nId ) +{ + + long nRowsOnPage = pDataWin->GetSizePixel().Height() / GetDataRowHeight(); + + switch ( nId ) + { + case BROWSER_SELECTCOLUMN: + if ( ColCount() ) + SelectColumnId( GetCurColumnId() ); + break; + + case BROWSER_CURSORDOWN: + if ( ( GetCurRow() + 1 ) < nRowCount ) + GoToRow( GetCurRow() + 1, false ); + break; + case BROWSER_CURSORUP: + if ( GetCurRow() > 0 ) + GoToRow( GetCurRow() - 1, false ); + break; + case BROWSER_SELECTHOME: + if ( GetRowCount() ) + { + DoHideCursor( "BROWSER_SELECTHOME" ); + for ( long nRow = GetCurRow(); nRow >= 0; --nRow ) + SelectRow( nRow ); + GoToRow( 0, true ); + DoShowCursor( "BROWSER_SELECTHOME" ); + } + break; + case BROWSER_SELECTEND: + if ( GetRowCount() ) + { + DoHideCursor( "BROWSER_SELECTEND" ); + long nRows = GetRowCount(); + for ( long nRow = GetCurRow(); nRow < nRows; ++nRow ) + SelectRow( nRow ); + GoToRow( GetRowCount() - 1, true ); + DoShowCursor( "BROWSER_SELECTEND" ); + } + break; + case BROWSER_SELECTDOWN: + { + if ( GetRowCount() && ( GetCurRow() + 1 ) < nRowCount ) + { + // deselect the current row, if it isn't the first + // and there is no other selected row above + long nRow = GetCurRow(); + bool bLocalSelect = ( !IsRowSelected( nRow ) || + GetSelectRowCount() == 1 || IsRowSelected( nRow - 1 ) ); + SelectRow( nRow, bLocalSelect ); + bool bDone = GoToRow( GetCurRow() + 1, false ); + if ( bDone ) + SelectRow( GetCurRow() ); + } + else + ScrollRows( 1 ); + break; + } + case BROWSER_SELECTUP: + if ( GetRowCount() ) + { + // deselect the current row, if it isn't the first + // and there is no other selected row under + long nRow = GetCurRow(); + bool bLocalSelect = ( !IsRowSelected( nRow ) || + GetSelectRowCount() == 1 || IsRowSelected( nRow + 1 ) ); + SelectRow( nCurRow, bLocalSelect ); + bool bDone = GoToRow( nRow - 1, false ); + if ( bDone ) + SelectRow( GetCurRow() ); + } + break; + case BROWSER_CURSORPAGEDOWN: + ScrollRows( nRowsOnPage ); + break; + case BROWSER_CURSORPAGEUP: + ScrollRows( -nRowsOnPage ); + break; + case BROWSER_CURSOREND: + if ( bColumnCursor ) + { + sal_uInt16 nNewId = GetColumnId(ColCount() -1); + nNewId != HandleColumnId && GoToColumnId( nNewId ); + break; + } + [[fallthrough]]; + case BROWSER_CURSORENDOFFILE: + GoToRow( nRowCount - 1, false ); + break; + case BROWSER_CURSORRIGHT: + if ( bColumnCursor ) + { + sal_uInt16 nNewPos = GetColumnPos( GetCurColumnId() ) + 1; + sal_uInt16 nNewId = GetColumnId( nNewPos ); + if (nNewId != BROWSER_INVALIDID) // At end of row ? + GoToColumnId( nNewId ); + else + { + sal_uInt16 nColId = GetColumnId(0); + if ( nColId == BROWSER_INVALIDID || nColId == HandleColumnId ) + nColId = GetColumnId(1); + if ( GetRowCount() ) + { + if ( nCurRow < GetRowCount() - 1 ) + { + GoToRowColumnId( nCurRow + 1, nColId ); + } + } + else if ( ColCount() ) + GoToColumnId( nColId ); + } + } + else + ScrollColumns( 1 ); + break; + case BROWSER_CURSORHOME: + if ( bColumnCursor ) + { + sal_uInt16 nNewId = GetColumnId(1); + if (nNewId != HandleColumnId) + { + GoToColumnId( nNewId ); + } + break; + } + [[fallthrough]]; + case BROWSER_CURSORTOPOFFILE: + GoToRow( 0, false ); + break; + case BROWSER_CURSORLEFT: + if ( bColumnCursor ) + { + sal_uInt16 nNewPos = GetColumnPos( GetCurColumnId() ) - 1; + sal_uInt16 nNewId = GetColumnId( nNewPos ); + if (nNewId != HandleColumnId) + GoToColumnId( nNewId ); + else + { + if ( GetRowCount() ) + { + if (nCurRow > 0) + { + GoToRowColumnId(nCurRow - 1, GetColumnId(ColCount() -1)); + } + } + else if ( ColCount() ) + GoToColumnId( GetColumnId(ColCount() -1) ); + } + } + else + ScrollColumns( -1 ); + break; + case BROWSER_ENHANCESELECTION: + if ( GetRowCount() ) + SelectRow( GetCurRow(), !IsRowSelected( GetCurRow() ) ); + break; + case BROWSER_SELECT: + if ( GetRowCount() ) + SelectRow( GetCurRow(), !IsRowSelected( GetCurRow() ), false ); + break; + case BROWSER_MOVECOLUMNLEFT: + case BROWSER_MOVECOLUMNRIGHT: + { // check if column moving is allowed + BrowserHeader* pHeaderBar = pDataWin->pHeaderBar; + if ( pHeaderBar && pHeaderBar->IsDragable() ) + { + sal_uInt16 nColId = GetCurColumnId(); + bool bColumnSelected = IsColumnSelected(nColId); + sal_uInt16 nNewPos = GetColumnPos(nColId); + bool bMoveAllowed = false; + if ( BROWSER_MOVECOLUMNLEFT == nId && nNewPos > 1 ) + { + --nNewPos; + bMoveAllowed = true; + } + else if ( BROWSER_MOVECOLUMNRIGHT == nId && nNewPos < (ColCount()-1) ) + { + ++nNewPos; + bMoveAllowed = true; + } + + if ( bMoveAllowed ) + { + SetColumnPos( nColId, nNewPos ); + ColumnMoved( nColId ); + MakeFieldVisible(GetCurRow(), nColId); + if ( bColumnSelected ) + SelectColumnId(nColId); + } + } + } + break; + } +} + + +void BrowseBox::SetCursorColor(const Color& _rCol) +{ + if (_rCol == m_aCursorColor) + return; + + // ensure the cursor is hidden + DoHideCursor("SetCursorColor"); + if (!m_bFocusOnlyCursor) + DoHideCursor("SetCursorColor - force"); + + m_aCursorColor = _rCol; + + if (!m_bFocusOnlyCursor) + DoShowCursor("SetCursorColor - force"); + DoShowCursor("SetCursorColor"); +} + +tools::Rectangle BrowseBox::calcHeaderRect(bool _bIsColumnBar, bool _bOnScreen) +{ + vcl::Window* pParent = nullptr; + if ( !_bOnScreen ) + pParent = GetAccessibleParentWindow(); + + Point aTopLeft; + long nWidth; + long nHeight; + if ( _bIsColumnBar ) + { + nWidth = pDataWin->GetOutputSizePixel().Width(); + nHeight = GetDataRowHeight(); + } + else + { + aTopLeft.setY( GetDataRowHeight() ); + nWidth = GetColumnWidth(0); + nHeight = GetWindowExtentsRelative( pParent ).GetHeight() - aTopLeft.Y() - GetControlArea().GetSize().Height(); + } + aTopLeft += GetWindowExtentsRelative( pParent ).TopLeft(); + return tools::Rectangle(aTopLeft,Size(nWidth,nHeight)); +} + +tools::Rectangle BrowseBox::calcTableRect(bool _bOnScreen) +{ + vcl::Window* pParent = nullptr; + if ( !_bOnScreen ) + pParent = GetAccessibleParentWindow(); + + tools::Rectangle aRect( GetWindowExtentsRelative( pParent ) ); + tools::Rectangle aRowBar = calcHeaderRect(false, pParent == nullptr); + + long nX = aRowBar.Right() - aRect.Left(); + long nY = aRowBar.Top() - aRect.Top(); + Size aSize(aRect.GetSize()); + + return tools::Rectangle(aRowBar.TopRight(), Size(aSize.Width() - nX, aSize.Height() - nY - GetBarHeight()) ); +} + +tools::Rectangle BrowseBox::GetFieldRectPixelAbs( sal_Int32 _nRowId, sal_uInt16 _nColId, bool /*_bIsHeader*/, bool _bOnScreen ) +{ + vcl::Window* pParent = nullptr; + if ( !_bOnScreen ) + pParent = GetAccessibleParentWindow(); + + tools::Rectangle aRect = GetFieldRectPixel(_nRowId,_nColId,_bOnScreen); + + Point aTopLeft = aRect.TopLeft(); + aTopLeft += GetWindowExtentsRelative( pParent ).TopLeft(); + + return tools::Rectangle(aTopLeft,aRect.GetSize()); +} + +// ------------------------------------------------------------------------- EOF + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svtools/source/brwbox/brwbox3.cxx b/svtools/source/brwbox/brwbox3.cxx new file mode 100644 index 000000000..0c4f60316 --- /dev/null +++ b/svtools/source/brwbox/brwbox3.cxx @@ -0,0 +1,559 @@ +/* -*- 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 . + */ + +#include <svtools/brwbox.hxx> +#include <vcl/AccessibleBrowseBoxObjType.hxx> +#include <vcl/accessiblefactory.hxx> +#include <unotools/accessiblestatesethelper.hxx> +#include <sal/log.hxx> +#include <tools/debug.hxx> +#include <tools/multisel.hxx> +#include "brwimpl.hxx" +#include <com/sun/star/accessibility/AccessibleStateType.hpp> + +// Accessibility ============================================================== + +using namespace ::com::sun::star::uno; +using ::com::sun::star::accessibility::XAccessible; +using namespace ::com::sun::star::accessibility; + + +namespace svt +{ + using namespace ::com::sun::star::lang; + using namespace utl; + + static Reference< XAccessible > getHeaderCell( BrowseBoxImpl::THeaderCellMap& _raHeaderCells, + sal_Int32 _nPos, + vcl::AccessibleBrowseBoxObjType _eType, + const Reference< XAccessible >& _rParent, + BrowseBox& _rBrowseBox, + vcl::IAccessibleFactory const & rFactory + ) + { + Reference< XAccessible > xRet; + BrowseBoxImpl::THeaderCellMap::iterator aFind = _raHeaderCells.find( _nPos ); + if ( aFind == _raHeaderCells.end() ) + { + Reference< XAccessible > xAccessible = rFactory.createAccessibleBrowseBoxHeaderCell( + _nPos, + _rParent, + _rBrowseBox, + nullptr, + _eType + ); + aFind = _raHeaderCells.emplace( _nPos, xAccessible ).first; + } + if ( aFind != _raHeaderCells.end() ) + xRet = aFind->second; + return xRet; + } + + + Reference< XAccessible > BrowseBoxImpl::getAccessibleHeaderBar( vcl::AccessibleBrowseBoxObjType _eObjType ) + { + if ( m_pAccessible && m_pAccessible->isAlive() ) + return m_pAccessible->getHeaderBar( _eObjType ); + return nullptr; + } + + + Reference< XAccessible > BrowseBoxImpl::getAccessibleTable( ) + { + if ( m_pAccessible && m_pAccessible->isAlive() ) + return m_pAccessible->getTable( ); + return nullptr; + } +} + + +Reference< XAccessible > BrowseBox::CreateAccessible() +{ + vcl::Window* pParent = GetAccessibleParentWindow(); + DBG_ASSERT( pParent, "BrowseBox::CreateAccessible - parent not found" ); + + if( pParent && !m_pImpl->m_pAccessible) + { + Reference< XAccessible > xAccParent = pParent->GetAccessible(); + if( xAccParent.is() ) + { + m_pImpl->m_pAccessible = getAccessibleFactory().createAccessibleBrowseBox( + xAccParent, *this + ); + } + } + + Reference< XAccessible > xAccessible; + if ( m_pImpl->m_pAccessible ) + xAccessible = m_pImpl->m_pAccessible->getMyself(); + + return xAccessible; +} + + +// Children ------------------------------------------------------------------- + +Reference< XAccessible > BrowseBox::CreateAccessibleCell( sal_Int32 _nRow, sal_uInt16 _nColumnPos ) +{ + // BBINDEX_TABLE must be the table + OSL_ENSURE(m_pImpl->m_pAccessible,"Invalid call: Accessible is null"); + + return m_pImpl->m_aFactoryAccess.getFactory().createAccessibleBrowseBoxTableCell( + m_pImpl->getAccessibleTable(), + *this, + nullptr, + _nRow, + _nColumnPos, + OFFSET_DEFAULT + ); +} + + +Reference< XAccessible > BrowseBox::CreateAccessibleRowHeader( sal_Int32 _nRow ) +{ + return svt::getHeaderCell( + m_pImpl->m_aRowHeaderCellMap, + _nRow, + vcl::BBTYPE_ROWHEADERCELL, + m_pImpl->getAccessibleHeaderBar(vcl::BBTYPE_ROWHEADERBAR), + *this, + m_pImpl->m_aFactoryAccess.getFactory() + ); +} + + +Reference< XAccessible > BrowseBox::CreateAccessibleColumnHeader( sal_uInt16 _nColumnPos ) +{ + return svt::getHeaderCell( + m_pImpl->m_aColHeaderCellMap, + _nColumnPos, + vcl::BBTYPE_COLUMNHEADERCELL, + m_pImpl->getAccessibleHeaderBar(vcl::BBTYPE_COLUMNHEADERBAR), + *this, + m_pImpl->m_aFactoryAccess.getFactory() + ); +} + + +sal_Int32 BrowseBox::GetAccessibleControlCount() const +{ + return 0; +} + + +Reference< XAccessible > BrowseBox::CreateAccessibleControl( sal_Int32 ) +{ + SAL_WARN( "svtools", "BrowseBox::CreateAccessibleControl: to be overwritten!" ); + return nullptr; +} + + +// Conversions ---------------------------------------------------------------- + +bool BrowseBox::ConvertPointToCellAddress( + sal_Int32& rnRow, sal_uInt16& rnColumnPos, const Point& rPoint ) +{ + //! TODO has to be checked + rnRow = GetRowAtYPosPixel(rPoint.Y()); + rnColumnPos = GetColumnAtXPosPixel(rPoint.X()); + return rnRow != BROWSER_INVALIDID && rnColumnPos != BROWSER_INVALIDID; +} + + +bool BrowseBox::ConvertPointToRowHeader( sal_Int32& rnRow, const Point& rPoint ) +{ + rnRow = GetRowAtYPosPixel(rPoint.Y()); + // sal_uInt16 nColumnId = GetColumnAtXPosPixel(rPoint.X()); + return rnRow != BROWSER_INVALIDID;// && nColumnId == 0; +} + + +bool BrowseBox::ConvertPointToColumnHeader( sal_uInt16& _rnColumnPos, const Point& _rPoint ) +{ + _rnColumnPos = GetColumnAtXPosPixel(_rPoint.X()); + return _rnColumnPos != BROWSER_INVALIDID; +} + + +bool BrowseBox::ConvertPointToControlIndex( sal_Int32& _rnIndex, const Point& _rPoint ) +{ + //! TODO has to be checked + sal_Int32 nRow = 0; + sal_uInt16 nColumn = 0; + bool bRet = ConvertPointToCellAddress(nRow,nColumn,_rPoint); + if ( bRet ) + _rnIndex = nRow * ColCount() + nColumn; + + return bRet; +} + + +// Object data and state ------------------------------------------------------ + +OUString BrowseBox::GetAccessibleObjectName( ::vcl::AccessibleBrowseBoxObjType eObjType,sal_Int32 _nPosition) const +{ + OUString aRetText; + switch( eObjType ) + { + case ::vcl::BBTYPE_BROWSEBOX: + aRetText = "BrowseBox"; + break; + case ::vcl::BBTYPE_TABLE: + aRetText = "Table"; + break; + case ::vcl::BBTYPE_ROWHEADERBAR: + aRetText = "RowHeaderBar"; + break; + case ::vcl::BBTYPE_COLUMNHEADERBAR: + aRetText = "ColumnHeaderBar"; + break; + case ::vcl::BBTYPE_TABLECELL: + if( ColCount() !=0 && GetRowCount()!=0) + { + + sal_Int32 columnId = _nPosition % ColCount() +1; + aRetText = GetColumnDescription( sal_Int16( columnId ) ); + sal_Int32 rowId = _nPosition / GetRowCount() + 1; + aRetText += OUString::number(rowId); + } + else + aRetText = "TableCell"; +#if OSL_DEBUG_LEVEL > 0 + aRetText += " [" + + OUString::number(sal_Int32(GetCurRow())) + + "," + + OUString::number(sal_Int32(GetCurColumnId())) + + "]"; +#endif + break; + case ::vcl::BBTYPE_ROWHEADERCELL: + { + sal_Int32 rowId = _nPosition + 1; + aRetText = OUString::number( rowId ); + } +#if OSL_DEBUG_LEVEL > 0 + aRetText += " [" + + OUString::number(sal_Int32(GetCurRow())) + + "," + + OUString::number(sal_Int32(GetCurColumnId())) + + "]"; +#endif + break; + case ::vcl::BBTYPE_COLUMNHEADERCELL: + aRetText = GetColumnDescription( sal_Int16( _nPosition ) ); +#if OSL_DEBUG_LEVEL > 0 + aRetText += " [" + + OUString::number(sal_Int32(GetCurRow())) + + "," + + OUString::number(sal_Int32(GetCurColumnId())) + + "]"; +#endif + break; + default: + OSL_FAIL("BrowseBox::GetAccessibleName: invalid enum!"); + } + return aRetText; +} + + +OUString BrowseBox::GetAccessibleObjectDescription( ::vcl::AccessibleBrowseBoxObjType eObjType,sal_Int32 ) const +{ + OUString aRetText; + switch( eObjType ) + { + case ::vcl::BBTYPE_BROWSEBOX: + aRetText = "BrowseBox description"; + break; + case ::vcl::BBTYPE_TABLE: + // aRetText = "TABLE description"; + break; + case ::vcl::BBTYPE_ROWHEADERBAR: + // aRetText = "ROWHEADERBAR description"; + break; + case ::vcl::BBTYPE_COLUMNHEADERBAR: + // aRetText = "COLUMNHEADERBAR description"; + break; + case ::vcl::BBTYPE_TABLECELL: + // aRetText = "TABLECELL description"; + break; + case ::vcl::BBTYPE_ROWHEADERCELL: + // aRetText = "ROWHEADERCELL description"; + break; + case ::vcl::BBTYPE_COLUMNHEADERCELL: + // aRetText = "COLUMNHEADERCELL description"; + break; + case ::vcl::BBTYPE_CHECKBOXCELL: + break; + } + return aRetText; +} + + +OUString BrowseBox::GetRowDescription( sal_Int32 ) const +{ + return OUString(); +} + + +OUString BrowseBox::GetColumnDescription( sal_uInt16 _nColumn ) const +{ + return GetColumnTitle( GetColumnId( _nColumn ) ); +} + + +void BrowseBox::FillAccessibleStateSet( + ::utl::AccessibleStateSetHelper& rStateSet, + ::vcl::AccessibleBrowseBoxObjType eObjType ) const +{ + switch( eObjType ) + { + case ::vcl::BBTYPE_BROWSEBOX: + case ::vcl::BBTYPE_TABLE: + + rStateSet.AddState( AccessibleStateType::FOCUSABLE ); + if ( HasFocus() ) + rStateSet.AddState( AccessibleStateType::FOCUSED ); + if ( IsActive() ) + rStateSet.AddState( AccessibleStateType::ACTIVE ); + if ( GetUpdateMode() ) + rStateSet.AddState( AccessibleStateType::EDITABLE ); + if ( IsEnabled() ) + { + rStateSet.AddState( AccessibleStateType::ENABLED ); + rStateSet.AddState( AccessibleStateType::SENSITIVE ); + } + if ( IsReallyVisible() ) + rStateSet.AddState( AccessibleStateType::VISIBLE ); + if ( eObjType == ::vcl::BBTYPE_TABLE ) + rStateSet.AddState( AccessibleStateType::MANAGES_DESCENDANTS ); + + break; + case ::vcl::BBTYPE_ROWHEADERBAR: + rStateSet.AddState( AccessibleStateType::FOCUSABLE ); + rStateSet.AddState( AccessibleStateType::VISIBLE ); + if ( GetSelectRowCount() ) + rStateSet.AddState( AccessibleStateType::FOCUSED ); + rStateSet.AddState( AccessibleStateType::MANAGES_DESCENDANTS ); + break; + case ::vcl::BBTYPE_COLUMNHEADERBAR: + rStateSet.AddState( AccessibleStateType::FOCUSABLE ); + rStateSet.AddState( AccessibleStateType::VISIBLE ); + if ( GetSelectColumnCount() ) + rStateSet.AddState( AccessibleStateType::FOCUSED ); + rStateSet.AddState( AccessibleStateType::MANAGES_DESCENDANTS ); + break; + case ::vcl::BBTYPE_TABLECELL: + { + sal_Int32 nRow = GetCurRow(); + sal_uInt16 nColumn = GetCurColumnId(); + if ( IsFieldVisible(nRow,nColumn) ) + rStateSet.AddState( AccessibleStateType::VISIBLE ); + if ( !IsFrozen( nColumn ) ) + rStateSet.AddState( AccessibleStateType::FOCUSABLE ); + rStateSet.AddState( AccessibleStateType::TRANSIENT ); + } + break; + case ::vcl::BBTYPE_ROWHEADERCELL: + case ::vcl::BBTYPE_COLUMNHEADERCELL: + case ::vcl::BBTYPE_CHECKBOXCELL: + OSL_FAIL("Illegal call here!"); + break; + } +} + +void BrowseBox::FillAccessibleStateSetForCell( ::utl::AccessibleStateSetHelper& _rStateSet, + sal_Int32 _nRow, sal_uInt16 _nColumnPos ) const +{ + //! TODO check if the state is valid for table cells + if ( IsCellVisible( _nRow, _nColumnPos ) ) + _rStateSet.AddState( AccessibleStateType::VISIBLE ); + if ( GetCurrRow() == _nRow && GetCurrColumn() == _nColumnPos ) + _rStateSet.AddState( AccessibleStateType::FOCUSED ); + else // only transient when column is not focused + _rStateSet.AddState( AccessibleStateType::TRANSIENT ); +} + + +void BrowseBox::GrabTableFocus() +{ + GrabFocus(); +} + +OUString BrowseBox::GetCellText(long, sal_uInt16 ) const +{ + SAL_WARN("svtools", "This method has to be implemented by the derived classes! BUG!!"); + return OUString(); +} + + +void BrowseBox::commitHeaderBarEvent(sal_Int16 nEventId, + const Any& rNewValue, const Any& rOldValue, bool _bColumnHeaderBar ) +{ + if ( isAccessibleAlive() ) + m_pImpl->m_pAccessible->commitHeaderBarEvent( nEventId, + rNewValue, rOldValue, _bColumnHeaderBar ); +} + +void BrowseBox::commitTableEvent( sal_Int16 _nEventId, const Any& _rNewValue, const Any& _rOldValue ) +{ + if ( isAccessibleAlive() ) + m_pImpl->m_pAccessible->commitTableEvent( _nEventId, _rNewValue, _rOldValue ); +} + +void BrowseBox::commitBrowseBoxEvent( sal_Int16 _nEventId, const Any& _rNewValue, const Any& _rOldValue ) +{ + if ( isAccessibleAlive() ) + m_pImpl->m_pAccessible->commitEvent( _nEventId, _rNewValue, _rOldValue); +} + +::vcl::IAccessibleFactory& BrowseBox::getAccessibleFactory() +{ + return m_pImpl->m_aFactoryAccess.getFactory(); +} + +bool BrowseBox::isAccessibleAlive( ) const +{ + return ( nullptr != m_pImpl->m_pAccessible ) && m_pImpl->m_pAccessible->isAlive(); +} + +// IAccessibleTableProvider + +sal_Int32 BrowseBox::GetCurrRow() const +{ + return GetCurRow(); +} + +sal_uInt16 BrowseBox::GetCurrColumn() const +{ + return GetColumnPos( GetCurColumnId() ); +} + +bool BrowseBox::HasRowHeader() const +{ + return ( GetColumnId( 0 ) == HandleColumnId ); // HandleColumn == RowHeader +} + +bool BrowseBox::GoToCell( sal_Int32 _nRow, sal_uInt16 _nColumn ) +{ + return GoToRowColumnId( _nRow, GetColumnId( _nColumn ) ); +} + +void BrowseBox::SelectColumn( sal_uInt16 _nColumn, bool _bSelect ) +{ + SelectColumnPos( _nColumn, _bSelect ); +} + +bool BrowseBox::IsColumnSelected( long _nColumn ) const +{ + return ( pColSel && (0 <= _nColumn) && (_nColumn <= 0xFFF) ) && + pColSel->IsSelected( static_cast< sal_uInt16 >( _nColumn ) ); +} + +sal_Int32 BrowseBox::GetSelectedRowCount() const +{ + return GetSelectRowCount(); +} + +sal_Int32 BrowseBox::GetSelectedColumnCount() const +{ + const MultiSelection* pColumnSel = GetColumnSelection(); + return pColumnSel ? pColumnSel->GetSelectCount() : 0; +} + +void BrowseBox::GetAllSelectedRows( css::uno::Sequence< sal_Int32 >& _rRows ) const +{ + sal_Int32 nCount = GetSelectRowCount(); + if( nCount ) + { + _rRows.realloc( nCount ); + _rRows[ 0 ] = const_cast< BrowseBox* >( this )->FirstSelectedRow(); + for( sal_Int32 nIndex = 1; nIndex < nCount; ++nIndex ) + _rRows[ nIndex ] = const_cast< BrowseBox* >( this )->NextSelectedRow(); + DBG_ASSERT( const_cast< BrowseBox* >( this )->NextSelectedRow() == BROWSER_ENDOFSELECTION, + "BrowseBox::GetAllSelectedRows - too many selected rows found" ); + } +} + +void BrowseBox::GetAllSelectedColumns( css::uno::Sequence< sal_Int32 >& _rColumns ) const +{ + const MultiSelection* pColumnSel = GetColumnSelection(); + sal_Int32 nCount = GetSelectedColumnCount(); + if( !(pColumnSel && nCount) ) + return; + + _rColumns.realloc( nCount ); + + sal_Int32 nIndex = 0; + const size_t nRangeCount = pColumnSel->GetRangeCount(); + for( size_t nRange = 0; nRange < nRangeCount; ++nRange ) + { + const Range& rRange = pColumnSel->GetRange( nRange ); + // loop has to include aRange.Max() + for( sal_Int32 nCol = rRange.Min(); nCol <= static_cast<sal_Int32>(rRange.Max()); ++nCol ) + { + DBG_ASSERT( nIndex < nCount, + "GetAllSelectedColumns - range overflow" ); + _rColumns[ nIndex ] = nCol; + ++nIndex; + } + } +} + +bool BrowseBox::IsCellVisible( sal_Int32 _nRow, sal_uInt16 _nColumnPos ) const +{ + return IsFieldVisible( _nRow, GetColumnId( _nColumnPos ) ); +} + +OUString BrowseBox::GetAccessibleCellText(long _nRow, sal_uInt16 _nColPos) const +{ + return GetCellText( _nRow, GetColumnId( _nColPos ) ); +} + + +bool BrowseBox::GetGlyphBoundRects( const Point& rOrigin, const OUString& rStr, int nIndex, int nLen, MetricVector& rVector ) +{ + return Control::GetGlyphBoundRects( rOrigin, rStr, nIndex, nLen, rVector ); +} + +tools::Rectangle BrowseBox::GetWindowExtentsRelative( vcl::Window *pRelativeWindow ) const +{ + return Control::GetWindowExtentsRelative( pRelativeWindow ); +} + +void BrowseBox::GrabFocus() +{ + Control::GrabFocus(); +} + +Reference< XAccessible > BrowseBox::GetAccessible() +{ + return Control::GetAccessible(); +} + +vcl::Window* BrowseBox::GetAccessibleParentWindow() const +{ + return Control::GetAccessibleParentWindow(); +} + +vcl::Window* BrowseBox::GetWindowInstance() +{ + return this; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svtools/source/brwbox/brwhead.cxx b/svtools/source/brwbox/brwhead.cxx new file mode 100644 index 000000000..fa42bfe29 --- /dev/null +++ b/svtools/source/brwbox/brwhead.cxx @@ -0,0 +1,107 @@ +/* -*- 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 . + */ + + +#include <svtools/brwhead.hxx> +#include <svtools/brwbox.hxx> +#include <vcl/commandevent.hxx> + + +BrowserHeader::BrowserHeader( BrowseBox* pParent, WinBits nWinBits ) + :HeaderBar( pParent, nWinBits ) + ,_pBrowseBox( pParent ) +{ + long nHeight = pParent->IsZoom() ? pParent->CalcZoom(pParent->GetTitleHeight()) : pParent->GetTitleHeight(); + + SetPosSizePixel( Point( 0, 0), + Size( pParent->GetOutputSizePixel().Width(), + nHeight ) ); + Show(); +} + + +BrowserHeader::~BrowserHeader() +{ + disposeOnce(); +} + +void BrowserHeader::dispose() +{ + _pBrowseBox.clear(); + HeaderBar::dispose(); +} + + +void BrowserHeader::Command( const CommandEvent& rCEvt ) +{ + if ( !GetCurItemId() && CommandEventId::ContextMenu == rCEvt.GetCommand() ) + { + Point aPos( rCEvt.GetMousePosPixel() ); + if ( _pBrowseBox->IsFrozen(0) ) + aPos.AdjustX(_pBrowseBox->GetColumnWidth(0) ); + _pBrowseBox->GetDataWindow().Command( CommandEvent( + Point( aPos.X(), aPos.Y() - GetSizePixel().Height() ), + CommandEventId::ContextMenu, rCEvt.IsMouseEvent() ) ); + } +} + + +void BrowserHeader::EndDrag() +{ + // call before other actions, it looks more nice in most cases + HeaderBar::EndDrag(); + PaintImmediately(); + + // not aborted? + sal_uInt16 nId = GetCurItemId(); + if ( !nId ) + return; + + // handle column? + if ( nId == USHRT_MAX-1 ) + nId = 0; + + if ( !IsItemMode() ) + { + // column resize + _pBrowseBox->SetColumnWidth( nId, GetItemSize( nId ) ); + _pBrowseBox->ColumnResized( nId ); + SetItemSize( nId, _pBrowseBox->GetColumnWidth( nId ) ); + } + else + { + // column drag + // did the position actually change? + // take the handle column into account + sal_uInt16 nOldPos = _pBrowseBox->GetColumnPos(nId), + nNewPos = GetItemPos( nId ); + + if (_pBrowseBox->GetColumnId(0) == BrowseBox::HandleColumnId) + nNewPos++; + + if (nOldPos != nNewPos) + { + _pBrowseBox->SetColumnPos( nId, nNewPos ); + _pBrowseBox->ColumnMoved( nId ); + } + } +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svtools/source/brwbox/brwimpl.hxx b/svtools/source/brwbox/brwimpl.hxx new file mode 100644 index 000000000..95623570c --- /dev/null +++ b/svtools/source/brwbox/brwimpl.hxx @@ -0,0 +1,80 @@ +/* -*- 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 . + */ +#pragma once + +#include <vcl/accessibletableprovider.hxx> +#include <vcl/svtaccessiblefactory.hxx> +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/accessibility/XAccessible.hpp> +#include <osl/diagnose.h> +#include <tools/diagnose_ex.h> + +#include <map> + +namespace svt +{ + class BrowseBoxImpl + { + // member + public: + typedef ::std::map< sal_Int32, css::uno::Reference< css::accessibility::XAccessible > > THeaderCellMap; + + struct THeaderCellMapFunctorDispose + { + void operator()(const THeaderCellMap::value_type& _aType) + { + css::uno::Reference< css::lang::XComponent > xComp( _aType.second, css::uno::UNO_QUERY ); + OSL_ENSURE( xComp.is() || !_aType.second.is(), "THeaderCellMapFunctorDispose: invalid accessible cell (no XComponent)!" ); + if ( xComp.is() ) + try + { + xComp->dispose(); + } + catch( const css::uno::Exception& ) + { + TOOLS_WARN_EXCEPTION( "svtools", "THeaderCellMapFunctorDispose" ); + } + } + }; + + public: + vcl::AccessibleFactoryAccess m_aFactoryAccess; + vcl::IAccessibleBrowseBox* m_pAccessible; + THeaderCellMap m_aColHeaderCellMap; + THeaderCellMap m_aRowHeaderCellMap; + + public: + BrowseBoxImpl() : m_pAccessible(nullptr) + { + } + + + /// @see AccessibleBrowseBox::getHeaderBar + css::uno::Reference< css::accessibility::XAccessible > + getAccessibleHeaderBar( vcl::AccessibleBrowseBoxObjType _eObjType ); + + /// @see AccessibleBrowseBox::getTable + css::uno::Reference< css::accessibility::XAccessible > + getAccessibleTable( ); + + }; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svtools/source/brwbox/datwin.cxx b/svtools/source/brwbox/datwin.cxx new file mode 100644 index 000000000..f22ddb19e --- /dev/null +++ b/svtools/source/brwbox/datwin.cxx @@ -0,0 +1,691 @@ +/* -*- 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 . + */ + + +#include "datwin.hxx" +#include <o3tl/numeric.hxx> +#include <vcl/commandevent.hxx> +#include <vcl/help.hxx> +#include <vcl/settings.hxx> +#include <vcl/scrbar.hxx> +#include <vcl/ptrstyle.hxx> +#include <tools/debug.hxx> +#include <tools/fract.hxx> + + +void ButtonFrame::Draw( OutputDevice& rDev ) +{ + Color aOldFillColor = rDev.GetFillColor(); + Color aOldLineColor = rDev.GetLineColor(); + + const StyleSettings &rSettings = rDev.GetSettings().GetStyleSettings(); + Color aColLight( rSettings.GetLightColor() ); + Color aColShadow( rSettings.GetShadowColor() ); + Color aColFace( rSettings.GetFaceColor() ); + + rDev.SetLineColor( aColFace ); + rDev.SetFillColor( aColFace ); + rDev.DrawRect( aRect ); + + if( rDev.GetOutDevType() != OUTDEV_WINDOW ) + { + rDev.SetLineColor( aColLight ); + rDev.DrawLine( aRect.TopLeft(), Point( aRect.Right(), aRect.Top() ) ); + rDev.DrawLine( aRect.TopLeft(), Point( aRect.Left(), aRect.Bottom() - 1 ) ); + rDev.SetLineColor( aColShadow ); + rDev.DrawLine( aRect.BottomRight(), Point( aRect.Right(), aRect.Top() ) ); + rDev.DrawLine( aRect.BottomRight(), Point( aRect.Left(), aRect.Bottom() ) ); + } + + if ( !aText.isEmpty() ) + { + OUString aVal = rDev.GetEllipsisString(aText,aInnerRect.GetWidth() - 2*MIN_COLUMNWIDTH); + + vcl::Font aFont( rDev.GetFont() ); + bool bOldTransp = aFont.IsTransparent(); + if ( !bOldTransp ) + { + aFont.SetTransparent( true ); + rDev.SetFont( aFont ); + } + + Color aOldColor = rDev.GetTextColor(); + if (m_bDrawDisabled) + rDev.SetTextColor(rSettings.GetDisableColor()); + + rDev.DrawText( Point( + ( aInnerRect.Left() + aInnerRect.Right() ) / 2 - ( rDev.GetTextWidth(aVal) / 2 ), + aInnerRect.Top() ), aVal ); + + // restore settings + if ( !bOldTransp ) + { + aFont.SetTransparent(false); + rDev.SetFont( aFont ); + } + if (m_bDrawDisabled) + rDev.SetTextColor(aOldColor); + } + + rDev.SetLineColor( aOldLineColor ); + rDev.SetFillColor( aOldFillColor ); +} + +BrowserColumn::BrowserColumn( sal_uInt16 nItemId, + const OUString& rTitle, sal_uLong nWidthPixel, const Fraction& rCurrentZoom ) +: _nId( nItemId ), + _nWidth( nWidthPixel ), + _aTitle( rTitle ), + _bFrozen( false ) +{ + double n = static_cast<double>(_nWidth); + n *= static_cast<double>(rCurrentZoom.GetDenominator()); + if (!rCurrentZoom.GetNumerator()) + throw o3tl::divide_by_zero(); + n /= static_cast<double>(rCurrentZoom.GetNumerator()); + _nOriginalWidth = n>0 ? static_cast<long>(n+0.5) : -static_cast<long>(-n+0.5); +} + +BrowserColumn::~BrowserColumn() +{ +} + +void BrowserColumn::SetWidth(sal_uLong nNewWidthPixel, const Fraction& rCurrentZoom) +{ + _nWidth = nNewWidthPixel; + // Avoid overflow when called with LONG_MAX from + // BrowseBox::AutoSizeLastColumn: + if (_nWidth == LONG_MAX) + { + _nOriginalWidth = _nWidth; + } + else + { + double n = static_cast<double>(_nWidth); + n *= static_cast<double>(rCurrentZoom.GetDenominator()); + if (!rCurrentZoom.GetNumerator()) + throw o3tl::divide_by_zero(); + n /= static_cast<double>(rCurrentZoom.GetNumerator()); + _nOriginalWidth = n>0 ? static_cast<long>(n+0.5) : -static_cast<long>(-n+0.5); + } +} + +void BrowserColumn::Draw( BrowseBox const & rBox, OutputDevice& rDev, const Point& rPos ) +{ + if ( _nId == 0 ) + { + // paint handle column + ButtonFrame( rPos, Size( Width()-1, rBox.GetDataRowHeight()-1 ), + "", false ).Draw( rDev ); + Color aOldLineColor = rDev.GetLineColor(); + rDev.SetLineColor( COL_BLACK ); + rDev.DrawLine( + Point( rPos.X(), rPos.Y()+rBox.GetDataRowHeight()-1 ), + Point( rPos.X() + Width() - 1, rPos.Y()+rBox.GetDataRowHeight()-1 ) ); + rDev.DrawLine( + Point( rPos.X() + Width() - 1, rPos.Y() ), + Point( rPos.X() + Width() - 1, rPos.Y()+rBox.GetDataRowHeight()-1 ) ); + rDev.SetLineColor( aOldLineColor ); + + rBox.DoPaintField( rDev, + tools::Rectangle( + Point( rPos.X() + 2, rPos.Y() + 2 ), + Size( Width()-1, rBox.GetDataRowHeight()-1 ) ), + GetId(), + BrowseBox::BrowserColumnAccess() ); + } + else + { + // paint data column + long nWidth = Width() == LONG_MAX ? rBox.GetDataWindow().GetSizePixel().Width() : Width(); + + rBox.DoPaintField( rDev, + tools::Rectangle( + Point( rPos.X() + MIN_COLUMNWIDTH, rPos.Y() ), + Size( nWidth-2*MIN_COLUMNWIDTH, rBox.GetDataRowHeight()-1 ) ), + GetId(), + BrowseBox::BrowserColumnAccess() ); + } +} + + +void BrowserColumn::ZoomChanged(const Fraction& rNewZoom) +{ + double n(_nOriginalWidth * rNewZoom); + _nWidth = n>0 ? static_cast<long>(n+0.5) : -static_cast<long>(-n+0.5); +} + + +BrowserDataWin::BrowserDataWin( BrowseBox* pParent ) + :Control( pParent, WB_CLIPCHILDREN ) + ,DragSourceHelper( this ) + ,DropTargetHelper( this ) + ,pHeaderBar( nullptr ) + ,pCornerWin( nullptr ) + ,bInDtor( false ) + ,bInPaint( false ) + ,bInCommand( false ) + ,bNoHScroll( false ) + ,bNoVScroll( false ) + ,bAutoHScroll(false) + ,bAutoVScroll(false) + ,bUpdateMode( true ) + ,bAutoSizeLastCol(false) + ,bResizeOnPaint( false ) + ,bUpdateOnUnlock( false ) + ,bInUpdateScrollbars( false ) + ,bHadRecursion( false ) + ,bCallingDropCallback( false ) + ,nUpdateLock( 0 ) + ,nCursorHidden( 0 ) + ,m_nDragRowDividerLimit( 0 ) + ,m_nDragRowDividerOffset( 0 ) +{ + aMouseTimer.SetInvokeHandler( LINK( this, BrowserDataWin, RepeatedMouseMove ) ); + aMouseTimer.SetTimeout( 100 ); +} + + +BrowserDataWin::~BrowserDataWin() +{ + disposeOnce(); +} + +void BrowserDataWin::dispose() +{ + bInDtor = true; + + aInvalidRegion.clear(); + pHeaderBar.clear(); + pCornerWin.clear(); + DragSourceHelper::dispose(); + DropTargetHelper::dispose(); + Control::dispose(); +} + + +void BrowserDataWin::LeaveUpdateLock() +{ + if ( !--nUpdateLock ) + { + DoOutstandingInvalidations(); + if (bUpdateOnUnlock ) + { + Control::PaintImmediately(); + bUpdateOnUnlock = false; + } + } +} + +void InitSettings_Impl(vcl::Window* pWin) +{ + const StyleSettings& rStyleSettings = pWin->GetSettings().GetStyleSettings(); + + pWin->ApplyControlFont(*pWin, rStyleSettings.GetFieldFont()); + pWin->ApplyControlForeground(*pWin, rStyleSettings.GetWindowTextColor()); + pWin->ApplyControlBackground(*pWin, rStyleSettings.GetWindowColor()); +} + + +void BrowserDataWin::Update() +{ + if ( !nUpdateLock ) + Control::PaintImmediately(); + else + bUpdateOnUnlock = true; +} + + +void BrowserDataWin::DataChanged( const DataChangedEvent& rDCEvt ) +{ + if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) && + (rDCEvt.GetFlags() & AllSettingsFlags::STYLE) ) + { + InitSettings_Impl(this); + Invalidate(); + InitSettings_Impl(GetParent()); + GetParent()->Invalidate(); + GetParent()->Resize(); + } + else + Control::DataChanged( rDCEvt ); +} + + +void BrowserDataWin::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) +{ + if (!nUpdateLock && GetUpdateMode()) + { + if (bInPaint) + { + aInvalidRegion.emplace_back(rRect); + return; + } + bInPaint = true; + GetParent()->PaintData(*this, rRenderContext, rRect); + bInPaint = false; + DoOutstandingInvalidations(); + } + else + { + aInvalidRegion.emplace_back(rRect); + } +} + + +BrowseEvent BrowserDataWin::CreateBrowseEvent( const Point& rPosPixel ) +{ + BrowseBox *pBox = GetParent(); + + // seek to row under mouse + long nRelRow = rPosPixel.Y() < 0 + ? -1 + : rPosPixel.Y() / pBox->GetDataRowHeight(); + long nRow = nRelRow < 0 ? -1 : nRelRow + pBox->nTopRow; + + // find column under mouse + long nMouseX = rPosPixel.X(); + long nColX = 0; + size_t nCol; + for ( nCol = 0; + nCol < pBox->mvCols.size() && nColX < GetSizePixel().Width(); + ++nCol ) + if ( pBox->mvCols[ nCol ]->IsFrozen() || nCol >= pBox->nFirstCol ) + { + nColX += pBox->mvCols[ nCol ]->Width(); + if ( nMouseX < nColX ) + break; + } + sal_uInt16 nColId = BROWSER_INVALIDID; + if ( nCol < pBox->mvCols.size() ) + nColId = pBox->mvCols[ nCol ]->GetId(); + + // compute the field rectangle and field relative MouseEvent + tools::Rectangle aFieldRect; + if ( nCol < pBox->mvCols.size() ) + { + nColX -= pBox->mvCols[ nCol ]->Width(); + aFieldRect = tools::Rectangle( + Point( nColX, nRelRow * pBox->GetDataRowHeight() ), + Size( pBox->mvCols[ nCol ]->Width(), + pBox->GetDataRowHeight() ) ); + } + + // assemble and return the BrowseEvent + return BrowseEvent( this, nRow, nCol, nColId, aFieldRect ); +} + + +sal_Int8 BrowserDataWin::AcceptDrop( const AcceptDropEvent& _rEvt ) +{ + bCallingDropCallback = true; + sal_Int8 nReturn = GetParent()->AcceptDrop( BrowserAcceptDropEvent( this, _rEvt ) ); + bCallingDropCallback = false; + return nReturn; +} + + +sal_Int8 BrowserDataWin::ExecuteDrop( const ExecuteDropEvent& _rEvt ) +{ + bCallingDropCallback = true; + sal_Int8 nReturn = GetParent()->ExecuteDrop( BrowserExecuteDropEvent( this, _rEvt ) ); + bCallingDropCallback = false; + return nReturn; +} + + +void BrowserDataWin::StartDrag( sal_Int8 _nAction, const Point& _rPosPixel ) +{ + if ( !GetParent()->bRowDividerDrag ) + { + Point aEventPos( _rPosPixel ); + aEventPos.AdjustY(GetParent()->GetTitleHeight() ); + GetParent()->StartDrag( _nAction, aEventPos ); + } +} + + +void BrowserDataWin::Command( const CommandEvent& rEvt ) +{ + // scroll mouse event? + BrowseBox *pBox = GetParent(); + if ( ( (rEvt.GetCommand() == CommandEventId::Wheel) || + (rEvt.GetCommand() == CommandEventId::StartAutoScroll) || + (rEvt.GetCommand() == CommandEventId::AutoScroll) ) && + ( HandleScrollCommand( rEvt, pBox->aHScroll.get(), pBox->pVScroll ) ) ) + return; + + Point aEventPos( rEvt.GetMousePosPixel() ); + long nRow = pBox->GetRowAtYPosPixel( aEventPos.Y(), false); + MouseEvent aMouseEvt( aEventPos, 1, MouseEventModifiers::SELECT, MOUSE_LEFT ); + if ( CommandEventId::ContextMenu == rEvt.GetCommand() && rEvt.IsMouseEvent() && + nRow < pBox->GetRowCount() && !pBox->IsRowSelected(nRow) ) + { + bInCommand = true; + MouseButtonDown( aMouseEvt ); + if( bInDtor ) + return; + MouseButtonUp( aMouseEvt ); + if( bInDtor ) + return; + bInCommand = false; + } + + aEventPos.AdjustY(GetParent()->GetTitleHeight() ); + CommandEvent aEvt( aEventPos, rEvt.GetCommand(), + rEvt.IsMouseEvent(), rEvt.GetEventData() ); + bInCommand = true; + GetParent()->Command( aEvt ); + if( bInDtor ) + return; + bInCommand = false; + + if ( CommandEventId::StartDrag == rEvt.GetCommand() ) + MouseButtonUp( aMouseEvt ); + + Control::Command( rEvt ); +} + + +bool BrowserDataWin::ImplRowDividerHitTest( const BrowserMouseEvent& _rEvent ) +{ + if ( ! ( GetParent()->IsInteractiveRowHeightEnabled() + && ( _rEvent.GetRow() >= 0 ) + && ( _rEvent.GetRow() < GetParent()->GetRowCount() ) + && ( _rEvent.GetColumnId() == BrowseBox::HandleColumnId ) + ) + ) + return false; + + long nDividerDistance = GetParent()->GetDataRowHeight() - ( _rEvent.GetPosPixel().Y() % GetParent()->GetDataRowHeight() ); + return ( nDividerDistance <= 4 ); +} + + +void BrowserDataWin::MouseButtonDown( const MouseEvent& rEvt ) +{ + aLastMousePos = OutputToScreenPixel( rEvt.GetPosPixel() ); + + BrowserMouseEvent aBrowserEvent( this, rEvt ); + if ( ( aBrowserEvent.GetClicks() == 1 ) && ImplRowDividerHitTest( aBrowserEvent ) ) + { + StartRowDividerDrag( aBrowserEvent.GetPosPixel() ); + return; + } + + GetParent()->MouseButtonDown( BrowserMouseEvent( this, rEvt ) ); +} + + +void BrowserDataWin::MouseMove( const MouseEvent& rEvt ) +{ + // avoid pseudo MouseMoves + Point aNewPos = OutputToScreenPixel( rEvt.GetPosPixel() ); + if ( aNewPos == aLastMousePos ) + return; + aLastMousePos = aNewPos; + + // transform to a BrowseEvent + BrowserMouseEvent aBrowserEvent( this, rEvt ); + GetParent()->MouseMove( aBrowserEvent ); + + // pointer shape + PointerStyle ePointerStyle = PointerStyle::Arrow; + if ( ImplRowDividerHitTest( aBrowserEvent ) ) + ePointerStyle = PointerStyle::VSizeBar; + SetPointer( ePointerStyle ); + + // dragging out of the visible area? + if ( rEvt.IsLeft() && + ( rEvt.GetPosPixel().Y() > GetSizePixel().Height() || + rEvt.GetPosPixel().Y() < 0 ) ) + { + // repeat the event + aRepeatEvt = rEvt; + aMouseTimer.Start(); + } + else + // killing old repeat-event + if ( aMouseTimer.IsActive() ) + aMouseTimer.Stop(); +} + + +IMPL_LINK_NOARG(BrowserDataWin, RepeatedMouseMove, Timer *, void) +{ + GetParent()->MouseMove( BrowserMouseEvent( this, aRepeatEvt ) ); +} + +void BrowserDataWin::MouseButtonUp( const MouseEvent& rEvt ) +{ + // avoid pseudo MouseMoves + Point aNewPos = OutputToScreenPixel( rEvt.GetPosPixel() ); + aLastMousePos = aNewPos; + + // simulate a move to the current position + MouseMove( rEvt ); + + // actual button up handling + ReleaseMouse(); + if ( aMouseTimer.IsActive() ) + aMouseTimer.Stop(); + GetParent()->MouseButtonUp( BrowserMouseEvent( this, rEvt ) ); +} + + +void BrowserDataWin::StartRowDividerDrag( const Point& _rStartPos ) +{ + long nDataRowHeight = GetParent()->GetDataRowHeight(); + // the exact separation pos of the two rows + long nDragRowDividerCurrentPos = _rStartPos.Y(); + if ( ( nDragRowDividerCurrentPos % nDataRowHeight ) > nDataRowHeight / 2 ) + nDragRowDividerCurrentPos += nDataRowHeight; + nDragRowDividerCurrentPos /= nDataRowHeight; + nDragRowDividerCurrentPos *= nDataRowHeight; + + m_nDragRowDividerOffset = nDragRowDividerCurrentPos - _rStartPos.Y(); + + m_nDragRowDividerLimit = nDragRowDividerCurrentPos - nDataRowHeight; + + GetParent()->bRowDividerDrag = true; + GetParent()->ImplStartTracking(); + + tools::Rectangle aDragSplitRect( 0, m_nDragRowDividerLimit, GetOutputSizePixel().Width(), nDragRowDividerCurrentPos ); + ShowTracking( aDragSplitRect ); + + StartTracking(); +} + + +void BrowserDataWin::Tracking( const TrackingEvent& rTEvt ) +{ + if ( !GetParent()->bRowDividerDrag ) + return; + + Point aMousePos = rTEvt.GetMouseEvent().GetPosPixel(); + // stop resizing at our bottom line + if ( aMousePos.Y() > GetOutputSizePixel().Height() ) + aMousePos.setY( GetOutputSizePixel().Height() ); + + if ( rTEvt.IsTrackingEnded() ) + { + HideTracking(); + GetParent()->bRowDividerDrag = false; + GetParent()->ImplEndTracking(); + + if ( !rTEvt.IsTrackingCanceled() ) + { + long nNewRowHeight = aMousePos.Y() + m_nDragRowDividerOffset - m_nDragRowDividerLimit; + + // care for minimum row height + if ( nNewRowHeight < GetParent()->QueryMinimumRowHeight() ) + nNewRowHeight = GetParent()->QueryMinimumRowHeight(); + + GetParent()->SetDataRowHeight( nNewRowHeight ); + GetParent()->RowHeightChanged(); + } + } + else + { + long nDragRowDividerCurrentPos = aMousePos.Y() + m_nDragRowDividerOffset; + + // care for minimum row height + if ( nDragRowDividerCurrentPos < m_nDragRowDividerLimit + GetParent()->QueryMinimumRowHeight() ) + nDragRowDividerCurrentPos = m_nDragRowDividerLimit + GetParent()->QueryMinimumRowHeight(); + + tools::Rectangle aDragSplitRect( 0, m_nDragRowDividerLimit, GetOutputSizePixel().Width(), nDragRowDividerCurrentPos ); + ShowTracking( aDragSplitRect ); + } +} + + +void BrowserDataWin::KeyInput( const KeyEvent& rEvt ) +{ + // pass to parent window + if ( !GetParent()->ProcessKey( rEvt ) ) + Control::KeyInput( rEvt ); +} + + +void BrowserDataWin::RequestHelp( const HelpEvent& rHEvt ) +{ + GetParent()->RequestHelp( rHEvt ); +} + + +BrowseEvent::BrowseEvent( vcl::Window* pWindow, + long nAbsRow, sal_uInt16 nColumn, sal_uInt16 nColumnId, + const tools::Rectangle& rRect ): + pWin(pWindow), + nRow(nAbsRow), + aRect(rRect), + nCol(nColumn), + nColId(nColumnId) +{ +} + + +BrowserMouseEvent::BrowserMouseEvent( BrowserDataWin *pWindow, + const MouseEvent& rEvt ): + MouseEvent(rEvt), + BrowseEvent( pWindow->CreateBrowseEvent( rEvt.GetPosPixel() ) ) +{ +} + + +BrowserMouseEvent::BrowserMouseEvent( vcl::Window *pWindow, const MouseEvent& rEvt, + long nAbsRow, sal_uInt16 nColumn, sal_uInt16 nColumnId, + const tools::Rectangle& rRect ): + MouseEvent(rEvt), + BrowseEvent( pWindow, nAbsRow, nColumn, nColumnId, rRect ) +{ +} + + +BrowserAcceptDropEvent::BrowserAcceptDropEvent( BrowserDataWin *pWindow, const AcceptDropEvent& rEvt ) + :AcceptDropEvent(rEvt) + ,BrowseEvent( pWindow->CreateBrowseEvent( rEvt.maPosPixel ) ) +{ +} + + +BrowserExecuteDropEvent::BrowserExecuteDropEvent( BrowserDataWin *pWindow, const ExecuteDropEvent& rEvt ) + :ExecuteDropEvent(rEvt) + ,BrowseEvent( pWindow->CreateBrowseEvent( rEvt.maPosPixel ) ) +{ +} + + +void BrowserDataWin::SetUpdateMode( bool bMode ) +{ + DBG_ASSERT( !bUpdateMode || aInvalidRegion.empty(), "invalid region not empty" ); + if ( bMode == bUpdateMode ) + return; + + bUpdateMode = bMode; + if ( bMode ) + DoOutstandingInvalidations(); +} + + +void BrowserDataWin::DoOutstandingInvalidations() +{ + for (const auto& rRect : aInvalidRegion) + Control::Invalidate( rRect ); + aInvalidRegion.clear(); +} + + +void BrowserDataWin::Invalidate( InvalidateFlags nFlags ) +{ + if ( !GetUpdateMode() ) + { + aInvalidRegion.clear(); + aInvalidRegion.emplace_back( Point( 0, 0 ), GetOutputSizePixel() ); + } + else + Window::Invalidate( nFlags ); +} + + +void BrowserDataWin::Invalidate( const tools::Rectangle& rRect, InvalidateFlags nFlags ) +{ + if ( !GetUpdateMode() ) + aInvalidRegion.emplace_back( rRect ); + else + Window::Invalidate( rRect, nFlags ); +} + +BrowserScrollBar::~BrowserScrollBar() +{ + disposeOnce(); +} + +void BrowserScrollBar::dispose() +{ + _pDataWin.clear(); + ScrollBar::dispose(); +} + +void BrowserScrollBar::Tracking( const TrackingEvent& rTEvt ) +{ + sal_uLong nPos = GetThumbPos(); + if ( nPos != _nLastPos ) + { + OUString aTip = OUString::number(nPos) + "/"; + if ( !_pDataWin->GetRealRowCount().isEmpty() ) + aTip += _pDataWin->GetRealRowCount(); + else + aTip += OUString::number(GetRangeMax()); + + tools::Rectangle aRect(GetPointerPosPixel(), Size(GetTextWidth(aTip), GetTextHeight())); + Help::ShowQuickHelp(this, aRect, aTip); + _nLastPos = nPos; + } + + ScrollBar::Tracking( rTEvt ); +} + +void BrowserScrollBar::EndScroll() +{ + Help::HideBalloonAndQuickHelp(); + ScrollBar::EndScroll(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svtools/source/brwbox/datwin.hxx b/svtools/source/brwbox/datwin.hxx new file mode 100644 index 000000000..1105708cc --- /dev/null +++ b/svtools/source/brwbox/datwin.hxx @@ -0,0 +1,189 @@ +/* -*- 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 . + */ + +#pragma once + +#include <svtools/brwbox.hxx> +#include <svtools/brwhead.hxx> +#include <vcl/scrbar.hxx> +#include <vcl/timer.hxx> +#include <vcl/transfer.hxx> +#include <vector> + +#define MIN_COLUMNWIDTH 2 + +class ButtonFrame +{ + tools::Rectangle aRect; + tools::Rectangle aInnerRect; + OUString aText; + bool m_bDrawDisabled; + +public: + ButtonFrame( const Point& rPt, const Size& rSz, + const OUString &rText, + bool _bDrawDisabled) + :aRect( rPt, rSz ) + ,aInnerRect( Point( aRect.Left()+1, aRect.Top()+1 ), + Size( aRect.GetWidth()-2, aRect.GetHeight()-2 ) ) + ,aText(rText) + ,m_bDrawDisabled(_bDrawDisabled) + { + } + + void Draw( OutputDevice& rDev ); +}; + + +class BrowserColumn final +{ + sal_uInt16 _nId; + sal_uLong _nOriginalWidth; + sal_uLong _nWidth; + OUString _aTitle; + bool _bFrozen; + +public: + BrowserColumn( sal_uInt16 nItemId, + const OUString& rTitle, sal_uLong nWidthPixel, const Fraction& rCurrentZoom ); + ~BrowserColumn(); + + sal_uInt16 GetId() const { return _nId; } + + sal_uLong Width() { return _nWidth; } + OUString& Title() { return _aTitle; } + + bool IsFrozen() const { return _bFrozen; } + void Freeze() { _bFrozen = true; } + + void Draw( BrowseBox const & rBox, OutputDevice& rDev, + const Point& rPos ); + + void SetWidth(sal_uLong nNewWidthPixel, const Fraction& rCurrentZoom); + void ZoomChanged(const Fraction& rNewZoom); +}; + + +class BrowserDataWin + :public Control + ,public DragSourceHelper + ,public DropTargetHelper +{ +public: + VclPtr<BrowserHeader> pHeaderBar; // only for BrowserMode::HEADERBAR_NEW + VclPtr<ScrollBarBox> pCornerWin; // Window in the corner btw the ScrollBars + bool bInDtor; + AutoTimer aMouseTimer; // recalls MouseMove on dragging out + MouseEvent aRepeatEvt; // a MouseEvent to repeat + Point aLastMousePos; // prevents pseudo-MouseMoves + + OUString aRealRowCount; // to show in VScrollBar + + std::vector<tools::Rectangle> aInvalidRegion; // invalidated Rectangles during !UpdateMode + bool bInPaint; // TRUE while in Paint + bool bInCommand; // TRUE while in Command + bool bNoHScroll; // no horizontal scrollbar + bool bNoVScroll; // no vertical scrollbar + bool bAutoHScroll; // autohide horizontaler Scrollbar + bool bAutoVScroll; // autohide horizontaler Scrollbar + bool bUpdateMode; // not SV-UpdateMode because of Invalidate() + bool bAutoSizeLastCol; // last column always fills up window + bool bResizeOnPaint; // outstanding resize-event + bool bUpdateOnUnlock; // Update() while locked + bool bInUpdateScrollbars; // prevents recursions + bool bHadRecursion; // a recursion occurred + bool bCallingDropCallback; // we're in a callback to AcceptDrop or ExecuteDrop currently + sal_uInt16 nUpdateLock; // lock count, don't call Control::Update()! + short nCursorHidden; // new counter for DoHide/ShowCursor + + long m_nDragRowDividerLimit; + long m_nDragRowDividerOffset; + +public: + explicit BrowserDataWin( BrowseBox* pParent ); + virtual ~BrowserDataWin() override; + virtual void dispose() override; + + virtual void DataChanged( const DataChangedEvent& rDCEvt ) override; + virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) override; + virtual void RequestHelp( const HelpEvent& rHEvt ) override; + virtual void Command( const CommandEvent& rEvt ) override; + virtual void MouseButtonDown( const MouseEvent& rEvt ) override; + virtual void MouseMove( const MouseEvent& rEvt ) override; + DECL_LINK( RepeatedMouseMove, Timer *, void ); + + virtual void MouseButtonUp( const MouseEvent& rEvt ) override; + virtual void KeyInput( const KeyEvent& rEvt ) override; + virtual void Tracking( const TrackingEvent& rTEvt ) override; + + // DropTargetHelper overridables + virtual sal_Int8 AcceptDrop( const AcceptDropEvent& rEvt ) override; + virtual sal_Int8 ExecuteDrop( const ExecuteDropEvent& rEvt ) override; + + // DragSourceHelper overridables + virtual void StartDrag( sal_Int8 _nAction, const Point& _rPosPixel ) override; + + + BrowseEvent CreateBrowseEvent( const Point& rPosPixel ); + BrowseBox* GetParent() const + { return static_cast<BrowseBox*>( Window::GetParent() ); } + const OUString& GetRealRowCount() const { return aRealRowCount; } + + void SetUpdateMode( bool bMode ); + bool GetUpdateMode() const { return bUpdateMode; } + void EnterUpdateLock() { ++nUpdateLock; } + void LeaveUpdateLock(); + void Update(); + void DoOutstandingInvalidations(); + void Invalidate( InvalidateFlags nFlags = InvalidateFlags::NONE ) override; + void Invalidate( const tools::Rectangle& rRect, InvalidateFlags nFlags = InvalidateFlags::NONE ) override; + using Control::Invalidate; + +protected: + void StartRowDividerDrag( const Point& _rStartPos ); + bool ImplRowDividerHitTest( const BrowserMouseEvent& _rEvent ); +}; + + +class BrowserScrollBar: public ScrollBar +{ + sal_uLong _nLastPos; + VclPtr<BrowserDataWin> _pDataWin; + +public: + BrowserScrollBar( vcl::Window* pParent, WinBits nStyle, + BrowserDataWin *pDataWin ) + : ScrollBar( pParent, nStyle ), + _nLastPos( ULONG_MAX ), + _pDataWin( pDataWin ) + {} + virtual ~BrowserScrollBar() override; + virtual void dispose() override; + //ScrollBar( vcl::Window* pParent, const ResId& rResId ); + + virtual void Tracking( const TrackingEvent& rTEvt ) override; + virtual void EndScroll() override; +}; + + +void InitSettings_Impl( vcl::Window *pWin ); + + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svtools/source/brwbox/ebbcontrols.cxx b/svtools/source/brwbox/ebbcontrols.cxx new file mode 100644 index 000000000..e402f0a42 --- /dev/null +++ b/svtools/source/brwbox/ebbcontrols.cxx @@ -0,0 +1,566 @@ +/* + * 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 . + */ + +#include <svtools/editbrowsebox.hxx> +#include <vcl/button.hxx> +#include <vcl/spinfld.hxx> +#include <vcl/fmtfield.hxx> +#include <vcl/xtextedt.hxx> +#include <vcl/textview.hxx> + +namespace svt +{ + + //= ComboBoxControl + ComboBoxControl::ComboBoxControl(vcl::Window* pParent) + : ControlBase(pParent, "svt/ui/combocontrol.ui", "ComboControl") + , m_xWidget(m_xBuilder->weld_combo_box("combobox")) + { + m_xWidget->set_entry_width_chars(1); // so a smaller than default width can be used + m_xWidget->connect_changed(LINK(this, ComboBoxControl, SelectHdl)); + } + + void ComboBoxControl::dispose() + { + m_xWidget.reset(); + ControlBase::dispose(); + } + + IMPL_LINK_NOARG(ComboBoxControl, SelectHdl, weld::ComboBox&, void) + { + CallModifyHdls(); + } + + //= ComboBoxCellController + ComboBoxCellController::ComboBoxCellController(ComboBoxControl* pWin) + :CellController(pWin) + { + static_cast<ComboBoxControl&>(GetWindow()).SetModifyHdl(LINK(this, ComboBoxCellController, ModifyHdl)); + } + + IMPL_LINK_NOARG(ComboBoxCellController, ModifyHdl, LinkParamNone*, void) + { + callModifyHdl(); + } + + bool ComboBoxCellController::MoveAllowed(const KeyEvent& rEvt) const + { + weld::ComboBox& rBox = GetComboBox(); + switch (rEvt.GetKeyCode().GetCode()) + { + case KEY_END: + case KEY_RIGHT: + { + int nStartPos, nEndPos; + bool bNoSelection = rBox.get_entry_selection_bounds(nStartPos, nEndPos); + return bNoSelection && nEndPos == rBox.get_active_text().getLength(); + } + case KEY_HOME: + case KEY_LEFT: + { + int nStartPos, nEndPos; + bool bNoSelection = rBox.get_entry_selection_bounds(nStartPos, nEndPos); + return bNoSelection && nStartPos == 0; + } + case KEY_UP: + case KEY_DOWN: + if (rBox.get_popup_shown()) + return false; + if (!rEvt.GetKeyCode().IsShift() && + rEvt.GetKeyCode().IsMod1()) + return false; + // drop down the list box + else if (rEvt.GetKeyCode().IsMod2() && rEvt.GetKeyCode().GetCode() == KEY_DOWN) + return false; + [[fallthrough]]; + case KEY_PAGEUP: + case KEY_PAGEDOWN: + case KEY_RETURN: + if (rBox.get_popup_shown()) + return false; + [[fallthrough]]; + default: + return true; + } + } + + bool ComboBoxCellController::IsModified() const + { + return GetComboBox().get_value_changed_from_saved(); + } + + void ComboBoxCellController::ClearModified() + { + GetComboBox().save_value(); + } + + //= ListBoxControl + ListBoxControl::ListBoxControl(vcl::Window* pParent) + : ControlBase(pParent, "svt/ui/listcontrol.ui", "ListControl") + , m_xWidget(m_xBuilder->weld_combo_box("listbox")) + { + m_xWidget->set_size_request(42, -1); // so a later narrow size request can stick + m_xWidget->connect_changed(LINK(this, ListBoxControl, SelectHdl)); + } + + void ListBoxControl::dispose() + { + m_xWidget.reset(); + ControlBase::dispose(); + } + + IMPL_LINK_NOARG(ListBoxControl, SelectHdl, weld::ComboBox&, void) + { + CallModifyHdls(); + } + + //= ListBoxCellController + ListBoxCellController::ListBoxCellController(ListBoxControl* pWin) + :CellController(pWin) + { + static_cast<ListBoxControl&>(GetWindow()).SetModifyHdl(LINK(this, ListBoxCellController, ListBoxSelectHdl)); + } + + bool ListBoxCellController::MoveAllowed(const KeyEvent& rEvt) const + { + const weld::ComboBox& rBox = GetListBox(); + switch (rEvt.GetKeyCode().GetCode()) + { + case KEY_UP: + case KEY_DOWN: + if (!rEvt.GetKeyCode().IsShift() && + rEvt.GetKeyCode().IsMod1()) + return false; + // drop down the list box + else + if (rEvt.GetKeyCode().IsMod2() && rEvt.GetKeyCode().GetCode() == KEY_DOWN) + return false; + [[fallthrough]]; + case KEY_PAGEUP: + case KEY_PAGEDOWN: + if (rBox.get_popup_shown()) + return false; + [[fallthrough]]; + default: + return true; + } + } + + bool ListBoxCellController::IsModified() const + { + return GetListBox().get_value_changed_from_saved(); + } + + void ListBoxCellController::ClearModified() + { + GetListBox().save_value(); + } + + IMPL_LINK_NOARG(ListBoxCellController, ListBoxSelectHdl, LinkParamNone*, void) + { + callModifyHdl(); + } + + //= CheckBoxControl + CheckBoxControl::CheckBoxControl(vcl::Window* pParent) + :Control(pParent, 0) + { + const Wallpaper& rParentBackground = pParent->GetBackground(); + if ( (pParent->GetStyle() & WB_CLIPCHILDREN) || rParentBackground.IsFixed() ) + SetBackground( rParentBackground ); + else + { + SetPaintTransparent( true ); + SetBackground(); + } + + EnableChildTransparentMode(); + + pBox = VclPtr<CheckBox>::Create(this,WB_CENTER|WB_VCENTER); + pBox->EnableTriState( true ); + pBox->SetLegacyNoTextAlign( true ); + pBox->EnableChildTransparentMode(); + pBox->SetPaintTransparent( true ); + pBox->SetClickHdl( LINK( this, CheckBoxControl, OnClick ) ); + pBox->Show(); + } + + CheckBoxControl::~CheckBoxControl() + { + disposeOnce(); + } + + void CheckBoxControl::dispose() + { + pBox.disposeAndClear(); + Control::dispose(); + } + + + IMPL_LINK_NOARG(CheckBoxControl, OnClick, Button*, void) + { + m_aClickLink.Call(pBox); + m_aModifyLink.Call(nullptr); + } + + + void CheckBoxControl::Resize() + { + Control::Resize(); + pBox->SetPosSizePixel(Point(0,0),GetSizePixel()); + } + + + void CheckBoxControl::DataChanged( const DataChangedEvent& _rEvent ) + { + if ( _rEvent.GetType() == DataChangedEventType::SETTINGS ) + pBox->SetSettings( GetSettings() ); + } + + + void CheckBoxControl::StateChanged( StateChangedType nStateChange ) + { + Control::StateChanged(nStateChange); + if ( nStateChange == StateChangedType::Zoom ) + pBox->SetZoom(GetZoom()); + } + + void CheckBoxControl::Draw( OutputDevice* pDev, const Point& rPos, DrawFlags nFlags ) + { + pBox->Draw(pDev, rPos, nFlags); + } + + void CheckBoxControl::GetFocus() + { + if (pBox) + pBox->GrabFocus(); + } + + + void CheckBoxControl::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rClientRect) + { + Control::Paint(rRenderContext, rClientRect); + if (HasFocus()) + ShowFocus(tools::Rectangle()); + } + + + bool CheckBoxControl::PreNotify(NotifyEvent& rEvt) + { + switch (rEvt.GetType()) + { + case MouseNotifyEvent::GETFOCUS: + ShowFocus(tools::Rectangle()); + break; + case MouseNotifyEvent::LOSEFOCUS: + HideFocus(); + break; + default: + break; + } + return Control::PreNotify(rEvt); + } + + + //= CheckBoxCellController + + + CheckBoxCellController::CheckBoxCellController(CheckBoxControl* pWin) + : CellController(pWin) + { + static_cast<CheckBoxControl &>(GetWindow()).SetModifyHdl( LINK(this, CheckBoxCellController, ModifyHdl) ); + } + + bool CheckBoxCellController::WantMouseEvent() const + { + return true; + } + + + CheckBox& CheckBoxCellController::GetCheckBox() const + { + return static_cast<CheckBoxControl &>(GetWindow()).GetBox(); + } + + + bool CheckBoxCellController::IsModified() const + { + return GetCheckBox().IsValueChangedFromSaved(); + } + + + void CheckBoxCellController::ClearModified() + { + GetCheckBox().SaveValue(); + } + + + IMPL_LINK_NOARG(CheckBoxCellController, ModifyHdl, LinkParamNone*, void) + { + callModifyHdl(); + } + + //= MultiLineEditImplementation + + + OUString MultiLineEditImplementation::GetText( LineEnd aSeparator ) const + { + return const_cast< MultiLineEditImplementation* >( this )->GetEditWindow().GetText( aSeparator ); + } + + + OUString MultiLineEditImplementation::GetSelected( LineEnd aSeparator ) const + { + return const_cast< MultiLineEditImplementation* >( this )->GetEditWindow().GetSelected( aSeparator ); + } + + + //= EditCellController + + + EditCellController::EditCellController( Edit* _pEdit ) + :CellController( _pEdit ) + ,m_pEditImplementation( new EditImplementation( *_pEdit ) ) + ,m_bOwnImplementation( true ) + { + m_pEditImplementation->SetModifyHdl( LINK(this, EditCellController, ModifyHdl) ); + } + + + EditCellController::EditCellController( IEditImplementation* _pImplementation ) + :CellController( &_pImplementation->GetControl() ) + ,m_pEditImplementation( _pImplementation ) + ,m_bOwnImplementation( false ) + { + m_pEditImplementation->SetModifyHdl( LINK(this, EditCellController, ModifyHdl) ); + } + + + EditCellController::~EditCellController( ) + { + if ( m_bOwnImplementation ) + DELETEZ( m_pEditImplementation ); + } + + + void EditCellController::SetModified() + { + m_pEditImplementation->SetModified(); + } + + + void EditCellController::ClearModified() + { + m_pEditImplementation->ClearModified(); + } + + + bool EditCellController::MoveAllowed(const KeyEvent& rEvt) const + { + bool bResult; + switch (rEvt.GetKeyCode().GetCode()) + { + case KEY_END: + case KEY_RIGHT: + { + Selection aSel = m_pEditImplementation->GetSelection(); + bResult = !aSel && aSel.Max() == m_pEditImplementation->GetText( LINEEND_LF ).getLength(); + } break; + case KEY_HOME: + case KEY_LEFT: + { + Selection aSel = m_pEditImplementation->GetSelection(); + bResult = !aSel && aSel.Min() == 0; + } break; + default: + bResult = true; + } + return bResult; + } + + + bool EditCellController::IsModified() const + { + return m_pEditImplementation->IsModified(); + } + + + IMPL_LINK_NOARG(EditCellController, ModifyHdl, Edit&, void) + { + callModifyHdl(); + } + + //= SpinCellController + + + SpinCellController::SpinCellController(SpinField* pWin) + :CellController(pWin) + { + GetSpinWindow().SetModifyHdl( LINK(this, SpinCellController, ModifyHdl) ); + } + + const SpinField& SpinCellController::GetSpinWindow() const + { + return static_cast<const SpinField &>(GetWindow()); + } + + SpinField& SpinCellController::GetSpinWindow() + { + return static_cast<SpinField &>(GetWindow()); + } + + void SpinCellController::SetModified() + { + GetSpinWindow().SetModifyFlag(); + } + + + void SpinCellController::ClearModified() + { + GetSpinWindow().ClearModifyFlag(); + } + + + bool SpinCellController::MoveAllowed(const KeyEvent& rEvt) const + { + bool bResult; + switch (rEvt.GetKeyCode().GetCode()) + { + case KEY_END: + case KEY_RIGHT: + { + Selection aSel = GetSpinWindow().GetSelection(); + bResult = !aSel && aSel.Max() == GetSpinWindow().GetText().getLength(); + } break; + case KEY_HOME: + case KEY_LEFT: + { + Selection aSel = GetSpinWindow().GetSelection(); + bResult = !aSel && aSel.Min() == 0; + } break; + default: + bResult = true; + } + return bResult; + } + + + bool SpinCellController::IsModified() const + { + return GetSpinWindow().IsModified(); + } + + IMPL_LINK_NOARG(SpinCellController, ModifyHdl, Edit&, void) + { + callModifyHdl(); + } + + //= FormattedFieldCellController + + + FormattedFieldCellController::FormattedFieldCellController( FormattedField* _pFormatted ) + :EditCellController( _pFormatted ) + { + } + + + void FormattedFieldCellController::CommitModifications() + { + static_cast< FormattedField& >( GetWindow() ).Commit(); + } + + + //= MultiLineTextCell + + + void MultiLineTextCell::Modify() + { + GetTextEngine()->SetModified( true ); + VclMultiLineEdit::Modify(); + } + + + bool MultiLineTextCell::dispatchKeyEvent( const KeyEvent& _rEvent ) + { + Selection aOldSelection( GetSelection() ); + + bool bWasModified = IsModified(); + ClearModifyFlag( ); + + bool bHandled = GetTextView()->KeyInput( _rEvent ); + + bool bIsModified = IsModified(); + if ( bWasModified && !bIsModified ) + // not sure whether this can really happen + SetModifyFlag(); + + if ( bHandled ) // the view claimed it handled the key input + { + // unfortunately, KeyInput also returns <TRUE/> (means "I handled this key input") + // when nothing really changed. Let's care for this. + Selection aNewSelection( GetSelection() ); + if ( aNewSelection != aOldSelection // selection changed + || bIsModified // or some other modification + ) + return true; + } + return false; + } + + + bool MultiLineTextCell::PreNotify( NotifyEvent& rNEvt ) + { + if ( rNEvt.GetType() == MouseNotifyEvent::KEYINPUT ) + { + if ( IsWindowOrChild( rNEvt.GetWindow() ) ) + { + // give the text view a chance to handle the keys + // this is necessary since a lot of keys which are normally handled + // by this view (in KeyInput) are intercepted by the EditBrowseBox, + // which uses them for other reasons. An example is the KeyUp key, + // which is used by both the text view and the edit browse box + + const KeyEvent* pKeyEvent = rNEvt.GetKeyEvent(); + const vcl::KeyCode& rKeyCode = pKeyEvent->GetKeyCode(); + sal_uInt16 nCode = rKeyCode.GetCode(); + + if ( ( nCode == KEY_RETURN ) && ( rKeyCode.GetModifier() == KEY_MOD1 ) ) + { + KeyEvent aEvent( pKeyEvent->GetCharCode(), + vcl::KeyCode( KEY_RETURN ), + pKeyEvent->GetRepeat() + ); + if ( dispatchKeyEvent( aEvent ) ) + return true; + } + + if ( ( nCode != KEY_TAB ) && ( nCode != KEY_RETURN ) ) // everything but tab and enter + { + if ( dispatchKeyEvent( *pKeyEvent ) ) + return true; + } + } + } + return VclMultiLineEdit::PreNotify( rNEvt ); + } + + +} // namespace svt + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svtools/source/brwbox/editbrowsebox.cxx b/svtools/source/brwbox/editbrowsebox.cxx new file mode 100644 index 000000000..12ef6d6fd --- /dev/null +++ b/svtools/source/brwbox/editbrowsebox.cxx @@ -0,0 +1,1326 @@ +/* -*- 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 . + */ + +#include <svtools/editbrowsebox.hxx> + +#include <vcl/svapp.hxx> +#include <tools/debug.hxx> +#include <vcl/window.hxx> + +#include <vcl/button.hxx> +#include <vcl/edit.hxx> +#include <vcl/settings.hxx> + +#include <bitmaps.hlst> + +#include <algorithm> +#include "editbrowseboximpl.hxx" +#include <com/sun/star/accessibility/AccessibleEventId.hpp> + + +namespace svt +{ + + namespace + { + + GetFocusFlags getRealGetFocusFlags( vcl::Window* _pWindow ) + { + GetFocusFlags nFlags = GetFocusFlags::NONE; + while ( _pWindow && nFlags == GetFocusFlags::NONE ) + { + nFlags = _pWindow->GetGetFocusFlags( ); + _pWindow = _pWindow->GetParent(); + } + return nFlags; + } + } + + using namespace ::com::sun::star::uno; + using namespace com::sun::star::accessibility::AccessibleEventId; + + + IEditImplementation::~IEditImplementation() + { + } + + //= EditBrowserHeader + + void EditBrowserHeader::DoubleClick() + { + sal_uInt16 nColId = GetCurItemId(); + if (nColId) + { + sal_uInt32 nAutoWidth = static_cast<EditBrowseBox*>(GetParent())->GetAutoColumnWidth(nColId); + if (nAutoWidth != static_cast<EditBrowseBox*>(GetParent())->GetColumnWidth(nColId)) + { + static_cast<EditBrowseBox*>(GetParent())->SetColumnWidth(nColId, nAutoWidth); + static_cast<EditBrowseBox*>(GetParent())->ColumnResized(nColId); + } + } + } + + //= EditBrowseBox + + void EditBrowseBox::BrowserMouseEventPtr::Clear() + { + pEvent.reset(); + } + + void EditBrowseBox::BrowserMouseEventPtr::Set(const BrowserMouseEvent* pEvt, bool bIsDown) + { + if (pEvt == pEvent.get()) + { + bDown = bIsDown; + return; + } + pEvent.reset(); + if (pEvt) + { + pEvent.reset(new BrowserMouseEvent(pEvt->GetWindow(), + *pEvt, + pEvt->GetRow(), + pEvt->GetColumn(), + pEvt->GetColumnId(), + pEvt->GetRect())); + bDown = bIsDown; + } + } + + EditBrowseBox::EditBrowseBox( vcl::Window* pParent, EditBrowseBoxFlags nBrowserFlags, WinBits nBits, BrowserMode _nMode ) + :BrowseBox( pParent, nBits, _nMode ) + ,nStartEvent(nullptr) + ,nEndEvent(nullptr) + ,nCellModifiedEvent(nullptr) + ,m_pFocusWhileRequest(nullptr) + ,nPaintRow(-1) + ,nEditRow(-1) + ,nEditCol(0) + ,bHasFocus(false) + ,bPaintStatus(true) + ,bActiveBeforeTracking( false ) + ,m_nBrowserFlags(nBrowserFlags) + ,pHeader(nullptr) + { + m_aImpl.reset(new EditBrowseBoxImpl); + + SetCompoundControl(true); + + ImplInitSettings(true, true, true); + + pCheckBoxPaint = VclPtr<CheckBoxControl>::Create(&GetDataWindow()); + pCheckBoxPaint->SetPaintTransparent( true ); + pCheckBoxPaint->SetBackground(); + } + + void EditBrowseBox::Init() + { + // late construction + } + + EditBrowseBox::~EditBrowseBox() + { + disposeOnce(); + } + + void EditBrowseBox::dispose() + { + if (nStartEvent) + Application::RemoveUserEvent(nStartEvent); + if (nEndEvent) + Application::RemoveUserEvent(nEndEvent); + if (nCellModifiedEvent) + Application::RemoveUserEvent(nCellModifiedEvent); + + pCheckBoxPaint.disposeAndClear(); + m_pFocusWhileRequest.clear(); + pHeader.clear(); + BrowseBox::dispose(); + } + + + void EditBrowseBox::RemoveRows() + { + BrowseBox::Clear(); + nEditRow = nPaintRow = -1; + nEditCol = 0; + } + + + VclPtr<BrowserHeader> EditBrowseBox::CreateHeaderBar(BrowseBox* pParent) + { + pHeader = imp_CreateHeaderBar(pParent); + if (!IsUpdateMode()) + pHeader->SetUpdateMode(false); + return pHeader; + } + + + VclPtr<BrowserHeader> EditBrowseBox::imp_CreateHeaderBar(BrowseBox* pParent) + { + return VclPtr<EditBrowserHeader>::Create(pParent); + } + + + void EditBrowseBox::LoseFocus() + { + BrowseBox::LoseFocus(); + DetermineFocus(); + } + + + void EditBrowseBox::GetFocus() + { + BrowseBox::GetFocus(); + + // This should handle the case that the BrowseBox (or one of its children) + // gets the focus from outside by pressing Tab + if (IsEditing() && Controller()->GetWindow().IsVisible()) + Controller()->GetWindow().GrabFocus(); + + DetermineFocus( getRealGetFocusFlags( this ) ); + } + + + bool EditBrowseBox::SeekRow(long nRow) + { + nPaintRow = nRow; + return true; + } + + + IMPL_LINK_NOARG(EditBrowseBox, StartEditHdl, void*, void) + { + nStartEvent = nullptr; + if (IsEditing()) + { + EnableAndShow(); + if (!ControlHasFocus() && (m_pFocusWhileRequest.get() == Application::GetFocusWindow())) + aController->GetWindow().GrabFocus(); + } + } + + void EditBrowseBox::PaintField( vcl::RenderContext& rDev, const tools::Rectangle& rRect, + sal_uInt16 nColumnId ) const + { + if (nColumnId == HandleColumnId) + { + if (bPaintStatus) + PaintStatusCell(rDev, rRect); + } + else + { + // don't paint the current cell + if (&rDev == &GetDataWindow()) + // but only if we're painting onto our data win (which is the usual painting) + if (nPaintRow == nEditRow) + { + if (IsEditing() && nEditCol == nColumnId && aController->GetWindow().IsVisible()) + return; + } + PaintCell(rDev, rRect, nColumnId); + } + } + + Image EditBrowseBox::GetImage(RowStatus eStatus) const + { + BitmapEx aBitmap; + bool bNeedMirror = IsRTLEnabled(); + switch (eStatus) + { + case CURRENT: + aBitmap = BitmapEx(BMP_CURRENT); + break; + case CURRENTNEW: + aBitmap = BitmapEx(BMP_CURRENTNEW); + break; + case MODIFIED: + aBitmap = BitmapEx(BMP_MODIFIED); + bNeedMirror = false; // the pen is not mirrored + break; + case NEW: + aBitmap = BitmapEx(BMP_NEW); + break; + case DELETED: + aBitmap = BitmapEx(BMP_DELETED); + break; + case PRIMARYKEY: + aBitmap = BitmapEx(BMP_PRIMARYKEY); + break; + case CURRENT_PRIMARYKEY: + aBitmap = BitmapEx(BMP_CURRENT_PRIMARYKEY); + break; + case FILTER: + aBitmap = BitmapEx(BMP_FILTER); + break; + case HEADERFOOTER: + aBitmap = BitmapEx(BMP_HEADERFOOTER); + break; + case CLEAN: + break; + } + if ( bNeedMirror ) + { + aBitmap.Mirror( BmpMirrorFlags::Horizontal ); + } + return Image(aBitmap); + } + + void EditBrowseBox::PaintStatusCell(OutputDevice& rDev, const tools::Rectangle& rRect) const + { + if (nPaintRow < 0) + return; + + RowStatus eStatus = GetRowStatus( nPaintRow ); + EditBrowseBoxFlags nBrowserFlags = GetBrowserFlags(); + + if (nBrowserFlags & EditBrowseBoxFlags::NO_HANDLE_COLUMN_CONTENT) + return; + + // draw the text of the header column + if (nBrowserFlags & EditBrowseBoxFlags::HANDLE_COLUMN_TEXT ) + { + rDev.DrawText( rRect, GetCellText( nPaintRow, 0 ), + DrawTextFlags::Center | DrawTextFlags::VCenter | DrawTextFlags::Clip ); + } + // draw an image + else if (eStatus != CLEAN && rDev.GetOutDevType() == OUTDEV_WINDOW) + { + Image aImage(GetImage(eStatus)); + // calc the image position + Size aImageSize(aImage.GetSizePixel()); + aImageSize.setWidth( CalcZoom(aImageSize.Width()) ); + aImageSize.setHeight( CalcZoom(aImageSize.Height()) ); + Point aPos( rRect.TopLeft() ); + + if ( ( aImageSize.Width() > rRect.GetWidth() ) || ( aImageSize.Height() > rRect.GetHeight() ) ) + rDev.SetClipRegion(vcl::Region(rRect)); + + if ( aImageSize.Width() < rRect.GetWidth() ) + aPos.AdjustX(( rRect.GetWidth() - aImageSize.Width() ) / 2 ); + + if ( aImageSize.Height() < rRect.GetHeight() ) + aPos.AdjustY(( rRect.GetHeight() - aImageSize.Height() ) / 2 ); + + if ( IsZoom() ) + rDev.DrawImage( aPos, aImageSize, aImage ); + else + rDev.DrawImage( aPos, aImage ); + + if (rDev.IsClipRegion()) + rDev.SetClipRegion(); + } + } + + + void EditBrowseBox::ImplStartTracking() + { + bActiveBeforeTracking = IsEditing(); + if ( bActiveBeforeTracking ) + { + DeactivateCell(); + PaintImmediately(); + } + + BrowseBox::ImplStartTracking(); + } + + + void EditBrowseBox::ImplEndTracking() + { + if ( bActiveBeforeTracking ) + ActivateCell(); + bActiveBeforeTracking = false; + + BrowseBox::ImplEndTracking(); + } + + + void EditBrowseBox::RowHeightChanged() + { + if ( IsEditing() ) + { + tools::Rectangle aRect( GetCellRect( nEditRow, nEditCol, false ) ); + CellControllerRef aCellController( Controller() ); + ResizeController( aCellController, aRect ); + aCellController->GetWindow().GrabFocus(); + } + + BrowseBox::RowHeightChanged(); + } + + + EditBrowseBox::RowStatus EditBrowseBox::GetRowStatus(long) const + { + return CLEAN; + } + + + void EditBrowseBox::KeyInput( const KeyEvent& rEvt ) + { + sal_uInt16 nCode = rEvt.GetKeyCode().GetCode(); + bool bShift = rEvt.GetKeyCode().IsShift(); + bool bCtrl = rEvt.GetKeyCode().IsMod1(); + + switch (nCode) + { + case KEY_RETURN: + if (!bCtrl && !bShift && IsTabAllowed(true)) + { + Dispatch(BROWSER_CURSORRIGHT); + } + else + BrowseBox::KeyInput(rEvt); + return; + case KEY_TAB: + if (!bCtrl && !bShift) + { + if (IsTabAllowed(true)) + Dispatch(BROWSER_CURSORRIGHT); + else + // do NOT call BrowseBox::KeyInput : this would handle the tab, but we already now + // that tab isn't allowed here. So give the Control class a chance + Control::KeyInput(rEvt); + return; + } + else if (!bCtrl && bShift) + { + if (IsTabAllowed(false)) + Dispatch(BROWSER_CURSORLEFT); + else + // do NOT call BrowseBox::KeyInput : this would handle the tab, but we already now + // that tab isn't allowed here. So give the Control class a chance + Control::KeyInput(rEvt); + return; + } + [[fallthrough]]; + default: + BrowseBox::KeyInput(rEvt); + } + } + + + void EditBrowseBox::MouseButtonDown(const BrowserMouseEvent& rEvt) + { + // absorb double clicks + if (rEvt.GetClicks() > 1 && rEvt.GetRow() >= 0) + return; + + // we are about to leave the current cell. If there is a "this cell has been modified" notification + // pending (asynchronously), this may be deadly -> do it synchronously + if ( nCellModifiedEvent ) + { + Application::RemoveUserEvent( nCellModifiedEvent ); + nCellModifiedEvent = nullptr; + LINK( this, EditBrowseBox, CellModifiedHdl ).Call( nullptr ); + } + + if (rEvt.GetColumnId() == HandleColumnId) + { // it was the handle column. save the current cell content if necessary + // (clicking on the handle column results in selecting the current row) + if (IsEditing() && aController->IsModified()) + SaveModified(); + } + + aMouseEvent.Set(&rEvt,true); + BrowseBox::MouseButtonDown(rEvt); + aMouseEvent.Clear(); + + if (m_nBrowserFlags & EditBrowseBoxFlags::ACTIVATE_ON_BUTTONDOWN) + { + // the base class does not travel upon MouseButtonDown, but implActivateCellOnMouseEvent assumes we traveled ... + GoToRowColumnId( rEvt.GetRow(), rEvt.GetColumnId() ); + if (rEvt.GetRow() >= 0) + implActivateCellOnMouseEvent(rEvt, false); + } + } + + + void EditBrowseBox::MouseButtonUp( const BrowserMouseEvent& rEvt ) + { + // absorb double clicks + if (rEvt.GetClicks() > 1 && rEvt.GetRow() >= 0) + return; + + aMouseEvent.Set(&rEvt,false); + BrowseBox::MouseButtonUp(rEvt); + aMouseEvent.Clear(); + + if (!(m_nBrowserFlags & EditBrowseBoxFlags::ACTIVATE_ON_BUTTONDOWN)) + if (rEvt.GetRow() >= 0) + implActivateCellOnMouseEvent(rEvt, true); + } + + bool EditBrowseBox::ControlHasFocus() const + { + Window* pControlWindow = aController ? &aController->GetWindow() : nullptr; + if (ControlBase* pControlBase = dynamic_cast<ControlBase*>(pControlWindow)) + return pControlBase->ControlHasFocus(); + return pControlWindow && pControlWindow->HasChildPathFocus(); + } + + void EditBrowseBox::implActivateCellOnMouseEvent(const BrowserMouseEvent& _rEvt, bool _bUp) + { + if (!IsEditing()) + ActivateCell(); + else if (IsEditing() && !aController->GetWindow().IsEnabled()) + DeactivateCell(); + else if (IsEditing() && !ControlHasFocus()) + AsynchGetFocus(); + + if (!(IsEditing() && aController->GetWindow().IsEnabled() && aController->WantMouseEvent())) + return; + +// forwards the event to the control + + // If the field has been moved previously, we have to adjust the position + + aController->GetWindow().GrabFocus(); + + // the position of the event relative to the controller's window + Point aPos = _rEvt.GetPosPixel() - _rEvt.GetRect().TopLeft(); + // the (child) window which should really get the event + vcl::Window* pRealHandler = aController->GetWindow().FindWindow(aPos); + if (pRealHandler) + // the coords relative to this real handler + aPos -= pRealHandler->GetPosPixel(); + else + pRealHandler = &aController->GetWindow(); + + // the faked event + MouseEvent aEvent(aPos, _rEvt.GetClicks(), _rEvt.GetMode(), + _rEvt.GetButtons(), + _rEvt.GetModifier()); + + pRealHandler->MouseButtonDown(aEvent); + if (_bUp) + pRealHandler->MouseButtonUp(aEvent); + + vcl::Window *pWin = &aController->GetWindow(); + if (!pWin->IsTracking()) + { + for (pWin = pWin->GetWindow(GetWindowType::FirstChild); + pWin && !pWin->IsTracking(); + pWin = pWin->GetWindow(GetWindowType::Next)) + { + } + } + if (pWin && pWin->IsTracking()) + pWin->EndTracking(); + } + + + void EditBrowseBox::Dispatch( sal_uInt16 _nId ) + { + if ( _nId == BROWSER_ENHANCESELECTION ) + { // this is a workaround for the bug in the base class: + // if the row selection is to be extended (which is what BROWSER_ENHANCESELECTION tells us) + // then the base class does not revert any column selections, while, for doing a "simple" + // selection (BROWSER_SELECT), it does. In fact, it does not only revert the col selection then, + // but also any current row selections. + // This clearly tells me that the both ids are for row selection only - there this behaviour does + // make sense. + // But here, where we have column selection, too, we take care of this ourself. + if ( GetSelectColumnCount( ) ) + { + while ( GetSelectColumnCount( ) ) + SelectColumnPos( + sal::static_int_cast< sal_uInt16 >(FirstSelectedColumn()), + false ); + Select(); + } + } + BrowseBox::Dispatch( _nId ); + } + + bool EditBrowseBox::PreNotify(NotifyEvent& rEvt) + { + if (rEvt.GetType() == MouseNotifyEvent::KEYINPUT) + { + if ( (IsEditing() && ControlHasFocus()) + || rEvt.GetWindow() == &GetDataWindow() + || (!IsEditing() && HasChildPathFocus()) + ) + { + const KeyEvent* pKeyEvent = rEvt.GetKeyEvent(); + sal_uInt16 nCode = pKeyEvent->GetKeyCode().GetCode(); + bool bShift = pKeyEvent->GetKeyCode().IsShift(); + bool bCtrl = pKeyEvent->GetKeyCode().IsMod1(); + bool bAlt = pKeyEvent->GetKeyCode().IsMod2(); + bool bLocalSelect = false; + bool bNonEditOnly = false; + sal_uInt16 nId = BROWSER_NONE; + + if (!bAlt && !bCtrl && !bShift ) + switch ( nCode ) + { + case KEY_DOWN: nId = BROWSER_CURSORDOWN; break; + case KEY_UP: nId = BROWSER_CURSORUP; break; + case KEY_PAGEDOWN: nId = BROWSER_CURSORPAGEDOWN; break; + case KEY_PAGEUP: nId = BROWSER_CURSORPAGEUP; break; + case KEY_HOME: nId = BROWSER_CURSORHOME; break; + case KEY_END: nId = BROWSER_CURSOREND; break; + + case KEY_TAB: + // ask if traveling to the next cell is allowed + if (IsTabAllowed(true)) + nId = BROWSER_CURSORRIGHT; + break; + + case KEY_RETURN: + // save the cell content (if necessary) + if (IsEditing() && aController->IsModified() && !SaveModified()) + { + // maybe we're not visible ... + EnableAndShow(); + aController->GetWindow().GrabFocus(); + return true; + } + // ask if traveling to the next cell is allowed + if (IsTabAllowed(true)) + nId = BROWSER_CURSORRIGHT; + + break; + case KEY_RIGHT: nId = BROWSER_CURSORRIGHT; break; + case KEY_LEFT: nId = BROWSER_CURSORLEFT; break; + case KEY_SPACE: nId = BROWSER_SELECT; bNonEditOnly = bLocalSelect = true; break; + } + + if ( !bAlt && !bCtrl && bShift ) + switch ( nCode ) + { + case KEY_DOWN: nId = BROWSER_SELECTDOWN; bLocalSelect = true; break; + case KEY_UP: nId = BROWSER_SELECTUP; bLocalSelect = true; break; + case KEY_HOME: nId = BROWSER_SELECTHOME; bLocalSelect = true; break; + case KEY_END: nId = BROWSER_SELECTEND; bLocalSelect = true; break; + case KEY_TAB: + if (IsTabAllowed(false)) + nId = BROWSER_CURSORLEFT; + break; + } + + if ( !bAlt && bCtrl && bShift ) + switch ( nCode ) + { + case KEY_SPACE: nId = BROWSER_SELECTCOLUMN; bLocalSelect = true; break; + } + + + if ( !bAlt && bCtrl && !bShift ) + switch ( nCode ) + { + case KEY_DOWN: nId = BROWSER_SCROLLUP; break; + case KEY_UP: nId = BROWSER_SCROLLDOWN; break; + case KEY_PAGEDOWN: nId = BROWSER_CURSORENDOFFILE; break; + case KEY_PAGEUP: nId = BROWSER_CURSORTOPOFFILE; break; + case KEY_HOME: nId = BROWSER_CURSORTOPOFSCREEN; break; + case KEY_END: nId = BROWSER_CURSORENDOFSCREEN; break; + case KEY_SPACE: nId = BROWSER_ENHANCESELECTION; bLocalSelect = true; break; + } + + + if ( ( nId != BROWSER_NONE ) + && ( !IsEditing() + || ( !bNonEditOnly + && aController->MoveAllowed( *pKeyEvent ) + ) + ) + ) + { + if (nId == BROWSER_SELECT || BROWSER_SELECTCOLUMN == nId ) + { + // save the cell content (if necessary) + if (IsEditing() && aController->IsModified() && !SaveModified()) + { + // maybe we're not visible ... + EnableAndShow(); + aController->GetWindow().GrabFocus(); + return true; + } + } + + Dispatch(nId); + + if (bLocalSelect && (GetSelectRowCount() || GetSelection() != nullptr)) + DeactivateCell(); + return true; + } + } + } + return BrowseBox::PreNotify(rEvt); + } + + bool EditBrowseBox::IsTabAllowed(bool) const + { + return true; + } + + + bool EditBrowseBox::EventNotify(NotifyEvent& rEvt) + { + switch (rEvt.GetType()) + { + case MouseNotifyEvent::GETFOCUS: + DetermineFocus( getRealGetFocusFlags( this ) ); + break; + + case MouseNotifyEvent::LOSEFOCUS: + DetermineFocus(); + break; + + default: + break; + } + return BrowseBox::EventNotify(rEvt); + } + + + void EditBrowseBox::StateChanged( StateChangedType nType ) + { + BrowseBox::StateChanged( nType ); + + bool bNeedCellReActivation = false; + if ( nType == StateChangedType::Mirroring ) + { + bNeedCellReActivation = true; + } + else if ( nType == StateChangedType::Zoom ) + { + ImplInitSettings( true, false, false ); + bNeedCellReActivation = true; + } + else if ( nType == StateChangedType::ControlFont ) + { + ImplInitSettings( true, false, false ); + Invalidate(); + } + else if ( nType == StateChangedType::ControlForeground ) + { + ImplInitSettings( false, true, false ); + Invalidate(); + } + else if ( nType == StateChangedType::ControlBackground ) + { + ImplInitSettings( false, false, true ); + Invalidate(); + } + else if (nType == StateChangedType::Style) + { + WinBits nStyle = GetStyle(); + if (!(nStyle & WB_NOTABSTOP) ) + nStyle |= WB_TABSTOP; + + SetStyle(nStyle); + } + if ( bNeedCellReActivation ) + { + if ( IsEditing() ) + { + DeactivateCell(); + ActivateCell(); + } + } + } + + + void EditBrowseBox::DataChanged( const DataChangedEvent& rDCEvt ) + { + BrowseBox::DataChanged( rDCEvt ); + + if ((( rDCEvt.GetType() == DataChangedEventType::SETTINGS ) || + ( rDCEvt.GetType() == DataChangedEventType::DISPLAY )) && + ( rDCEvt.GetFlags() & AllSettingsFlags::STYLE )) + { + ImplInitSettings( true, true, true ); + Invalidate(); + } + } + + void EditBrowseBox::ImplInitSettings( bool bFont, bool bForeground, bool bBackground ) + { + const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings(); + + if (bFont) + { + vcl::Font aFont = rStyleSettings.GetFieldFont(); + if (IsControlFont()) + { + GetDataWindow().SetControlFont(GetControlFont()); + aFont.Merge(GetControlFont()); + } + else + GetDataWindow().SetControlFont(); + + GetDataWindow().SetZoomedPointFont(GetDataWindow(), aFont); + } + + if (bFont || bForeground) + { + Color aTextColor = rStyleSettings.GetFieldTextColor(); + if (IsControlForeground()) + { + aTextColor = GetControlForeground(); + GetDataWindow().SetControlForeground(aTextColor); + } + else + GetDataWindow().SetControlForeground(); + + GetDataWindow().SetTextColor( aTextColor ); + } + + if (!bBackground) // FIXME: Outside of Paint Hierarchy + return; + + if (GetDataWindow().IsControlBackground()) + { + GetDataWindow().SetControlBackground(GetControlBackground()); + GetDataWindow().SetBackground(GetDataWindow().GetControlBackground()); + GetDataWindow().SetFillColor(GetDataWindow().GetControlBackground()); + } + else + { + GetDataWindow().SetControlBackground(); + GetDataWindow().SetBackground(rStyleSettings.GetFieldColor()); + GetDataWindow().SetFillColor(rStyleSettings.GetFieldColor()); + } + } + + + bool EditBrowseBox::IsCursorMoveAllowed(long nNewRow, sal_uInt16 nNewColId) const + { + sal_uInt16 nInfo = 0; + + if (GetSelectColumnCount() || (aMouseEvent.Is() && aMouseEvent->GetRow() < 0)) + nInfo |= COLSELECT; + if ((GetSelection() != nullptr && GetSelectRowCount()) || + (aMouseEvent.Is() && aMouseEvent->GetColumnId() == HandleColumnId)) + nInfo |= ROWSELECT; + if (!nInfo && nNewRow != nEditRow) + nInfo |= ROWCHANGE; + if (!nInfo && nNewColId != nEditCol) + nInfo |= COLCHANGE; + + if (nInfo == 0) // nothing happened + return true; + + // save the cell content + if (IsEditing() && aController->IsModified() && !const_cast<EditBrowseBox *>(this)->SaveModified()) + { + // maybe we're not visible ... + EnableAndShow(); + aController->GetWindow().GrabFocus(); + return false; + } + + EditBrowseBox * pTHIS = const_cast<EditBrowseBox *> (this); + + // save the cell content if + // a) a selection is being made + // b) the row is changing + if (IsModified() && (nInfo & (ROWCHANGE | COLSELECT | ROWSELECT)) && + !pTHIS->SaveRow()) + { + if (nInfo & COLSELECT || + nInfo & ROWSELECT) + { + // cancel selected + pTHIS->SetNoSelection(); + } + + if (IsEditing()) + { + if (!Controller()->GetWindow().IsVisible()) + { + EnableAndShow(); + } + aController->GetWindow().GrabFocus(); + } + return false; + } + + if (nNewRow != nEditRow) + { + vcl::Window& rWindow = GetDataWindow(); + if ((nEditRow >= 0) && !(GetBrowserFlags() & EditBrowseBoxFlags::NO_HANDLE_COLUMN_CONTENT)) + { + tools::Rectangle aRect = GetFieldRectPixel(nEditRow, 0, false ); + // status cell should be painted if and only if text is displayed + pTHIS->bPaintStatus = ( GetBrowserFlags() & EditBrowseBoxFlags::HANDLE_COLUMN_TEXT ) == EditBrowseBoxFlags::HANDLE_COLUMN_TEXT; + rWindow.Invalidate(aRect); + pTHIS->bPaintStatus = true; + } + + // don't paint during row change + rWindow.EnablePaint(false); + + // the last veto chance for derived classes + if (!pTHIS->CursorMoving(nNewRow, nNewColId)) + { + pTHIS->InvalidateStatusCell(nEditRow); + rWindow.EnablePaint(true); + return false; + } + else + { + rWindow.EnablePaint(true); + return true; + } + } + else + return pTHIS->CursorMoving(nNewRow, nNewColId); + } + + + void EditBrowseBox::ColumnMoved(sal_uInt16 nId) + { + BrowseBox::ColumnMoved(nId); + if (IsEditing()) + { + tools::Rectangle aRect( GetCellRect(nEditRow, nEditCol, false)); + CellControllerRef aControllerRef = Controller(); + ResizeController(aControllerRef, aRect); + Controller()->GetWindow().GrabFocus(); + } + } + + + bool EditBrowseBox::SaveRow() + { + return true; + } + + + bool EditBrowseBox::CursorMoving(long, sal_uInt16) + { + DeactivateCell(false); + return true; + } + + + void EditBrowseBox::CursorMoved() + { + long nNewRow = GetCurRow(); + if (nEditRow != nNewRow) + { + if (!(GetBrowserFlags() & EditBrowseBoxFlags::NO_HANDLE_COLUMN_CONTENT)) + InvalidateStatusCell(nNewRow); + nEditRow = nNewRow; + } + ActivateCell(); + GetDataWindow().EnablePaint(true); + // should not be called here because the descant event is not needed here + //BrowseBox::CursorMoved(); + } + + + void EditBrowseBox::EndScroll() + { + if (IsEditing()) + { + tools::Rectangle aRect = GetCellRect(nEditRow, nEditCol, false); + ResizeController(aController,aRect); + AsynchGetFocus(); + } + BrowseBox::EndScroll(); + } + + + void EditBrowseBox::ActivateCell(long nRow, sal_uInt16 nCol, bool bCellFocus) + { + if (IsEditing()) + return; + + nEditCol = nCol; + + if ((GetSelectRowCount() && GetSelection() != nullptr) || GetSelectColumnCount() || + (aMouseEvent.Is() && (aMouseEvent.IsDown() || aMouseEvent->GetClicks() > 1))) // nothing happens on MouseDown + { + return; + } + + if (!(nEditRow >= 0 && nEditCol > HandleColumnId)) + return; + + aController = GetController(nRow, nCol); + if (aController.is()) + { + tools::Rectangle aRect( GetCellRect(nEditRow, nEditCol, false)); + ResizeController(aController, aRect); + + InitController(aController, nEditRow, nEditCol); + + aController->ClearModified(); + aController->SetModifyHdl(LINK(this,EditBrowseBox,ModifyHdl)); + EnableAndShow(); + + if ( isAccessibleAlive() ) + implCreateActiveAccessible(); + + // activate the cell only of the browser has the focus + if ( bHasFocus && bCellFocus ) + AsynchGetFocus(); + } + else + { + // no controller -> we have a new "active descendant" + if ( isAccessibleAlive() && HasFocus() ) + { + commitTableEvent( + ACTIVE_DESCENDANT_CHANGED, + makeAny( CreateAccessibleCell( nRow, GetColumnPos( nCol -1) ) ), + Any() + ); + } + } + } + + + void EditBrowseBox::DeactivateCell(bool bUpdate) + { + if (!IsEditing()) + return; + + if ( isAccessibleAlive() ) + { + commitBrowseBoxEvent( CHILD, Any(), makeAny( m_aImpl->m_xActiveCell ) ); + m_aImpl->clearActiveCell(); + } + + aOldController = aController; + aController.clear(); + + // reset the modify handler + aOldController->SetModifyHdl(Link<LinkParamNone*,void>()); + + if (bHasFocus) + GrabFocus(); // ensure that we have (and keep) the focus + + aOldController->suspend(); + + // update if requested + if (bUpdate) + PaintImmediately(); + + // release the controller (asynchronously) + if (nEndEvent) + Application::RemoveUserEvent(nEndEvent); + nEndEvent = Application::PostUserEvent(LINK(this,EditBrowseBox,EndEditHdl), nullptr, true); + } + + + tools::Rectangle EditBrowseBox::GetCellRect(long nRow, sal_uInt16 nColId, bool bRel) const + { + tools::Rectangle aRect( GetFieldRectPixel(nRow, nColId, bRel)); + if ((GetMode() & BrowserMode::CURSOR_WO_FOCUS) == BrowserMode::CURSOR_WO_FOCUS) + { + aRect.AdjustTop(1 ); + aRect.AdjustBottom( -1 ); + } + return aRect; + } + + + IMPL_LINK_NOARG(EditBrowseBox, EndEditHdl, void*, void) + { + nEndEvent = nullptr; + + aOldController = CellControllerRef(); + } + + + IMPL_LINK_NOARG(EditBrowseBox, ModifyHdl, LinkParamNone*, void) + { + if (nCellModifiedEvent) + Application::RemoveUserEvent(nCellModifiedEvent); + nCellModifiedEvent = Application::PostUserEvent(LINK(this,EditBrowseBox,CellModifiedHdl), nullptr, true); + } + + + IMPL_LINK_NOARG(EditBrowseBox, CellModifiedHdl, void*, void) + { + nCellModifiedEvent = nullptr; + CellModified(); + } + + + void EditBrowseBox::ColumnResized( sal_uInt16 ) + { + if (IsEditing()) + { + tools::Rectangle aRect( GetCellRect(nEditRow, nEditCol, false)); + CellControllerRef aControllerRef = Controller(); + ResizeController(aControllerRef, aRect); + Controller()->GetWindow().GrabFocus(); + } + } + + + sal_uInt16 EditBrowseBox::AppendColumn(const OUString& rName, sal_uInt16 nWidth, sal_uInt16 nPos, sal_uInt16 nId) + { + if (nId == BROWSER_INVALIDID) + { + // look for the next free id + for (nId = ColCount(); nId > 0 && GetColumnPos(nId) != BROWSER_INVALIDID; nId--) + ; + + if (!nId) + { + // if there is no handle column + // increment the id + if ( ColCount() == 0 || GetColumnId(0) != HandleColumnId ) + nId = ColCount() + 1; + } + } + + DBG_ASSERT(nId, "EditBrowseBox::AppendColumn: invalid id!"); + + long w = nWidth; + if (!w) + w = GetDefaultColumnWidth(rName); + + InsertDataColumn(nId, rName, w, (HeaderBarItemBits::CENTER | HeaderBarItemBits::CLICKABLE), nPos); + return nId; + } + + void EditBrowseBox::Resize() + { + BrowseBox::Resize(); + + // if the window is smaller than "title line height" + "control area", + // do nothing + if (GetOutputSizePixel().Height() < + (GetControlArea().GetHeight() + GetDataWindow().GetPosPixel().Y())) + return; + + // the size of the control area + Point aPoint(GetControlArea().TopLeft()); + sal_uInt16 nX = static_cast<sal_uInt16>(aPoint.X()); + + ArrangeControls(nX, static_cast<sal_uInt16>(aPoint.Y())); + + if (!nX) + nX = USHRT_MAX; + + bool bChanged = ReserveControlArea(nX); + + //tdf#97731 if the reserved area changed size, give the controls a + //chance to adapt to the new size + if (bChanged) + { + nX = static_cast<sal_uInt16>(aPoint.X()); + ArrangeControls(nX, static_cast<sal_uInt16>(aPoint.Y())); + } + } + + void EditBrowseBox::ArrangeControls(sal_uInt16&, sal_uInt16) + { + } + + CellController* EditBrowseBox::GetController(long, sal_uInt16) + { + return nullptr; + } + + void EditBrowseBox::ResizeController(CellControllerRef const & rController, const tools::Rectangle& rRect) + { + Point aPoint(rRect.TopLeft()); + Size aSize(rRect.GetSize()); + Control& rControl = rController->GetWindow(); + auto nMinHeight = rControl.get_preferred_size().Height(); + if (nMinHeight > aSize.Height()) + { + auto nOffset = (nMinHeight - aSize.Height()) / 2; + aPoint.AdjustY(-nOffset); + aSize.setHeight(nMinHeight); + } + rControl.SetPosSizePixel(aPoint, aSize); + } + + void EditBrowseBox::InitController(CellControllerRef&, long, sal_uInt16) + { + } + + + void EditBrowseBox::CellModified() + { + } + + + bool EditBrowseBox::SaveModified() + { + return true; + } + + + void EditBrowseBox::DoubleClick(const BrowserMouseEvent& rEvt) + { + // when double clicking on the column, the optimum size will be calculated + sal_uInt16 nColId = rEvt.GetColumnId(); + if (nColId != HandleColumnId) + SetColumnWidth(nColId, GetAutoColumnWidth(nColId)); + } + + + sal_uInt32 EditBrowseBox::GetAutoColumnWidth(sal_uInt16 nColId) + { + sal_uInt32 nCurColWidth = GetColumnWidth(nColId); + sal_uInt32 nMinColWidth = CalcZoom(20); // minimum + sal_uInt32 nNewColWidth = nMinColWidth; + long nMaxRows = std::min(long(GetVisibleRows()), GetRowCount()); + long nLastVisRow = GetTopRow() + nMaxRows - 1; + + if (GetTopRow() <= nLastVisRow) // calc the column with using the cell contents + { + for (long i = GetTopRow(); i <= nLastVisRow; ++i) + nNewColWidth = std::max(nNewColWidth,GetTotalCellWidth(i,nColId) + 12); + + if (nNewColWidth == nCurColWidth) // size has not changed + nNewColWidth = GetDefaultColumnWidth(GetColumnTitle(nColId)); + } + else + nNewColWidth = GetDefaultColumnWidth(GetColumnTitle(nColId)); + return nNewColWidth; + } + + + sal_uInt32 EditBrowseBox::GetTotalCellWidth(long, sal_uInt16) + { + return 0; + } + + + void EditBrowseBox::InvalidateHandleColumn() + { + tools::Rectangle aHdlFieldRect( GetFieldRectPixel( 0, 0 )); + tools::Rectangle aInvalidRect( Point(0,0), GetOutputSizePixel() ); + aInvalidRect.SetRight( aHdlFieldRect.Right() ); + Invalidate( aInvalidRect ); + } + + + void EditBrowseBox::PaintTristate(const tools::Rectangle& rRect, const TriState& eState, bool _bEnabled) const + { + pCheckBoxPaint->GetBox().SetState(eState); + pCheckBoxPaint->SetPosSizePixel(rRect.TopLeft(), rRect.GetSize()); + + pCheckBoxPaint->GetBox().Enable(_bEnabled); + pCheckBoxPaint->Show(); + pCheckBoxPaint->SetParentUpdateMode( false ); + pCheckBoxPaint->PaintImmediately(); + pCheckBoxPaint->Hide(); + pCheckBoxPaint->SetParentUpdateMode( true ); + } + + + void EditBrowseBox::AsynchGetFocus() + { + if (nStartEvent) + Application::RemoveUserEvent(nStartEvent); + + m_pFocusWhileRequest = Application::GetFocusWindow(); + nStartEvent = Application::PostUserEvent(LINK(this,EditBrowseBox,StartEditHdl), nullptr, true); + } + + + void EditBrowseBox::SetBrowserFlags(EditBrowseBoxFlags nFlags) + { + if (m_nBrowserFlags == nFlags) + return; + + bool RowPicturesChanges = ((m_nBrowserFlags & EditBrowseBoxFlags::NO_HANDLE_COLUMN_CONTENT) != + (nFlags & EditBrowseBoxFlags::NO_HANDLE_COLUMN_CONTENT)); + m_nBrowserFlags = nFlags; + + if (RowPicturesChanges) + InvalidateStatusCell(GetCurRow()); + } + + inline void EditBrowseBox::EnableAndShow() const + { + Controller()->resume(); + } + + + CellController::CellController(Control* pW) + :pWindow( pW ) + ,bSuspended( true ) + { + + DBG_ASSERT(pWindow, "CellController::CellController: missing the window!"); + DBG_ASSERT(!pWindow->IsVisible(), "CellController::CellController: window should not be visible!"); + } + + + CellController::~CellController() + { + + } + + + void CellController::suspend( ) + { + DBG_ASSERT( bSuspended == !GetWindow().IsVisible(), "CellController::suspend: inconsistence!" ); + if ( !isSuspended( ) ) + { + CommitModifications(); + GetWindow().Hide( ); + GetWindow().Disable( ); + bSuspended = true; + } + } + + + void CellController::resume( ) + { + DBG_ASSERT( bSuspended == !GetWindow().IsVisible(), "CellController::resume: inconsistence!" ); + if ( isSuspended( ) ) + { + GetWindow().Enable( ); + GetWindow().Show( ); + bSuspended = false; + } + } + + + void CellController::CommitModifications() + { + // nothing to do in this base class + } + + + bool CellController::WantMouseEvent() const + { + return false; + } + + + void CellController::SetModified() + { + } + + + bool CellController::MoveAllowed(const KeyEvent&) const + { + return true; + } + +} // namespace svt + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svtools/source/brwbox/editbrowsebox2.cxx b/svtools/source/brwbox/editbrowsebox2.cxx new file mode 100644 index 000000000..7c84cf5eb --- /dev/null +++ b/svtools/source/brwbox/editbrowsebox2.cxx @@ -0,0 +1,200 @@ +/* -*- 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 . + */ + +#include <svtools/editbrowsebox.hxx> +#include <com/sun/star/accessibility/XAccessible.hpp> +#include <com/sun/star/accessibility/AccessibleEventId.hpp> +#include "editbrowseboximpl.hxx" +#include <comphelper/types.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <vcl/accessiblefactory.hxx> +#include <vcl/svapp.hxx> +#include <osl/diagnose.h> +#include <tools/debug.hxx> + +namespace svt +{ + using namespace com::sun::star::accessibility; + using namespace com::sun::star::uno; + using namespace ::com::sun::star::accessibility::AccessibleEventId; + + +Reference< XAccessible > EditBrowseBox::CreateAccessibleCheckBoxCell(long _nRow, sal_uInt16 _nColumnPos,const TriState& eState) +{ + Reference< XAccessible > xAccessible( GetAccessible() ); + Reference< XAccessibleContext > xAccContext; + if ( xAccessible.is() ) + xAccContext = xAccessible->getAccessibleContext(); + + Reference< XAccessible > xReturn; + if ( xAccContext.is() ) + { + xReturn = getAccessibleFactory().createAccessibleCheckBoxCell( + xAccContext->getAccessibleChild( ::vcl::BBINDEX_TABLE ), + *this, + nullptr, + _nRow, + _nColumnPos, + eState, + true + ); + } + return xReturn; +} + +sal_Int32 EditBrowseBox::GetAccessibleControlCount() const +{ + return IsEditing() ? 1 : 0; +} + +void EditBrowseBox::implCreateActiveAccessible( ) +{ + DBG_ASSERT( IsEditing(), "EditBrowseBox::implCreateActiveAccessible: not to be called if we're not editing currently!" ); + DBG_ASSERT( !m_aImpl->m_xActiveCell.is(), "EditBrowseBox::implCreateActiveAccessible: not to be called if the old one is still alive!" ); + + if ( !(!m_aImpl->m_xActiveCell.is() && IsEditing()) ) + return; + + Reference< XAccessible > xCont = aController->GetWindow().GetAccessible(); + Reference< XAccessible > xMy = GetAccessible(); + if ( !(xMy.is() && xCont.is()) ) + return; + + m_aImpl->m_xActiveCell = getAccessibleFactory().createEditBrowseBoxTableCellAccess( + xMy, // parent accessible + xCont, // control accessible + VCLUnoHelper::GetInterface( &aController->GetWindow() ), // focus window (for notifications) + *this, // the browse box + GetCurRow(), + GetColumnPos( GetCurColumnId() ) + ); + + commitBrowseBoxEvent( CHILD, makeAny( m_aImpl->m_xActiveCell ), Any() ); +} + + +Reference< XAccessible > EditBrowseBox::CreateAccessibleControl( sal_Int32 _nIndex ) +{ + DBG_ASSERT( 0 == _nIndex, "EditBrowseBox::CreateAccessibleControl: invalid index!" ); + + if ( isAccessibleAlive() ) + { + if ( !m_aImpl->m_xActiveCell.is() ) + implCreateActiveAccessible(); + } + + return m_aImpl->m_xActiveCell; +} + +void EditBrowseBoxImpl::clearActiveCell() +{ + try + { + ::comphelper::disposeComponent(m_xActiveCell); + } + catch(const Exception&) + { + OSL_FAIL( "EditBrowseBoxImpl::clearActiveCell: caught an exception while disposing the AccessibleCell!" ); + } + + m_xActiveCell = nullptr; +} + +void EditBrowseBox::GrabTableFocus() +{ + if ( aController.is() ) + aController->GetWindow().GrabFocus(); +} + +void EditBrowseBox::DetermineFocus( const GetFocusFlags _nGetFocusFlags ) +{ + bool bFocus = ControlHasFocus(); + for (vcl::Window* pWindow = Application::GetFocusWindow(); + pWindow && !bFocus; + pWindow = pWindow->GetParent()) + bFocus = pWindow == this; + + if (bFocus == bHasFocus) + return; + + bHasFocus = bFocus; + + if ( !(GetBrowserFlags( ) & EditBrowseBoxFlags::SMART_TAB_TRAVEL) ) + return; + + if ( !(bHasFocus // we got the focus + && ( _nGetFocusFlags & GetFocusFlags::Tab )) // using the TAB key + ) + return; + + long nRows = GetRowCount(); + sal_uInt16 nCols = ColCount(); + + if ( ( nRows > 0 ) && ( nCols > 0 ) ) + { + if ( _nGetFocusFlags & GetFocusFlags::Forward ) + { + if ( GetColumnId( 0 ) != HandleColumnId ) + { + GoToRowColumnId( 0, GetColumnId( 0 ) ); + } + else + { // the first column is the handle column -> not focussable + if ( nCols > 1 ) + GoToRowColumnId( 0, GetColumnId( 1 ) ); + } + } + else if ( _nGetFocusFlags & GetFocusFlags::Backward ) + { + GoToRowColumnId( nRows - 1, GetColumnId( nCols -1 ) ); + } + } +} + +tools::Rectangle EditBrowseBox::GetFieldCharacterBounds(sal_Int32 _nRow,sal_Int32 _nColumnPos,sal_Int32 _nIndex) +{ + tools::Rectangle aRect; + if ( SeekRow(_nRow) ) + { + CellController* pController = GetController( + _nRow, GetColumnId( sal::static_int_cast< sal_uInt16 >(_nColumnPos) ) ); + if ( pController ) + aRect = pController->GetWindow().GetCharacterBounds(_nIndex); + } + return aRect; +} + +sal_Int32 EditBrowseBox::GetFieldIndexAtPoint(sal_Int32 _nRow,sal_Int32 _nColumnPos,const Point& _rPoint) +{ + sal_Int32 nRet = -1; + if ( SeekRow(_nRow) ) + { + CellController* pController = GetController( + _nRow, GetColumnId( sal::static_int_cast< sal_uInt16 >(_nColumnPos) ) ); + if ( pController ) + nRet = pController->GetWindow().GetIndexForPoint(_rPoint); + } + return nRet; +} + + +} // namespace svt + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svtools/source/brwbox/editbrowseboximpl.hxx b/svtools/source/brwbox/editbrowseboximpl.hxx new file mode 100644 index 000000000..f71aab9b7 --- /dev/null +++ b/svtools/source/brwbox/editbrowseboximpl.hxx @@ -0,0 +1,35 @@ +/* -*- 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 . + */ +#pragma once + +#include <com/sun/star/accessibility/XAccessible.hpp> + +namespace svt +{ + // impl class for the class EditBrowseBox + class EditBrowseBoxImpl + { + public: + css::uno::Reference< css::accessibility::XAccessible > m_xActiveCell; + + void clearActiveCell(); + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |