diff options
Diffstat (limited to 'svtools/source/brwbox')
-rw-r--r-- | svtools/source/brwbox/brwbox1.cxx | 2400 | ||||
-rw-r--r-- | svtools/source/brwbox/brwbox2.cxx | 2012 | ||||
-rw-r--r-- | svtools/source/brwbox/brwbox3.cxx | 560 | ||||
-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 | 652 | ||||
-rw-r--r-- | svtools/source/brwbox/datwin.hxx | 83 | ||||
-rw-r--r-- | svtools/source/brwbox/ebbcontrols.cxx | 768 | ||||
-rw-r--r-- | svtools/source/brwbox/editbrowsebox.cxx | 1284 | ||||
-rw-r--r-- | svtools/source/brwbox/editbrowsebox2.cxx | 200 | ||||
-rw-r--r-- | svtools/source/brwbox/editbrowseboximpl.hxx | 35 | ||||
-rw-r--r-- | svtools/source/brwbox/recorditemwindow.cxx | 113 |
12 files changed, 8294 insertions, 0 deletions
diff --git a/svtools/source/brwbox/brwbox1.cxx b/svtools/source/brwbox/brwbox1.cxx new file mode 100644 index 0000000000..cc78b514f7 --- /dev/null +++ b/svtools/source/brwbox/brwbox1.cxx @@ -0,0 +1,2400 @@ +/* -*- 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 <svtools/scrolladaptor.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/InterimItemWindow.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.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; + m_nDataRowHeight = 0; + nTitleLines = 1; + nFirstCol = 0; + nTopRow = 0; + nCurRow = BROWSER_ENDOFSELECTION; + nCurColId = 0; + nResizeX = 0; + nMinResizeX = 0; + nDragX = 0; + nResizeCol = 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, HorzScrollHdl ) ); + pDataWin->Show(); + + SetMode( nMode ); + bSelectionIsVisible = bKeepHighlight; + bHasFocus = HasChildPathFocus(); + pDataWin->nCursorHidden = + ( bHasFocus ? 0 : 1 ) + ( GetUpdateMode() ? 0 : 1 ); +} + +// we're just measuring the "real" NavigationBar +class MeasureStatusBar final : public InterimItemWindow +{ +private: + std::unique_ptr<weld::Label> m_xRecordText; + std::unique_ptr<weld::Entry> m_xAbsolute; + std::unique_ptr<weld::Label> m_xRecordOf; + std::unique_ptr<weld::Label> m_xRecordCount; +public: + MeasureStatusBar(vcl::Window *pParent) + : InterimItemWindow(pParent, "svx/ui/navigationbar.ui", "NavigationBar") + , m_xRecordText(m_xBuilder->weld_label("recordtext")) + , m_xAbsolute(m_xBuilder->weld_entry("entry-noframe")) + , m_xRecordOf(m_xBuilder->weld_label("recordof")) + , m_xRecordCount(m_xBuilder->weld_label("recordcount")) + { + vcl::Font aApplFont(Application::GetSettings().GetStyleSettings().GetToolFont()); + m_xAbsolute->set_font(aApplFont); + m_xRecordText->set_font(aApplFont); + m_xRecordOf->set_font(aApplFont); + m_xRecordCount->set_font(aApplFont); + + SetSizePixel(get_preferred_size()); + } + + virtual void dispose() override + { + m_xRecordCount.reset(); + m_xRecordOf.reset(); + m_xAbsolute.reset(); + m_xRecordText.reset(); + InterimItemWindow::dispose(); + } +}; + +tools::Long BrowseBox::GetBarHeight() const +{ + tools::Long nScrollBarSize = GetSettings().GetStyleSettings().GetScrollBarSize(); + if (!m_bNavigationBar) + return nScrollBarSize; + + // tdf#115941 because some platforms have things like overlay scrollbars, take a max + // of a statusbar height and a scrollbar height as the control area height + + // (we can't ask the scrollbars for their size cause if we're zoomed they still have to be + // resized - which is done in UpdateScrollbars) + return std::max(aStatusBarHeight->GetSizePixel().Height(), nScrollBarSize); +} + +BrowseBox::BrowseBox( vcl::Window* pParent, WinBits nBits, BrowserMode nMode ) + :Control( pParent, nBits | WB_3DLOOK ) + ,DragSourceHelper( this ) + ,DropTargetHelper( this ) + ,aHScroll( VclPtr<ScrollAdaptor>::Create(this, true) ) + // see NavigationBar ctor, here we just want to know its height + ,aStatusBarHeight(VclPtr<MeasureStatusBar>::Create(this)) + ,m_nCornerHeight(0) + ,m_nCornerWidth(0) + ,m_nActualCornerWidth(0) + ,m_bNavigationBar(false) +{ + ConstructImpl( nMode ); +} + +BrowseBox::~BrowseBox() +{ + disposeOnce(); +} + +void BrowseBox::DisposeAccessible() +{ + if (m_pImpl->m_pAccessible ) + { + disposeAndClearHeaderCell(m_pImpl->m_aColHeaderCellMap); + disposeAndClearHeaderCell(m_pImpl->m_aRowHeaderCellMap); + m_pImpl->m_pAccessible->dispose(); + m_pImpl->m_pAccessible = nullptr; + } +} + +void BrowseBox::dispose() +{ + SAL_INFO("svtools", "BrowseBox:dispose " << this ); + + DisposeAccessible(); + + Hide(); + pDataWin->pHeaderBar.disposeAndClear(); + pDataWin.disposeAndClear(); + pVScroll.disposeAndClear(); + aHScroll.disposeAndClear(); + aStatusBarHeight.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() +{ + if (!pDataWin) + return; + short nHiddenCount = --pDataWin->nCursorHidden; + if (PaintCursorIfHiddenOnce()) + { + if (1 == nHiddenCount) + DrawCursor(); + } + else + { + if (0 == nHiddenCount) + DrawCursor(); + } +} + + +void BrowseBox::DoHideCursor() +{ + 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(); +} + +tools::Long BrowseBox::GetDefaultColumnWidth( const OUString& _rText ) const +{ + return pDataWin->GetTextWidth( _rText ) + pDataWin->GetTextWidth(OUString('0')) * 4; +} + + +void BrowseBox::InsertHandleColumn( tools::Long 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, + tools::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(); + ToggleSelection(); + tools::Long nSelected = pColSel->FirstSelected(); + if (nSelected != static_cast<tools::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(); + } +} + +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() ) + { + tools::Long nScroll = -aFromRect.GetWidth(); + tools::Rectangle aScrollArea; + if ( nOldPos > nPos ) + { + tools::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, + Any( AccessibleTableModelChange( + COLUMNS_REMOVED, + -1, + -1, + nOldPos, + nOldPos + ) + ), + Any() + ); + + commitTableEvent( + TABLE_MODEL_CHANGED, + Any( AccessibleTableModelChange( + COLUMNS_INSERTED, + -1, + -1, + 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, + Any( rTitle ), + Any( sOld ) + ); + } +} + + +void BrowseBox::SetColumnWidth( sal_uInt16 nItemId, tools::Long 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; + + tools::Long nOldWidth = mvCols[ nItemPos ]->Width(); + + // adjust last column, if necessary + if ( IsVisible() && nItemPos == mvCols.size() - 1 ) + { + tools::Long nMaxWidth = pDataWin->GetSizePixel().Width(); + nMaxWidth -= pDataWin->bAutoSizeLastCol + ? GetFieldRect(nItemId).Left() + : GetFrozenWidth(); + if ( pDataWin->bAutoSizeLastCol || nWidth > 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( nOldWidth == nWidth ) + return; + + // do we want to display the change immediately? + bool bUpdate = GetUpdateMode() && + ( mvCols[ nItemPos ]->IsFrozen() || nItemPos >= nFirstCol ); + + if ( bUpdate ) + { + // Selection hidden + DoHideCursor(); + ToggleSelection(); + //!pDataWin->Update(); + //!Control::Update(); + } + + // set width + mvCols[ nItemPos ]->SetWidth(nWidth, GetZoom()); + + // scroll and invalidate + if ( bUpdate ) + { + // get X-Pos of the column changed + tools::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->GetOutDev()->SetClipRegion(); + bool bSelVis = bSelectionIsVisible; + bSelectionIsVisible = false; + if( GetBackground().IsScrollable() ) + { + + tools::Rectangle aScrRect( nX + std::min( 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, 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(); + } + 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, + Any( AccessibleTableModelChange(COLUMNS_REMOVED, + -1, + -1, + nPos, + nPos + ) + ), + Any() + ); + + commitHeaderBarEvent( + CHILD, + Any(), + Any( 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(), + Any(m_pImpl->getAccessibleHeaderBar(AccessibleBrowseBoxObjType::ColumnHeaderBar)) + ); + + // and now append it again + commitBrowseBoxEvent( + CHILD, + Any(m_pImpl->getAccessibleHeaderBar(AccessibleBrowseBoxObjType::ColumnHeaderBar)), + Any() + ); + + // notify a table model change + commitTableEvent( + TABLE_MODEL_CHANGED, + Any ( AccessibleTableModelChange( COLUMNS_REMOVED, + -1, + -1, + 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(); +} + +sal_Int32 BrowseBox::GetRowCount() const +{ + return nRowCount; +} + +sal_uInt16 BrowseBox::ColCount() const +{ + return static_cast<sal_uInt16>(mvCols.size()); +} + +tools::Long BrowseBox::ImpGetDataRowHeight() const +{ + BrowseBox *pThis = const_cast<BrowseBox*>(this); + pThis->m_nDataRowHeight = pThis->CalcReverseZoom(pDataWin->GetTextHeight() + 4); + pThis->Resize(); + pDataWin->Invalidate(); + return m_nDataRowHeight; +} + +void BrowseBox::SetDataRowHeight( tools::Long nPixel ) +{ + + m_nDataRowHeight = CalcReverseZoom(nPixel); + Resize(); + pDataWin->Invalidate(); +} + +void BrowseBox::SetTitleLines( sal_uInt16 nLines ) +{ + + nTitleLines = nLines; +} + +sal_Int32 BrowseBox::ScrollColumns( sal_Int32 nCols ) +{ + + if ( nFirstCol + nCols < 0 || + o3tl::make_unsigned(nFirstCol + nCols) >= 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 + { + tools::Long nDelta = mvCols[ nFirstCol-1 ]->Width(); + tools::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 + { + tools::Long nDelta = mvCols[ nFirstCol ]->Width(); + tools::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 ) + { + tools::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; +} + + +sal_Int32 BrowseBox::ScrollRows( sal_Int32 nRows ) +{ + // compute new top row + sal_Int32 nTmpMin = std::min( static_cast<sal_Int32>(nTopRow + nRows), static_cast<sal_Int32>(nRowCount - 1) ); + + sal_Int32 nNewTopRow = std::max<sal_Int32>( 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<tools::Long>(nTopRow + nRows), static_cast<tools::Long>(nRowCount - 1) ); + + nNewTopRow = std::max<tools::Long>( nTmpMin, 0 ); + + StartScroll(); + + // scroll area on screen and/or repaint + tools::Long nDeltaY = GetDataRowHeight() * ( nNewTopRow - nTopRow ); + sal_Int32 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( sal_Int32 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(); + sal_Int32 nOldRowCount = nRowCount; + nRowCount = 0; + if(bMultiSelection) + { + assert(uRow.pSel); + uRow.pSel->Reset(); + } + else + uRow.nSel = BROWSER_ENDOFSELECTION; + nCurRow = BROWSER_ENDOFSELECTION; + nTopRow = 0; + nCurColId = 0; + + // nFirstCol may not be reset, else the scrolling code will become confused. + // nFirstCol may only be changed when adding or deleting columns + // nFirstCol = 0; -> wrong! + aHScroll->SetThumbPos( 0 ); + pVScroll->SetThumbPos( 0 ); + + Invalidate(); + UpdateScrollbars(); + SetNoSelection(); + DoShowCursor(); + 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(), + Any( m_pImpl->getAccessibleHeaderBar( AccessibleBrowseBoxObjType::RowHeaderBar ) ) + ); + + // and now append it again + commitBrowseBoxEvent( + CHILD, + Any( m_pImpl->getAccessibleHeaderBar( AccessibleBrowseBoxObjType::RowHeaderBar ) ), + Any() + ); + + // notify a table model change + commitTableEvent( + TABLE_MODEL_CHANGED, + Any( AccessibleTableModelChange(ROWS_REMOVED, + 0, + nOldRowCount, + -1, + -1) + ), + Any() + ); +} + +void BrowseBox::RowInserted( sal_Int32 nRow, sal_Int32 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(); + + // must we paint the new rows? + sal_Int32 nOldCurRow = nCurRow; + Size aSz = pDataWin->GetOutputSizePixel(); + if ( bDoPaint && nRow >= nTopRow && + nRow <= nTopRow + aSz.Height() / GetDataRowHeight() ) + { + tools::Long nY = (nRow-nTopRow) * GetDataRowHeight(); + if ( !bLastRow ) + { + // scroll down the rows behind the new row + pDataWin->GetOutDev()->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(); + // notify accessible that rows were inserted + if ( isAccessibleAlive() ) + { + commitTableEvent( + TABLE_MODEL_CHANGED, + Any( AccessibleTableModelChange( + ROWS_INSERTED, + nRow, + nRow + nNumRows, + -1, + -1 + ) + ), + Any() + ); + + for (tools::Long i = nRow+1 ; i <= nRowCount ; ++i) + { + commitHeaderBarEvent( + CHILD, + Any( 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( sal_Int32 nRow, sal_Int32 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(); + } + + // adjust total row count + nRowCount -= nNumRows; + if (nRowCount < 0) nRowCount = 0; + sal_Int32 nOldCurRow = nCurRow; + + // adjust the selection + if ( bMultiSelection ) + // uRow.pSel->Remove( nRow, nNumRows ); + for ( tools::Long i = 0; i < nNumRows; i++ ) + uRow.pSel->Remove( nRow ); + 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) + { + tools::Long nY = (nRow-nTopRow) * GetDataRowHeight(); + pDataWin->GetOutDev()->SetClipRegion(); + if( pDataWin->GetBackground().IsScrollable() ) + { + pDataWin->Scroll( 0, - static_cast<short>(GetDataRowHeight()) * nNumRows, + tools::Rectangle( Point( 0, nY ), Size( aSz.Width(), + aSz.Height() - nY + nNumRows*GetDataRowHeight() ) ), + SCROLL_FLAGS ); + } + else + pDataWin->Window::Invalidate( InvalidateFlags::NoChildren ); + } + else + { + // Repaint the Rect of the deleted row + tools::Rectangle aRect( + Point( 0, (nRow-nTopRow)*GetDataRowHeight() ), + Size( pDataWin->GetSizePixel().Width(), + nNumRows * GetDataRowHeight() ) ); + pDataWin->Invalidate( aRect ); + } + } + } + // is the deleted row above of the visible area? + else if ( nRow < nTopRow ) + nTopRow = nTopRow >= nNumRows ? nTopRow-nNumRows : 0; + + if ( bDoPaint ) + { + // reshow cursor and selection + ToggleSelection(); + SAL_INFO("svtools", "BrowseBox::ShowCursor " << this ); + DoShowCursor(); + + // adjust the vertical scrollbar + UpdateScrollbars(); + AutoSizeLastColumn(); + } + + if ( isAccessibleAlive() ) + { + if ( nRowCount == 0 ) + { + // all columns should be removed, so we remove the column header bar and append it again + // to avoid to notify every column remove + commitBrowseBoxEvent( + CHILD, + Any(), + Any( m_pImpl->getAccessibleHeaderBar( AccessibleBrowseBoxObjType::RowHeaderBar ) ) + ); + + // and now append it again + commitBrowseBoxEvent( + CHILD, + Any(m_pImpl->getAccessibleHeaderBar(AccessibleBrowseBoxObjType::RowHeaderBar)), + Any() + ); + commitBrowseBoxEvent( + CHILD, + Any(), + Any( m_pImpl->getAccessibleTable() ) + ); + + // and now append it again + commitBrowseBoxEvent( + CHILD, + Any( m_pImpl->getAccessibleTable() ), + Any() + ); + } + else + { + commitTableEvent( + TABLE_MODEL_CHANGED, + Any( AccessibleTableModelChange( + ROWS_REMOVED, + nRow, + nRow + nNumRows, + -1, + -1 + ) + ), + Any() + ); + + for (tools::Long i = nRow+1 ; i <= (nRow+nNumRows) ; ++i) + { + commitHeaderBarEvent( + CHILD, + Any(), + Any( 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( sal_Int32 nRow) +{ + return GoToRow(nRow, false); +} + + +bool BrowseBox::GoToRow( sal_Int32 nRow, bool bRowColMove, bool bKeepSelection ) +{ + sal_Int32 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 ); + sal_Int32 nLastRow = nTopRow + nVisibleRows; + + // suspend Updates + pDataWin->EnterUpdateLock(); + + // remove old highlight, if necessary + if ( !bMultiSelection && !bKeepSelection ) + ToggleSelection(); + DoHideCursor(); + + // 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(); + 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(); + 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(); + 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( sal_Int32 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(); + bool bMoved = GoToRow(nRow, true) && GoToColumnId(nColId, true, true); + DoShowCursor(); + + 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(); + tools::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 ( sal_Int32 nRow = std::max<sal_Int32>( 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( sal_Int32 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(); + tools::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 +} + + +sal_Int32 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; +} + + +sal_Int32 BrowseBox::FirstSelectedColumn( ) const +{ + return pColSel ? pColSel->FirstSelected() : BROWSER_ENDOFSELECTION; +} + + +sal_Int32 BrowseBox::FirstSelectedRow() +{ + + return bMultiSelection ? uRow.pSel->FirstSelected() : uRow.nSel; +} + + +sal_Int32 BrowseBox::NextSelectedRow() +{ + + return bMultiSelection ? uRow.pSel->NextSelected() : BROWSER_ENDOFSELECTION; +} + + +sal_Int32 BrowseBox::LastSelectedRow() +{ + + return bMultiSelection ? uRow.pSel->LastSelected() : uRow.nSel; +} + + +bool BrowseBox::IsRowSelected( sal_Int32 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 +( + sal_Int32 nRow, // line number of the field (starting with 0) + sal_uInt16 nColId // column ID of the field +) + +/* [Description] + + Makes visible the field described in 'nRow' and 'nColId' by scrolling + accordingly. + +*/ + +{ + if (!pDataWin) + return; + + Size aTestSize = pDataWin->GetSizePixel(); + + if ( !bBootstrapped || aTestSize.IsEmpty() ) + return; + + // is it visible already? + bool bVisible = IsFieldVisible( nRow, nColId, true/*bComplete*/ ); + if ( bVisible ) + return; + + // calculate column position, field rectangle and painting area + sal_uInt16 nColPos = GetColumnPos( nColId ); + tools::Rectangle aFieldRect = GetFieldRectPixel( nRow, nColId, false ); + tools::Rectangle aDataRect( Point(0, 0), pDataWin->GetSizePixel() ); + + // positioned outside on the left? + if ( nColPos >= FrozenColCount() && nColPos < nFirstCol ) + // => scroll to the right + ScrollColumns( nColPos - nFirstCol ); + + // while outside on the right + while ( aDataRect.Right() < aFieldRect.Right() ) + { + // => scroll to the left + if ( ScrollColumns( 1 ) != 1 ) + // no more need to scroll + break; + aFieldRect = GetFieldRectPixel( nRow, nColId, false ); + } + + // positioned outside above? + if ( nRow < nTopRow ) + // scroll further to the bottom + ScrollRows( nRow - nTopRow ); + + // positioned outside below? + sal_Int32 nBottomRow = nTopRow + GetVisibleRows(); + // decrement nBottomRow to make it the number of the last visible line + // (count starts with 0!). + // Example: BrowseBox contains exactly one entry. nBottomRow := 0 + 1 - 1 + if( nBottomRow ) + nBottomRow--; + + if ( nRow > nBottomRow ) + // scroll further to the top + ScrollRows( nRow - nBottomRow ); +} + + +bool BrowseBox::IsFieldVisible( sal_Int32 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.Contains( aRect ); + else + // test if the field is partly of completely visible + return !aOutRect.Intersection( aRect ).IsEmpty(); +} + + +tools::Rectangle BrowseBox::GetFieldRectPixel( sal_Int32 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( sal_Int32 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.Top() > 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( sal_Int32 nRow, sal_uInt16 nColumnId ) const +{ + + // compute the X-coordinate relative to DataWin by accumulation + tools::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 + tools::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 ) ); +} + + +sal_Int32 BrowseBox::GetRowAtYPosPixel( tools::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( tools::Long nX ) const +{ + + // accumulate the widths of the visible columns + tools::Long nColX = 0; + for ( size_t nCol = 0; nCol < mvCols.size(); ++nCol ) + { + BrowserColumn *pCol = mvCols[ nCol ].get(); + if ( pCol->IsFrozen() || nCol >= nFirstCol ) + nColX += pCol->Width(); + + if ( nColX > nX ) + return nCol; + } + + return BROWSER_INVALIDID; +} + +bool BrowseBox::ReserveControlArea(sal_uInt16 nWidth) +{ + if (nWidth != nControlAreaWidth) + { + OSL_ENSURE(nWidth,"Control area of 0 is not allowed, Use USHRT_MAX instead!"); + nControlAreaWidth = nWidth; + UpdateScrollbars(); + return true; + } + return false; +} + +tools::Rectangle BrowseBox::GetControlArea() const +{ + auto nHeight = aHScroll->GetSizePixel().Height(); + auto nEndRight = aHScroll->GetPosPixel().X(); + + return tools::Rectangle( + Point( 0, GetOutputSizePixel().Height() - nHeight ), + Size( nEndRight, 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; + + tools::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; + + pVScroll = VclPtr<ScrollAdaptor>::Create(this, false); + pVScroll->SetLineSize( 1 ); + pVScroll->SetPageSize(1); + pVScroll->SetScrollHdl( LINK( this, BrowseBox, VertScrollHdl ) ); + + 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( sal_Int32, 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( sal_Int32, 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; +} + + +tools::Long BrowseBox::GetDataRowHeight() const +{ + return CalcZoom(m_nDataRowHeight ? m_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 ) ); +} + +tools::Long BrowseBox::GetTitleHeight() const +{ + tools::Long nHeight; + // ask the header bar for the text height (if possible), as the header bar's font is adjusted with + // our (and the header's) zoom factor + HeaderBar* pHeaderBar = pDataWin->pHeaderBar; + if ( pHeaderBar ) + nHeight = pHeaderBar->GetTextHeight(); + else + nHeight = GetTextHeight(); + + return nTitleLines ? nTitleLines * nHeight + 4 : 0; +} + +tools::Long BrowseBox::CalcReverseZoom(tools::Long nVal) const +{ + 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<tools::Long>(n + 0.5) : -static_cast<tools::Long>(-n + 0.5); + } + + return nVal; +} + +void BrowseBox::CursorMoved() +{ + // before implementing more here, please adjust the EditBrowseBox + + if ( isAccessibleAlive() && HasFocus() ) + commitTableEvent( + ACTIVE_DESCENDANT_CHANGED, + Any( CreateAccessibleCell( GetCurRow(),GetColumnPos( GetCurColumnId() ) ) ), + Any() + ); +} + +void BrowseBox::LoseFocus() +{ + SAL_INFO("svtools", "BrowseBox::LoseFocus " << this ); + + if ( bHasFocus ) + { + SAL_INFO("svtools", "BrowseBox::HideCursor " << this ); + DoHideCursor(); + + 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(); + } + Control::GetFocus(); +} + + +sal_uInt16 BrowseBox::GetVisibleRows() const +{ + return static_cast<sal_uInt16>((pDataWin->GetOutputSizePixel().Height() - 1 )/ GetDataRowHeight() + 1); +} + +BrowserDataWin& 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 0000000000..08bfe6c7f4 --- /dev/null +++ b/svtools/source/brwbox/brwbox2.cxx @@ -0,0 +1,2012 @@ +/* -*- 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 <svtools/brwhead.hxx> +#include <svtools/colorcfg.hxx> +#include <svtools/scrolladaptor.hxx> +#include "datwin.hxx" +#include <vcl/commandevent.hxx> +#include <vcl/help.hxx> +#include <vcl/ptrstyle.hxx> +#include <vcl/settings.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 ) const +{ + 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(pDataWin->pHeaderBar); + + 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 & ) +{ +} + + +tools::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(); +} + + +void BrowseBox::EndScroll() +{ + UpdateScrollbars(); + AutoSizeLastColumn(); + DoShowCursor(); +} + + +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; + sal_Int32 nLastRowInRect = 0; // for the CFront + + // don't highlight handle column + BrowserColumn *pFirstCol = mvCols.empty() ? nullptr : mvCols[ 0 ].get(); + tools::Long nOfsX = (!pFirstCol || pFirstCol->GetId()) ? 0 : pFirstCol->Width(); + + // accumulate old row selection + sal_Int32 nBottomRow = nTopRow + + pDataWin->GetOutputSizePixel().Height() / GetDataRowHeight(); + if ( nBottomRow > GetRowCount() && GetRowCount() ) + nBottomRow = GetRowCount(); + for ( sal_Int32 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 ( tools::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->GetOutDev()->GetFillColor() : m_aCursorColor; + Color aOldFillColor = pDataWin->GetOutDev()->GetFillColor(); + Color aOldLineColor = pDataWin->GetOutDev()->GetLineColor(); + pDataWin->GetOutDev()->SetFillColor(); + pDataWin->GetOutDev()->SetLineColor( rCol ); + pDataWin->GetOutDev()->DrawRect( aCursor ); + pDataWin->GetOutDev()->SetLineColor( aOldLineColor ); + pDataWin->GetOutDev()->SetFillColor( aOldFillColor ); + } +} + + +tools::Long 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(); + + // expand the last selection + if ( bMultiSelection ) + { + Range aJustifiedRange( aSelRange ); + aJustifiedRange.Normalize(); + + bool bSelectThis = ( bSelect != aJustifiedRange.Contains( rEvt.GetRow() ) ); + + if ( aJustifiedRange.Contains( 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(); +} + + +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 nSBHeight = GetBarHeight(); + sal_uLong nSBWidth = GetSettings().GetStyleSettings().GetScrollBarSize(); + if (IsZoom()) + { + nSBHeight = static_cast<sal_uLong>(nSBHeight * static_cast<double>(GetZoom())); + nSBWidth = static_cast<sal_uLong>(nSBWidth * static_cast<double>(GetZoom())); + } + + DoHideCursor(); + 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 + tools::Long nDataHeight = GetOutputSizePixel().Height() - GetTitleHeight(); + if ( aHScroll->IsVisible() || ( nControlAreaWidth != USHRT_MAX ) ) + nDataHeight -= nSBHeight; + + tools::Long nDataWidth = GetOutputSizePixel().Width(); + if ( pVScroll->IsVisible() ) + nDataWidth -= nSBWidth; + + // 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(); + tools::Long nOfsX = pFirstCol->GetId() ? 0 : pFirstCol->Width(); + pHeaderBar->SetPosSizePixel( Point( nOfsX, 0 ), Size( GetOutputSizePixel().Width() - nOfsX, GetTitleHeight() ) ); + } + + AutoSizeLastColumn(); // adjust last column width + DoShowCursor(); +} + + +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); + + // 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 + tools::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(vcl::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(vcl::PushFlags::FILLCOLOR | vcl::PushFlags::LINECOLOR); + rRenderContext.SetFillColor(aColFace); + rRenderContext.SetLineColor(aColFace); + rRenderContext.DrawRect(tools::Rectangle(Point(nX, 0), + Point(rRect.Right(), GetTitleHeight() - 2 ))); + rRenderContext.Pop(); + } + + if (m_nActualCornerWidth) + { + const StyleSettings &rSettings = rRenderContext.GetSettings().GetStyleSettings(); + Color aColFace(rSettings.GetFaceColor()); + rRenderContext.Push(vcl::PushFlags::FILLCOLOR | vcl::PushFlags::LINECOLOR); + rRenderContext.SetFillColor(aColFace); + rRenderContext.SetLineColor(aColFace); + rRenderContext.DrawRect(tools::Rectangle(Point(GetOutputSizePixel().Width() - m_nActualCornerWidth, aHScroll->GetPosPixel().Y()), + Size(m_nActualCornerWidth, m_nCornerHeight))); + rRenderContext.Pop(); + } +} + +void BrowseBox::Draw( OutputDevice* pDev, const Point& rPos, SystemTextColorFlags 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 ); + if (nFlags & SystemTextColorFlags::Mono) + pDev->SetTextColor(COL_BLACK); + else + pDev->SetTextColor(pDataWin->GetTextColor()); + + // 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 (!m_nDataRowHeight) + ImpGetDataRowHeight(); + tools::Long nHeightLogic = PixelToLogic(Size(0, m_nDataRowHeight), MapMode(MapUnit::Map10thMM)).Height(); + tools::Long nForeignHeightPixel = pDev->LogicToPixel(Size(0, nHeightLogic), MapMode(MapUnit::Map10thMM)).Height(); + + tools::Long nOriginalHeight = m_nDataRowHeight; + m_nDataRowHeight = nForeignHeightPixel; + + // this counts for the column widths, too + size_t nPos; + for ( nPos = 0; nPos < mvCols.size(); ++nPos ) + { + BrowserColumn* pCurrent = mvCols[ nPos ].get(); + + tools::Long nWidthLogic = PixelToLogic(Size(pCurrent->Width(), 0), MapMode(MapUnit::Map10thMM)).Width(); + tools::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 + tools::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( vcl::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 + m_nDataRowHeight = nOriginalHeight; + for ( nPos = 0; nPos < mvCols.size(); ++nPos ) + { + BrowserColumn* pCurrent = mvCols[ nPos ].get(); + + tools::Long nForeignWidthLogic = pDev->PixelToLogic(Size(pCurrent->Width(), 0), MapMode(MapUnit::Map10thMM)).Width(); + tools::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() ); + + tools::Long nDataRowHeight = GetDataRowHeight(); + + // compute relative rows to redraw + sal_uLong nRelTopRow = 0; + sal_uLong nRelBottomRow = aOverallAreaSize.Height(); + if (!_bForeignDevice && nDataRowHeight) + { + nRelTopRow = (static_cast<sal_uLong>(_rRect.Top()) / nDataRowHeight); + nRelBottomRow = static_cast<sal_uLong>(_rRect.Bottom()) / nDataRowHeight; + } + + // cache frequently used values + Point aPos( aOverallAreaPos.X(), nRelTopRow * nDataRowHeight + 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(); + tools::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(nDataRowHeight ) ) + { + // 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+tools::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.Left(), aPos.Y() ), + Size( _rRect.GetSize().Width(), nDataRowHeight ) ); + + 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(), nDataRowHeight ) ); + _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(), nDataRowHeight); + + 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( vcl::PushFlags::LINECOLOR ); + _rOut.SetLineColor( aDelimiterLineColor ); + tools::Long nY = aPos.Y() + nDataRowHeight - 1; + if (nY <= aOverallAreaBRPos.Y()) + _rOut.DrawLine( Point( nHLineX, nY ), + Point( bVLines + ? std::min(tools::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 ); + tools::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() ); + tools::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); +} + +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) + m_nCornerHeight = GetBarHeight(); + m_nCornerWidth = GetSettings().GetStyleSettings().GetScrollBarSize(); + if (IsZoom()) + { + m_nCornerHeight = static_cast<sal_uLong>(m_nCornerHeight * static_cast<double>(GetZoom())); + m_nCornerWidth = static_cast<sal_uLong>(m_nCornerWidth * static_cast<double>(GetZoom())); + } + + bool bNeedsVScroll = false; + sal_Int32 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() - m_nCornerWidth ); + 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(m_nCornerHeight) ); + } + else if ( !aHScroll->IsVisible() ) + { + Size aNewSize( aDataWinSize ); + aNewSize.setHeight( GetOutputSizePixel().Height() - GetTitleHeight() - m_nCornerHeight ); + aDataWinSize = aNewSize; + } + + // adjust position and Width of horizontal scrollbar + sal_uLong nHScrX = nControlAreaWidth == USHRT_MAX + ? 0 + : nControlAreaWidth; + + aHScroll->SetPosSizePixel( + Point( nHScrX, GetOutputSizePixel().Height() - m_nCornerHeight ), + Size( aDataWinSize.Width() - nHScrX, m_nCornerHeight ) ); + + // 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 ); + + if (nVisibleHSize) + { + short nRange = std::max( nScrollCols, short(0) ); + aHScroll->SetVisibleSize( nVisibleHSize ); + aHScroll->SetRange( Range( 0, nRange )); + } + else + { + // ensure scrollbar is shown as fully filled + aHScroll->SetVisibleSize(1); + aHScroll->SetRange(Range(0, 1)); + } + 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 ); + tools::Long nVisibleSize = std::min( std::min( nRowCount, nMaxRows ), (nRowCount-nTopRow) ); + pVScroll->SetVisibleSize( nVisibleSize ? nVisibleSize : 1 ); + pVScroll->SetRange( Range( 0, nRowCount ) ); + pVScroll->SetPosSizePixel( + Point( aDataWinSize.Width(), GetTitleHeight() ), + Size( m_nCornerWidth, aDataWinSize.Height()) ); + tools::Long nLclDataRowHeight = GetDataRowHeight(); + if ( nLclDataRowHeight > 0 && nRowCount < tools::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) + m_nActualCornerWidth = 0; + if (aHScroll->IsVisible() && pVScroll && pVScroll->IsVisible() ) + { + // if we have both scrollbars, the corner window fills the point of intersection of these two + m_nActualCornerWidth = m_nCornerWidth; + } + 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 + m_nActualCornerWidth = GetOutputSizePixel().Width() - nControlAreaWidth; + } + + // scroll headerbar, if necessary + if ( pDataWin->pHeaderBar ) + { + tools::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(); + } + else + DoHideCursor(); +} + + +bool BrowseBox::GetUpdateMode() const +{ + + return pDataWin->IsUpdateMode(); +} + + +tools::Long BrowseBox::GetFrozenWidth() const +{ + + tools::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, VertScrollHdl, weld::Scrollbar&, rScrollbar, void) +{ + auto nCurScrollRow = nTopRow; + auto nPos = rScrollbar.adjustment_get_value(); + ScrollRows(nPos - nCurScrollRow); + + bool bShowTooltip = ((m_nCurrentMode & BrowserMode::TRACKING_TIPS) == BrowserMode::TRACKING_TIPS); + if (bShowTooltip && + rScrollbar.get_scroll_type() == ScrollType::Drag && + Help::IsQuickHelpEnabled()) + { + OUString aTip = OUString::number(nPos) + "/"; + if (!pDataWin->GetRealRowCount().isEmpty()) + aTip += pDataWin->GetRealRowCount(); + else + aTip += OUString::number(rScrollbar.adjustment_get_upper()); + tools::Rectangle aRect(GetPointerPosPixel(), Size(GetTextWidth(aTip), GetTextHeight())); + Help::ShowQuickHelp(this, aRect, aTip); + } +} + +IMPL_LINK(BrowseBox, HorzScrollHdl, weld::Scrollbar&, rScrollbar, void) +{ + auto nCurScrollCol = nFirstCol - FrozenColCount(); + ScrollColumns(rScrollbar.adjustment_get_value() - nCurScrollCol); +} + +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; + + tools::Long nX = 0; + tools::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 + tools::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->GetOutDev()->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() ) < 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<tools::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 ); + tools::Long nDeltaX = nDragX - nResizeX; + sal_uInt16 nId = GetColumnId(nResizeCol); + tools::Long 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) != mvCols[ nResizeCol ]->Width() ) + { + // resize column + tools::Long nMaxX = pDataWin->GetSizePixel().Width(); + nDragX = std::min( nDragX, nMaxX ); + tools::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(); + + // 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(); + 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(); + 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::ChildFocusIn() +{ +} + +void BrowseBox::ChildFocusOut() +{ +} + +void BrowseBox::Dispatch( sal_uInt16 nId ) +{ + + tools::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(); + for ( sal_Int32 nRow = GetCurRow(); nRow >= 0; --nRow ) + SelectRow( nRow ); + GoToRow( 0, true ); + DoShowCursor(); + } + break; + case BROWSER_SELECTEND: + if ( GetRowCount() ) + { + DoHideCursor(); + sal_Int32 nRows = GetRowCount(); + for ( sal_Int32 nRow = GetCurRow(); nRow < nRows; ++nRow ) + SelectRow( nRow ); + GoToRow( GetRowCount() - 1, true ); + DoShowCursor(); + } + 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 + sal_Int32 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 + sal_Int32 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(); + if (!m_bFocusOnlyCursor) + DoHideCursor(); + + m_aCursorColor = _rCol; + + if (!m_bFocusOnlyCursor) + DoShowCursor(); + DoShowCursor(); +} + +tools::Rectangle BrowseBox::calcHeaderRect(bool _bIsColumnBar, bool _bOnScreen) +{ + vcl::Window* pParent = nullptr; + if ( !_bOnScreen ) + pParent = GetAccessibleParentWindow(); + + Point aTopLeft; + tools::Long nWidth; + tools::Long nHeight; + if ( _bIsColumnBar ) + { + nWidth = pDataWin->GetOutputSizePixel().Width(); + nHeight = GetDataRowHeight(); + } + else + { + aTopLeft.setY( GetDataRowHeight() ); + nWidth = GetColumnWidth(0); + if (pParent) + nHeight = GetWindowExtentsRelative( *pParent ).GetHeight() - aTopLeft.Y() - GetControlArea().GetSize().Height(); + else + nHeight = GetWindowExtentsAbsolute().GetHeight() - aTopLeft.Y() - GetControlArea().GetSize().Height(); + } + if (pParent) + aTopLeft += GetWindowExtentsRelative( *pParent ).TopLeft(); + else + aTopLeft += Point(GetWindowExtentsAbsolute().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; + if (pParent) + aRect = GetWindowExtentsRelative( *pParent ); + else + aRect = tools::Rectangle(GetWindowExtentsAbsolute()); + tools::Rectangle aRowBar = calcHeaderRect(false, pParent == nullptr); + + tools::Long nX = aRowBar.Right() - aRect.Left(); + tools::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::GetFieldRectPixel( 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(); + if (pParent) + aTopLeft += GetWindowExtentsRelative( *pParent ).TopLeft(); + else + aTopLeft += Point(GetWindowExtentsAbsolute().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 0000000000..32ab1807ac --- /dev/null +++ b/svtools/source/brwbox/brwbox3.cxx @@ -0,0 +1,560 @@ +/* -*- 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 <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; + + static Reference< XAccessible > getHeaderCell( BrowseBoxImpl::THeaderCellMap& _raHeaderCells, + sal_Int32 _nPos, + 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( 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 + ); + } + } + + return m_pImpl->m_pAccessible; +} + + +// 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, + AccessibleBrowseBoxObjType::RowHeaderCell, + m_pImpl->getAccessibleHeaderBar(AccessibleBrowseBoxObjType::RowHeaderBar), + *this, + m_pImpl->m_aFactoryAccess.getFactory() + ); +} + + +Reference< XAccessible > BrowseBox::CreateAccessibleColumnHeader( sal_uInt16 _nColumnPos ) +{ + return svt::getHeaderCell( + m_pImpl->m_aColHeaderCellMap, + _nColumnPos, + AccessibleBrowseBoxObjType::ColumnHeaderCell, + m_pImpl->getAccessibleHeaderBar(AccessibleBrowseBoxObjType::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( AccessibleBrowseBoxObjType eObjType,sal_Int32 _nPosition) const +{ + OUString aRetText; + switch( eObjType ) + { + case AccessibleBrowseBoxObjType::BrowseBox: + aRetText = "BrowseBox"; + break; + case AccessibleBrowseBoxObjType::Table: + aRetText = "Table"; + break; + case AccessibleBrowseBoxObjType::RowHeaderBar: + aRetText = "RowHeaderBar"; + break; + case AccessibleBrowseBoxObjType::ColumnHeaderBar: + aRetText = "ColumnHeaderBar"; + break; + case AccessibleBrowseBoxObjType::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(GetCurRow()) + + "," + + OUString::number(sal_Int32(GetCurColumnId())) + + "]"; +#endif + break; + case AccessibleBrowseBoxObjType::RowHeaderCell: + { + sal_Int32 rowId = _nPosition + 1; + aRetText = OUString::number( rowId ); + } +#if OSL_DEBUG_LEVEL > 0 + aRetText += " [" + + OUString::number(GetCurRow()) + + "," + + OUString::number(sal_Int32(GetCurColumnId())) + + "]"; +#endif + break; + case AccessibleBrowseBoxObjType::ColumnHeaderCell: + aRetText = GetColumnDescription( sal_Int16( _nPosition ) ); +#if OSL_DEBUG_LEVEL > 0 + aRetText += " [" + + OUString::number(GetCurRow()) + + "," + + OUString::number(sal_Int32(GetCurColumnId())) + + "]"; +#endif + break; + default: + OSL_FAIL("BrowseBox::GetAccessibleName: invalid enum!"); + } + return aRetText; +} + + +OUString BrowseBox::GetAccessibleObjectDescription( AccessibleBrowseBoxObjType eObjType,sal_Int32 ) const +{ + OUString aRetText; + switch( eObjType ) + { + case AccessibleBrowseBoxObjType::BrowseBox: + aRetText = "BrowseBox description"; + break; + case AccessibleBrowseBoxObjType::Table: + // aRetText = "TABLE description"; + break; + case AccessibleBrowseBoxObjType::RowHeaderBar: + // aRetText = "ROWHEADERBAR description"; + break; + case AccessibleBrowseBoxObjType::ColumnHeaderBar: + // aRetText = "COLUMNHEADERBAR description"; + break; + case AccessibleBrowseBoxObjType::TableCell: + // aRetText = "TABLECELL description"; + break; + case AccessibleBrowseBoxObjType::RowHeaderCell: + // aRetText = "ROWHEADERCELL description"; + break; + case AccessibleBrowseBoxObjType::ColumnHeaderCell: + // aRetText = "COLUMNHEADERCELL description"; + break; + case AccessibleBrowseBoxObjType::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( + sal_Int64& rStateSet, + AccessibleBrowseBoxObjType eObjType ) const +{ + switch( eObjType ) + { + case AccessibleBrowseBoxObjType::BrowseBox: + case AccessibleBrowseBoxObjType::Table: + + rStateSet |= AccessibleStateType::FOCUSABLE; + if ( HasFocus() ) + rStateSet |= AccessibleStateType::FOCUSED; + if ( IsActive() ) + rStateSet |= AccessibleStateType::ACTIVE; + if ( GetUpdateMode() ) + rStateSet |= AccessibleStateType::EDITABLE; + if ( IsEnabled() ) + { + rStateSet |= AccessibleStateType::ENABLED; + rStateSet |= AccessibleStateType::SENSITIVE; + } + if ( IsReallyVisible() ) + rStateSet |= AccessibleStateType::VISIBLE; + if ( eObjType == AccessibleBrowseBoxObjType::Table ) + rStateSet |= AccessibleStateType::MANAGES_DESCENDANTS; + + break; + case AccessibleBrowseBoxObjType::RowHeaderBar: + rStateSet |= AccessibleStateType::FOCUSABLE; + rStateSet |= AccessibleStateType::VISIBLE; + if ( GetSelectRowCount() ) + rStateSet |= AccessibleStateType::FOCUSED; + rStateSet |= AccessibleStateType::MANAGES_DESCENDANTS; + break; + case AccessibleBrowseBoxObjType::ColumnHeaderBar: + rStateSet |= AccessibleStateType::FOCUSABLE; + rStateSet |= AccessibleStateType::VISIBLE; + if ( GetSelectColumnCount() ) + rStateSet |= AccessibleStateType::FOCUSED; + rStateSet |= AccessibleStateType::MANAGES_DESCENDANTS; + break; + case AccessibleBrowseBoxObjType::TableCell: + { + sal_Int32 nRow = GetCurRow(); + sal_uInt16 nColumn = GetCurColumnId(); + if ( IsFieldVisible(nRow,nColumn) ) + rStateSet |= AccessibleStateType::VISIBLE; + if ( !IsFrozen( nColumn ) ) + rStateSet |= AccessibleStateType::FOCUSABLE; + rStateSet |= AccessibleStateType::TRANSIENT; + } + break; + case AccessibleBrowseBoxObjType::RowHeaderCell: + case AccessibleBrowseBoxObjType::ColumnHeaderCell: + case AccessibleBrowseBoxObjType::CheckBoxCell: + OSL_FAIL("Illegal call here!"); + break; + } +} + +void BrowseBox::FillAccessibleStateSetForCell( sal_Int64& _rStateSet, + sal_Int32 _nRow, sal_uInt16 _nColumnPos ) const +{ + //! TODO check if the state is valid for table cells + if ( IsCellVisible( _nRow, _nColumnPos ) ) + _rStateSet |= AccessibleStateType::VISIBLE; + if ( GetCurrRow() == _nRow && GetCurrColumn() == _nColumnPos ) + _rStateSet |= AccessibleStateType::FOCUSED; + else // only transient when column is not focused + _rStateSet |= AccessibleStateType::TRANSIENT; +} + + +void BrowseBox::GrabTableFocus() +{ + GrabFocus(); +} + +OUString BrowseBox::GetCellText(sal_Int32, 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 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( sal_Int32 _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 ); + auto pRows = _rRows.getArray(); + pRows[ 0 ] = const_cast< BrowseBox* >( this )->FirstSelectedRow(); + for( sal_Int32 nIndex = 1; nIndex < nCount; ++nIndex ) + pRows[ 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 ); + auto pColumns = _rColumns.getArray(); + + 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" ); + pColumns[ nIndex ] = nCol; + ++nIndex; + } + } +} + +bool BrowseBox::IsCellVisible( sal_Int32 _nRow, sal_uInt16 _nColumnPos ) const +{ + return IsFieldVisible( _nRow, GetColumnId( _nColumnPos ) ); +} + +OUString BrowseBox::GetAccessibleCellText(sal_Int32 _nRow, sal_uInt16 _nColPos) const +{ + return GetCellText( _nRow, GetColumnId( _nColPos ) ); +} + + +bool BrowseBox::GetGlyphBoundRects( const Point& rOrigin, const OUString& rStr, int nIndex, int nLen, std::vector< tools::Rectangle >& rVector ) +{ + return GetOutDev()->GetGlyphBoundRects( rOrigin, rStr, nIndex, nLen, rVector ); +} + +AbsoluteScreenPixelRectangle BrowseBox::GetWindowExtentsAbsolute() const +{ + return Control::GetWindowExtentsAbsolute(); +} + +tools::Rectangle BrowseBox::GetWindowExtentsRelative(const vcl::Window& rRelativeWindow) const +{ + return Control::GetWindowExtentsRelative( rRelativeWindow ); +} + +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 0000000000..86ed55d0df --- /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 ) +{ + tools::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 0000000000..8c7017675a --- /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 <comphelper/diagnose_ex.hxx> + +#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; + rtl::Reference<vcl::IAccessibleBrowseBox> m_pAccessible; + THeaderCellMap m_aColHeaderCellMap; + THeaderCellMap m_aRowHeaderCellMap; + + public: + BrowseBoxImpl() + { + } + + + /// @see AccessibleBrowseBox::getHeaderBar + css::uno::Reference< css::accessibility::XAccessible > + getAccessibleHeaderBar( 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 0000000000..c818bb9d82 --- /dev/null +++ b/svtools/source/brwbox/datwin.cxx @@ -0,0 +1,652 @@ +/* -*- 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 <svtools/brwhead.hxx> +#include <svtools/scrolladaptor.hxx> +#include <utility> +#include <vcl/commandevent.hxx> +#include <vcl/help.hxx> +#include <vcl/settings.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, + OUString aTitle, tools::Long nWidthPixel, const Fraction& rCurrentZoom ) +: _nId( nItemId ), + _nWidth( nWidthPixel ), + _aTitle(std::move( aTitle )), + _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<tools::Long>(n+0.5) : -static_cast<tools::Long>(-n+0.5); +} + +BrowserColumn::~BrowserColumn() +{ +} + +void BrowserColumn::SetWidth(tools::Long 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<tools::Long>(n+0.5) : -static_cast<tools::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 + tools::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<tools::Long>(n+0.5) : -static_cast<tools::Long>(-n+0.5); +} + + +BrowserDataWin::BrowserDataWin( BrowseBox* pParent ) + :Control( pParent, WB_CLIPCHILDREN ) + ,DragSourceHelper( this ) + ,DropTargetHelper( this ) + ,pHeaderBar( nullptr ) + ,bInDtor( false ) + ,aMouseTimer("BrowserDataWin aMouseTimer") + ,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(); + 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->GetOutDev(), rStyleSettings.GetFieldFont()); + pWin->ApplyControlForeground(*pWin->GetOutDev(), rStyleSettings.GetWindowTextColor()); + pWin->ApplyControlBackground(*pWin->GetOutDev(), 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); + } +} + +BrowseBox* BrowserDataWin::GetParent() const +{ + return static_cast<BrowseBox*>(Window::GetParent()); +} + +BrowseEvent BrowserDataWin::CreateBrowseEvent( const Point& rPosPixel ) +{ + BrowseBox *pBox = GetParent(); + + // seek to row under mouse + sal_Int32 nRelRow = rPosPixel.Y() < 0 + ? -1 + : rPosPixel.Y() / pBox->GetDataRowHeight(); + sal_Int32 nRow = nRelRow < 0 ? -1 : nRelRow + pBox->nTopRow; + + // find column under mouse + tools::Long nMouseX = rPosPixel.X(); + tools::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() ); + sal_Int32 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 ) const +{ + if ( ! ( GetParent()->IsInteractiveRowHeightEnabled() + && ( _rEvent.GetRow() >= 0 ) + && ( _rEvent.GetRow() < GetParent()->GetRowCount() ) + && ( _rEvent.GetColumnId() == BrowseBox::HandleColumnId ) + ) + ) + return false; + + tools::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 ) +{ + tools::Long nDataRowHeight = GetParent()->GetDataRowHeight(); + // the exact separation pos of the two rows + tools::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() ) + { + tools::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 + { + tools::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, + sal_Int32 nAbsRow, sal_uInt16 nColumn, sal_uInt16 nColumnId, + const tools::Rectangle& rRect ): + pWin(pWindow), + aRect(rRect), + nRow(nAbsRow), + 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, + sal_Int32 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) + { + vcl::Region aRegion(rRect); + Control::ImplInvalidate( &aRegion, InvalidateFlags::NONE ); + } + aInvalidRegion.clear(); +} + +void BrowserDataWin::ImplInvalidate( const vcl::Region* pRegion, InvalidateFlags nFlags ) +{ + if ( !GetUpdateMode() ) + { + aInvalidRegion.clear(); + if (!pRegion) + aInvalidRegion.emplace_back( Point( 0, 0 ), GetOutputSizePixel() ); + else + aInvalidRegion.emplace_back( pRegion->GetBoundRect() ); + } + else + Window::ImplInvalidate( pRegion, nFlags ); +} + +/* 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 0000000000..29e0ac301b --- /dev/null +++ b/svtools/source/brwbox/datwin.hxx @@ -0,0 +1,83 @@ +/* -*- 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 <tools/long.hxx> +#include <utility> + +#include <limits> + +#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, + OUString _aText, + bool _bDrawDisabled) + :aRect( rPt, rSz ) + ,aInnerRect( Point( aRect.Left()+1, aRect.Top()+1 ), + Size( aRect.GetWidth()-2, aRect.GetHeight()-2 ) ) + ,aText(std::move(_aText)) + ,m_bDrawDisabled(_bDrawDisabled) + { + } + + void Draw( OutputDevice& rDev ); +}; + + +class BrowserColumn final +{ + sal_uInt16 _nId; + tools::Long _nOriginalWidth; + tools::Long _nWidth; + OUString _aTitle; + bool _bFrozen; + +public: + BrowserColumn( sal_uInt16 nItemId, + OUString aTitle, tools::Long nWidthPixel, const Fraction& rCurrentZoom ); + ~BrowserColumn(); + + sal_uInt16 GetId() const { return _nId; } + + tools::Long Width() const { 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(tools::Long nNewWidthPixel, const Fraction& rCurrentZoom); + void ZoomChanged(const Fraction& rNewZoom); +}; + +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 0000000000..45b1a3766e --- /dev/null +++ b/svtools/source/brwbox/ebbcontrols.cxx @@ -0,0 +1,768 @@ +/* + * 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> + +namespace svt +{ + //= ComboBoxControl + ComboBoxControl::ComboBoxControl(BrowserDataWin* pParent) + : ControlBase(pParent, "svt/ui/combocontrol.ui", "ComboControl") + , m_xWidget(m_xBuilder->weld_combo_box("combobox")) + { + InitControlBase(m_xWidget.get()); + m_xWidget->set_entry_width_chars(1); // so a smaller than default width can be used + m_xWidget->connect_changed(LINK(this, ComboBoxControl, SelectHdl)); + m_xWidget->connect_key_press(LINK(this, ControlBase, KeyInputHdl)); + m_xWidget->connect_key_release(LINK(this, ControlBase, KeyReleaseHdl)); + m_xWidget->connect_focus_in(LINK(this, ControlBase, FocusInHdl)); + m_xWidget->connect_focus_out(LINK(this, ControlBase, FocusOutHdl)); + m_xWidget->connect_mouse_press(LINK(this, ControlBase, MousePressHdl)); + m_xWidget->connect_mouse_release(LINK(this, ControlBase, MouseReleaseHdl)); + m_xWidget->connect_mouse_move(LINK(this, ControlBase, MouseMoveHdl)); + } + + void ComboBoxControl::SetPointFont(const vcl::Font& rFont) + { + m_xWidget->set_entry_font(rFont); + } + + 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::IsValueChangedFromSaved() const + { + return GetComboBox().get_value_changed_from_saved(); + } + + void ComboBoxCellController::SaveValue() + { + GetComboBox().save_value(); + } + + //= ListBoxControl + ListBoxControl::ListBoxControl(BrowserDataWin* pParent) + : ControlBase(pParent, "svt/ui/listcontrol.ui", "ListControl") + , m_xWidget(m_xBuilder->weld_combo_box("listbox")) + { + InitControlBase(m_xWidget.get()); + m_xWidget->set_size_request(42, -1); // so a later narrow size request can stick + m_xWidget->connect_changed(LINK(this, ListBoxControl, SelectHdl)); + m_xWidget->connect_key_press(LINK(this, ControlBase, KeyInputHdl)); + m_xWidget->connect_key_release(LINK(this, ControlBase, KeyReleaseHdl)); + m_xWidget->connect_focus_in(LINK(this, ControlBase, FocusInHdl)); + m_xWidget->connect_focus_out(LINK(this, ControlBase, FocusOutHdl)); + m_xWidget->connect_mouse_press(LINK(this, ControlBase, MousePressHdl)); + m_xWidget->connect_mouse_release(LINK(this, ControlBase, MouseReleaseHdl)); + m_xWidget->connect_mouse_move(LINK(this, ControlBase, MouseMoveHdl)); + } + + void ListBoxControl::SetPointFont(const vcl::Font& rFont) + { + m_xWidget->set_font(rFont); + } + + 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::IsValueChangedFromSaved() const + { + return GetListBox().get_value_changed_from_saved(); + } + + void ListBoxCellController::SaveValue() + { + GetListBox().save_value(); + } + + IMPL_LINK_NOARG(ListBoxCellController, ListBoxSelectHdl, LinkParamNone*, void) + { + callModifyHdl(); + } + + //= CheckBoxControl + CheckBoxControl::CheckBoxControl(BrowserDataWin* pParent) + : ControlBase(pParent, "svt/ui/checkboxcontrol.ui", "CheckBoxControl") + , m_xBox(m_xBuilder->weld_check_button("checkbox")) + { + m_aModeState.bTriStateEnabled = true; + InitControlBase(m_xBox.get()); + m_xBox->connect_key_press(LINK(this, ControlBase, KeyInputHdl)); + m_xBox->connect_key_release(LINK(this, ControlBase, KeyReleaseHdl)); + m_xBox->connect_focus_in(LINK(this, ControlBase, FocusInHdl)); + m_xBox->connect_focus_out(LINK(this, ControlBase, FocusOutHdl)); + m_xBox->connect_mouse_press(LINK(this, ControlBase, MousePressHdl)); + m_xBox->connect_mouse_release(LINK(this, ControlBase, MouseReleaseHdl)); + m_xBox->connect_mouse_move(LINK(this, ControlBase, MouseMoveHdl)); + m_xBox->connect_toggled(LINK(this, CheckBoxControl, OnToggle)); + } + + void CheckBoxControl::SetPointFont(const vcl::Font& /*rFont*/) + { + } + + void CheckBoxControl::EnableTriState( bool bTriState ) + { + if (m_aModeState.bTriStateEnabled != bTriState) + { + m_aModeState.bTriStateEnabled = bTriState; + + if (!m_aModeState.bTriStateEnabled && GetState() == TRISTATE_INDET) + SetState(TRISTATE_FALSE); + } + } + + void CheckBoxControl::SetState(TriState eState) + { + if (!m_aModeState.bTriStateEnabled && (eState == TRISTATE_INDET)) + eState = TRISTATE_FALSE; + m_aModeState.eState = eState; + m_xBox->set_state(eState); + } + + CheckBoxControl::~CheckBoxControl() + { + disposeOnce(); + } + + void CheckBoxControl::dispose() + { + m_xBox.reset(); + ControlBase::dispose(); + } + + void CheckBoxControl::Clicked() + { + // if tristate is enabled, m_aModeState will take care of setting the + // next state in the sequence via TriStateEnabled::ButtonToggled + if (!m_aModeState.bTriStateEnabled) + m_xBox->set_active(!m_xBox->get_active()); + OnToggle(*m_xBox); + } + + IMPL_LINK_NOARG(CheckBoxControl, OnToggle, weld::Toggleable&, void) + { + m_aModeState.ButtonToggled(*m_xBox); + m_aToggleLink.Call(*m_xBox); + CallModifyHdls(); + } + + //= CheckBoxCellController + CheckBoxCellController::CheckBoxCellController(CheckBoxControl* pWin) + : CellController(pWin) + { + static_cast<CheckBoxControl &>(GetWindow()).SetModifyHdl( LINK(this, CheckBoxCellController, ModifyHdl) ); + } + + void CheckBoxCellController::ActivatingMouseEvent(const BrowserMouseEvent& rEvt, bool /*bUp*/) + { + CheckBoxControl& rControl = static_cast<CheckBoxControl&>(GetWindow()); + rControl.GrabFocus(); + + // we have to adjust the position of the event relative to the controller's window + Point aPos = rEvt.GetPosPixel() - rEvt.GetRect().TopLeft(); + + Size aControlSize = rControl.GetSizePixel(); + Size aBoxSize = rControl.GetBox().get_preferred_size(); + tools::Rectangle aHotRect(Point((aControlSize.Width() - aBoxSize.Width()) / 2, + (aControlSize.Height() - aBoxSize.Height()) / 2), + aBoxSize); + + // we want the initial mouse event to act as if it was performed on the checkbox + if (aHotRect.Contains(aPos)) + rControl.Clicked(); + } + + weld::CheckButton& CheckBoxCellController::GetCheckBox() const + { + return static_cast<CheckBoxControl &>(GetWindow()).GetBox(); + } + + bool CheckBoxCellController::IsValueChangedFromSaved() const + { + return GetCheckBox().get_state_changed_from_saved(); + } + + void CheckBoxCellController::SaveValue() + { + GetCheckBox().save_state(); + } + + IMPL_LINK_NOARG(CheckBoxCellController, ModifyHdl, LinkParamNone*, void) + { + callModifyHdl(); + } + + //= MultiLineEditImplementation + OUString MultiLineEditImplementation::GetText(LineEnd eSeparator) const + { + weld::TextView& rEntry = m_rEdit.get_widget(); + return convertLineEnd(rEntry.get_text(), eSeparator); + } + + OUString MultiLineEditImplementation::GetSelected(LineEnd eSeparator) const + { + int nStartPos, nEndPos; + weld::TextView& rEntry = m_rEdit.get_widget(); + rEntry.get_selection_bounds(nStartPos, nEndPos); + return convertLineEnd(rEntry.get_text().copy(nStartPos, nEndPos - nStartPos), eSeparator); + } + + IMPL_LINK_NOARG(MultiLineEditImplementation, ModifyHdl, weld::TextView&, void) + { + CallModifyHdls(); + } + + EditCellController::EditCellController( IEditImplementation* _pImplementation ) + :CellController( &_pImplementation->GetControl() ) + ,m_pEditImplementation( _pImplementation ) + ,m_bOwnImplementation( false ) + { + m_pEditImplementation->SetModifyHdl( LINK(this, EditCellController, ModifyHdl) ); + } + + IMPL_LINK_NOARG(EntryImplementation, ModifyHdl, weld::Entry&, void) + { + CallModifyHdls(); + } + + ControlBase::ControlBase(BrowserDataWin* pParent, const OUString& rUIXMLDescription, const OUString& rID) + : InterimItemWindow(pParent, rUIXMLDescription, rID) + { + } + + void ControlBase::SetEditableReadOnly(bool /*bReadOnly*/) + { + // expected to be overridden for Entry, TextView or the editable entry part of a ComboBox + } + + EditControlBase::EditControlBase(BrowserDataWin* pParent) + : ControlBase(pParent, "svt/ui/thineditcontrol.ui", "EditControl") // *thin*editcontrol has no frame/border + , m_pEntry(nullptr) // inheritors are expected to call InitEditControlBase + { + } + + void EditControlBase::InitEditControlBase(weld::Entry* pEntry) + { + InitControlBase(pEntry); + m_pEntry = pEntry; + m_pEntry->show(); + m_pEntry->set_width_chars(1); // so a smaller than default width can be used + connect_focus_in(LINK(this, ControlBase, FocusInHdl)); // need to chain with pattern handler + connect_focus_out(LINK(this, ControlBase, FocusOutHdl)); // need to chain with pattern handler + connect_key_press(LINK(this, ControlBase, KeyInputHdl)); // need to chain with pattern handler + m_pEntry->connect_key_release(LINK(this, ControlBase, KeyReleaseHdl)); + m_pEntry->connect_mouse_press(LINK(this, ControlBase, MousePressHdl)); + m_pEntry->connect_mouse_release(LINK(this, ControlBase, MouseReleaseHdl)); + m_pEntry->connect_mouse_move(LINK(this, ControlBase, MouseMoveHdl)); + } + + bool ControlBase::ProcessKey(const KeyEvent& rKEvt) + { + return static_cast<BrowserDataWin*>(GetParent())->GetParent()->ProcessKey(rKEvt); + } + + IMPL_LINK(ControlBase, KeyInputHdl, const KeyEvent&, rKEvt, bool) + { + m_aKeyInputHdl.Call(rKEvt); + return ProcessKey(rKEvt); + } + + IMPL_LINK(ControlBase, KeyReleaseHdl, const KeyEvent&, rKEvt, bool) + { + m_aKeyReleaseHdl.Call(rKEvt); + return false; + } + + IMPL_LINK_NOARG(ControlBase, FocusInHdl, weld::Widget&, void) + { + m_aFocusInHdl.Call(nullptr); + static_cast<BrowserDataWin*>(GetParent())->GetParent()->ChildFocusIn(); + } + + IMPL_LINK_NOARG(ControlBase, FocusOutHdl, weld::Widget&, void) + { + m_aFocusOutHdl.Call(nullptr); + static_cast<BrowserDataWin*>(GetParent())->GetParent()->ChildFocusOut(); + } + + IMPL_LINK(ControlBase, MousePressHdl, const MouseEvent&, rEvent, bool) + { + m_aMousePressHdl.Call(rEvent); + return false; + } + + IMPL_LINK(ControlBase, MouseReleaseHdl, const MouseEvent&, rEvent, bool) + { + m_aMouseReleaseHdl.Call(rEvent); + return false; + } + + IMPL_LINK(ControlBase, MouseMoveHdl, const MouseEvent&, rEvent, bool) + { + m_aMouseMoveHdl.Call(rEvent); + return false; + } + + void EditControlBase::dispose() + { + m_pEntry = nullptr; + ControlBase::dispose(); + } + + EditControl::EditControl(BrowserDataWin* pParent) + : EditControlBase(pParent) + , m_xWidget(m_xBuilder->weld_entry("entry")) + { + InitEditControlBase(m_xWidget.get()); + } + + void EditControl::dispose() + { + m_xWidget.reset(); + EditControlBase::dispose(); + } + + FormattedControlBase::FormattedControlBase(BrowserDataWin* pParent, bool bSpinVariant) + : EditControlBase(pParent) + , m_bSpinVariant(bSpinVariant) + , m_xEntry(m_xBuilder->weld_entry("entry")) + , m_xSpinButton(m_xBuilder->weld_formatted_spin_button("spinbutton")) + { + } + + void FormattedControlBase::InitFormattedControlBase() + { + InitEditControlBase(m_bSpinVariant ? m_xSpinButton.get() : m_xEntry.get()); + } + + void FormattedControlBase::connect_changed(const Link<weld::Entry&, void>& rLink) + { + get_formatter().connect_changed(rLink); + } + + void FormattedControlBase::connect_focus_in(const Link<weld::Widget&, void>& rLink) + { + get_widget().connect_focus_in(rLink); + } + + void FormattedControlBase::connect_focus_out(const Link<weld::Widget&, void>& rLink) + { + get_formatter().connect_focus_out(rLink); + } + + void FormattedControlBase::connect_key_press(const Link<const KeyEvent&, bool>& rLink) + { + get_widget().connect_key_press(rLink); + } + + weld::EntryFormatter& FormattedControlBase::get_formatter() + { + return *m_xEntryFormatter; + } + + void FormattedControlBase::dispose() + { + m_xEntryFormatter.reset(); + m_xSpinButton.reset(); + m_xEntry.reset(); + EditControlBase::dispose(); + } + + FormattedControl::FormattedControl(BrowserDataWin* pParent, bool bSpinVariant) + : FormattedControlBase(pParent, bSpinVariant) + { + if (bSpinVariant) + m_xEntryFormatter.reset(new weld::EntryFormatter(*m_xSpinButton)); + else + m_xEntryFormatter.reset(new weld::EntryFormatter(*m_xEntry)); + InitFormattedControlBase(); + } + + DoubleNumericControl::DoubleNumericControl(BrowserDataWin* pParent, bool bSpinVariant) + : FormattedControlBase(pParent, bSpinVariant) + { + if (bSpinVariant) + m_xEntryFormatter.reset(new weld::DoubleNumericFormatter(*m_xSpinButton)); + else + m_xEntryFormatter.reset(new weld::DoubleNumericFormatter(*m_xEntry)); + InitFormattedControlBase(); + } + + LongCurrencyControl::LongCurrencyControl(BrowserDataWin* pParent, bool bSpinVariant) + : FormattedControlBase(pParent, bSpinVariant) + { + if (bSpinVariant) + m_xEntryFormatter.reset(new weld::LongCurrencyFormatter(*m_xSpinButton)); + else + m_xEntryFormatter.reset(new weld::LongCurrencyFormatter(*m_xEntry)); + InitFormattedControlBase(); + } + + TimeControl::TimeControl(BrowserDataWin* pParent, bool bSpinVariant) + : FormattedControlBase(pParent, bSpinVariant) + { + if (bSpinVariant) + m_xEntryFormatter.reset(new weld::TimeFormatter(*m_xSpinButton)); + else + m_xEntryFormatter.reset(new weld::TimeFormatter(*m_xEntry)); + InitFormattedControlBase(); + } + + DateControl::DateControl(BrowserDataWin* pParent, bool bDropDown) + : FormattedControlBase(pParent, false) + , m_xMenuButton(m_xBuilder->weld_menu_button("button")) + , m_xCalendarBuilder(Application::CreateBuilder(m_xMenuButton.get(), "svt/ui/datewindow.ui")) + , m_xTopLevel(m_xCalendarBuilder->weld_widget("date_popup_window")) + , m_xCalendar(m_xCalendarBuilder->weld_calendar("date_picker")) + , m_xExtras(m_xCalendarBuilder->weld_widget("extras")) + , m_xTodayBtn(m_xCalendarBuilder->weld_button("today")) + , m_xNoneBtn(m_xCalendarBuilder->weld_button("none")) + { + m_xEntryFormatter.reset(new weld::DateFormatter(*m_xEntry)); + InitFormattedControlBase(); + + m_xMenuButton->set_popover(m_xTopLevel.get()); + m_xMenuButton->set_visible(bDropDown); + m_xMenuButton->connect_toggled(LINK(this, DateControl, ToggleHdl)); + + m_xExtras->show(); + + m_xTodayBtn->connect_clicked(LINK(this, DateControl, ImplClickHdl)); + m_xNoneBtn->connect_clicked(LINK(this, DateControl, ImplClickHdl)); + + m_xCalendar->connect_activated(LINK(this, DateControl, ActivateHdl)); + } + + IMPL_LINK(DateControl, ImplClickHdl, weld::Button&, rBtn, void) + { + m_xMenuButton->set_active(false); + get_widget().grab_focus(); + + if (&rBtn == m_xTodayBtn.get()) + { + Date aToday(Date::SYSTEM); + SetDate(aToday); + } + else if (&rBtn == m_xNoneBtn.get()) + { + get_widget().set_text(OUString()); + } + } + + IMPL_LINK(DateControl, ToggleHdl, weld::Toggleable&, rButton, void) + { + if (rButton.get_active()) + m_xCalendar->set_date(static_cast<weld::DateFormatter&>(get_formatter()).GetDate()); + } + + IMPL_LINK_NOARG(DateControl, ActivateHdl, weld::Calendar&, void) + { + if (m_xMenuButton->get_active()) + m_xMenuButton->set_active(false); + static_cast<weld::DateFormatter&>(get_formatter()).SetDate(m_xCalendar->get_date()); + } + + void DateControl::SetDate(const Date& rDate) + { + static_cast<weld::DateFormatter&>(get_formatter()).SetDate(rDate); + m_xCalendar->set_date(rDate); + } + + void DateControl::dispose() + { + m_xTodayBtn.reset(); + m_xNoneBtn.reset(); + m_xExtras.reset(); + m_xCalendar.reset(); + m_xTopLevel.reset(); + m_xCalendarBuilder.reset(); + m_xMenuButton.reset(); + FormattedControlBase::dispose(); + } + + PatternControl::PatternControl(BrowserDataWin* pParent) + : EditControlBase(pParent) + , m_xWidget(m_xBuilder->weld_entry("entry")) + { + m_xEntryFormatter.reset(new weld::PatternFormatter(*m_xWidget)); + InitEditControlBase(m_xWidget.get()); + } + + void PatternControl::connect_changed(const Link<weld::Entry&, void>& rLink) + { + m_xEntryFormatter->connect_changed(rLink); + } + + void PatternControl::connect_focus_in(const Link<weld::Widget&, void>& rLink) + { + m_xEntryFormatter->connect_focus_in(rLink); + } + + void PatternControl::connect_focus_out(const Link<weld::Widget&, void>& rLink) + { + m_xEntryFormatter->connect_focus_out(rLink); + } + + void PatternControl::connect_key_press(const Link<const KeyEvent&, bool>& rLink) + { + m_xEntryFormatter->connect_key_press(rLink); + } + + void PatternControl::dispose() + { + m_xEntryFormatter.reset(); + m_xWidget.reset(); + EditControlBase::dispose(); + } + + EditCellController::EditCellController(EditControlBase* pEdit) + : CellController(pEdit) + , m_pEditImplementation(new EntryImplementation(*pEdit)) + , m_bOwnImplementation(true) + { + m_pEditImplementation->SetModifyHdl( LINK(this, EditCellController, ModifyHdl) ); + } + + EditCellController::~EditCellController( ) + { + if ( m_bOwnImplementation ) + delete m_pEditImplementation; + } + + void EditCellController::SaveValue() + { + m_pEditImplementation->SaveValue(); + } + + 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; + } + case KEY_DOWN: + { + bResult = !m_pEditImplementation->CanDown(); + break; + } + case KEY_UP: + { + bResult = !m_pEditImplementation->CanUp(); + break; + } + default: + bResult = true; + } + return bResult; + } + + bool EditCellController::IsValueChangedFromSaved() const + { + return m_pEditImplementation->IsValueChangedFromSaved(); + } + + IMPL_LINK_NOARG(EditCellController, ModifyHdl, LinkParamNone*, void) + { + callModifyHdl(); + } + + //= FormattedFieldCellController + FormattedFieldCellController::FormattedFieldCellController( FormattedControlBase* _pFormatted ) + : EditCellController(_pFormatted) + { + } + + void FormattedFieldCellController::CommitModifications() + { + static_cast<FormattedControl&>(GetWindow()).get_formatter().Commit(); + } + + MultiLineTextCell::MultiLineTextCell(BrowserDataWin* pParent) + : ControlBase(pParent, "svt/ui/textviewcontrol.ui", "TextViewControl") + , m_xWidget(m_xBuilder->weld_text_view("textview")) + { + InitControlBase(m_xWidget.get()); + m_xWidget->connect_key_press(LINK(this, ControlBase, KeyInputHdl)); + m_xWidget->connect_key_release(LINK(this, ControlBase, KeyReleaseHdl)); + m_xWidget->connect_focus_in(LINK(this, ControlBase, FocusInHdl)); + m_xWidget->connect_focus_out(LINK(this, ControlBase, FocusOutHdl)); + m_xWidget->connect_mouse_press(LINK(this, ControlBase, MousePressHdl)); + m_xWidget->connect_mouse_release(LINK(this, ControlBase, MouseReleaseHdl)); + m_xWidget->connect_mouse_move(LINK(this, ControlBase, MouseMoveHdl)); + // so any the natural size doesn't have an effect + m_xWidget->set_size_request(1, 1); + } + + void MultiLineTextCell::GetFocus() + { + if (m_xWidget) + m_xWidget->select_region(-1, 0); + ControlBase::GetFocus(); + } + + void MultiLineTextCell::dispose() + { + m_xWidget.reset(); + ControlBase::dispose(); + } + + bool MultiLineTextCell::ProcessKey(const KeyEvent& rKEvt) + { + bool bSendToDataWindow = true; + + sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode(); + bool bShift = rKEvt.GetKeyCode().IsShift(); + bool bCtrl = rKEvt.GetKeyCode().IsMod1(); + bool bAlt = rKEvt.GetKeyCode().IsMod2(); + + if (!bAlt && !bCtrl && !bShift) + { + switch (nCode) + { + case KEY_DOWN: + bSendToDataWindow = !m_xWidget->can_move_cursor_with_down(); + break; + case KEY_UP: + bSendToDataWindow = !m_xWidget->can_move_cursor_with_up(); + break; + } + } + + if (bSendToDataWindow) + return ControlBase::ProcessKey(rKEvt); + return false; + } +} // 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 0000000000..515c102379 --- /dev/null +++ b/svtools/source/brwbox/editbrowsebox.cxx @@ -0,0 +1,1284 @@ +/* -*- 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 <tools/debug.hxx> +#include <vcl/image.hxx> +#include <vcl/settings.hxx> +#include <vcl/window.hxx> +#include <vcl/svapp.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) + { + tools::Long 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(sal_Int32 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.GetOwnerWindow() == &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(sal_Int32) 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::ChildFocusIn() + { + DetermineFocus(getRealGetFocusFlags(this)); + } + + void EditBrowseBox::ChildFocusOut() + { + DetermineFocus(); + } + + 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->IsValueChangedFromSaved()) + 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()) + return; + + // forwards the event to the control + aController->ActivatingMouseEvent(_rEvt, _bUp); + } + + 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::ProcessKey(const KeyEvent& rKeyEvent) + { + sal_uInt16 nCode = rKeyEvent.GetKeyCode().GetCode(); + bool bShift = rKeyEvent.GetKeyCode().IsShift(); + bool bCtrl = rKeyEvent.GetKeyCode().IsMod1(); + bool bAlt = rKeyEvent.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->IsValueChangedFromSaved() && !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(rKeyEvent) + ) + ) + ) + { + if (nId == BROWSER_SELECT || BROWSER_SELECTCOLUMN == nId ) + { + // save the cell content (if necessary) + if (IsEditing() && aController->IsValueChangedFromSaved() && !SaveModified()) + { + // maybe we're not visible ... + EnableAndShow(); + aController->GetWindow().GrabFocus(); + return true; + } + } + + Dispatch(nId); + + if (bLocalSelect && (GetSelectRowCount() || GetSelection() != nullptr)) + DeactivateCell(); + return true; + } + return false; + } + + bool EditBrowseBox::PreNotify(NotifyEvent& rEvt) + { + if (rEvt.GetType() == NotifyEventType::KEYINPUT) + { + if ( (IsEditing() && ControlHasFocus()) + || rEvt.GetWindow() == &GetDataWindow() + || (!IsEditing() && HasChildPathFocus()) + ) + { + if (ProcessKey(*rEvt.GetKeyEvent())) + return true; + } + } + return BrowseBox::PreNotify(rEvt); + } + + bool EditBrowseBox::IsTabAllowed(bool) const + { + return true; + } + + + bool EditBrowseBox::EventNotify(NotifyEvent& rEvt) + { + switch (rEvt.GetType()) + { + case NotifyEventType::GETFOCUS: + DetermineFocus(getRealGetFocusFlags(this)); + break; + + case NotifyEventType::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().GetOutDev(), 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().GetOutDev()->SetFillColor(GetDataWindow().GetControlBackground()); + } + else + { + GetDataWindow().SetControlBackground(); + GetDataWindow().SetBackground(rStyleSettings.GetFieldColor()); + GetDataWindow().GetOutDev()->SetFillColor(rStyleSettings.GetFieldColor()); + } + } + + + bool EditBrowseBox::IsCursorMoveAllowed(sal_Int32 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->IsValueChangedFromSaved() && !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(sal_Int32, sal_uInt16) + { + DeactivateCell(false); + return true; + } + + + void EditBrowseBox::CursorMoved() + { + sal_Int32 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(sal_Int32 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->SaveValue(); + 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, + Any( CreateAccessibleCell( nRow, GetColumnPos( nCol -1) ) ), + Any() + ); + } + } + } + + + void EditBrowseBox::DeactivateCell(bool bUpdate) + { + if (!IsEditing()) + return; + + if ( isAccessibleAlive() && m_aImpl->m_xActiveCell) + { + commitBrowseBoxEvent( CHILD, Any(), Any( 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(sal_Int32 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); + // don't grab focus if Field Properties panel is being + // resized by split pane drag resizing + if (Application::IsUICaptured()) + return; + 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!"); + + tools::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(sal_Int32, 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&, sal_Int32, 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; + sal_Int32 nMaxRows = std::min(sal_Int32(GetVisibleRows()), GetRowCount()); + sal_Int32 nLastVisRow = GetTopRow() + nMaxRows - 1; + + if (GetTopRow() <= nLastVisRow) // calc the column with using the cell contents + { + for (tools::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(sal_Int32, 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->SetState(eState); + + pCheckBoxPaint->GetBox().set_sensitive(_bEnabled); + + Size aBoxSize = pCheckBoxPaint->GetBox().get_preferred_size(); + tools::Rectangle aRect(Point(rRect.Left() + ((rRect.GetWidth() - aBoxSize.Width()) / 2), + rRect.Top() + ((rRect.GetHeight() - aBoxSize.Height()) / 2)), + aBoxSize); + pCheckBoxPaint->SetPosSizePixel(aRect.TopLeft(), aRect.GetSize()); + + pCheckBoxPaint->Draw(GetDataWindow().GetOutDev(), aRect.TopLeft(), SystemTextColorFlags::NONE); + } + + 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(ControlBase* 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 + } + + void CellController::ActivatingMouseEvent(const BrowserMouseEvent& /*rEvt*/, bool /*bUp*/) + { + // nothing to do in this base class + } + + 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 0000000000..a72918ebb0 --- /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 <tools/debug.hxx> +#include <comphelper/diagnose_ex.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(sal_Int32 _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, Any( 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&) + { + TOOLS_WARN_EXCEPTION( "svtools", "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; + + sal_Int32 nRows = GetRowCount(); + sal_uInt16 nCols = ColCount(); + + if (( nRows <= 0 ) || ( nCols <= 0 )) + return; + + 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 0000000000..f71aab9b76 --- /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: */ diff --git a/svtools/source/brwbox/recorditemwindow.cxx b/svtools/source/brwbox/recorditemwindow.cxx new file mode 100644 index 0000000000..98ff79a275 --- /dev/null +++ b/svtools/source/brwbox/recorditemwindow.cxx @@ -0,0 +1,113 @@ +/* -*- 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/recorditemwindow.hxx> +#include <vcl/event.hxx> + +RecordItemWindowBase::RecordItemWindowBase(std::unique_ptr<weld::Entry> xEntry) + : m_xWidget(std::move(xEntry)) +{ + m_xWidget->connect_key_press(LINK(this, RecordItemWindowBase, KeyInputHdl)); + m_xWidget->connect_activate(LINK(this, RecordItemWindowBase, ActivatedHdl)); + m_xWidget->connect_focus_out(LINK(this, RecordItemWindowBase, FocusOutHdl)); + + m_xWidget->show(); +} + +RecordItemWindowBase::~RecordItemWindowBase() {} + +RecordItemWindow::RecordItemWindow(vcl::Window* pParent) + : InterimItemWindow(pParent, "svx/ui/absrecbox.ui", "AbsRecBox") + , RecordItemWindowBase(m_xBuilder->weld_entry("entry-frame")) +{ + InitControlBase(m_xWidget.get()); + + auto aPrefSize(m_xWidget->get_preferred_size()); + + m_xWidget->set_width_chars(1); // so a smaller than default width can be used later + + SetSizePixel(aPrefSize); +} + +void RecordItemWindow::dispose() +{ + m_xWidget.reset(); + InterimItemWindow::dispose(); +} + +RecordItemWindow::~RecordItemWindow() { disposeOnce(); } + +void RecordItemWindowBase::FirePosition(bool _bForce) +{ + if (!_bForce && !m_xWidget->get_value_changed_from_saved()) + return; + + sal_Int64 nRecord = m_xWidget->get_text().toInt64(); + if (nRecord < 1) + nRecord = 1; + + PositionFired(nRecord); + + m_xWidget->save_value(); +} + +IMPL_LINK_NOARG(RecordItemWindowBase, FocusOutHdl, weld::Widget&, void) { FirePosition(false); } + +bool RecordItemWindowBase::DoKeyInput(const KeyEvent& rKEvt) +{ + vcl::KeyCode aCode = rKEvt.GetKeyCode(); + bool bUp = (aCode.GetCode() == KEY_UP); + bool bDown = (aCode.GetCode() == KEY_DOWN); + + if (!aCode.IsShift() && !aCode.IsMod1() && !aCode.IsMod2() && (bUp || bDown)) + { + sal_Int64 nRecord = m_xWidget->get_text().toInt64(); + if (bUp) + ++nRecord; + else + --nRecord; + if (nRecord < 1) + nRecord = 1; + m_xWidget->set_text(OUString::number(nRecord)); + return true; + } + + return false; +} + +bool RecordItemWindow::DoKeyInput(const KeyEvent& rKEvt) +{ + return RecordItemWindowBase::DoKeyInput(rKEvt) || ChildKeyInput(rKEvt); +} + +void RecordItemWindowBase::PositionFired(sal_Int64 /*nRecord*/) {} + +IMPL_LINK(RecordItemWindowBase, KeyInputHdl, const KeyEvent&, rKEvt, bool) +{ + return DoKeyInput(rKEvt); +} + +IMPL_LINK_NOARG(RecordItemWindowBase, ActivatedHdl, weld::Entry&, bool) +{ + if (!m_xWidget->get_text().isEmpty()) + FirePosition(true); + return true; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |