summaryrefslogtreecommitdiffstats
path: root/svtools/source/brwbox
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
commited5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch)
tree7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /svtools/source/brwbox
parentInitial commit. (diff)
downloadlibreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.tar.xz
libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.zip
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'svtools/source/brwbox')
-rw-r--r--svtools/source/brwbox/brwbox1.cxx2397
-rw-r--r--svtools/source/brwbox/brwbox2.cxx1971
-rw-r--r--svtools/source/brwbox/brwbox3.cxx561
-rw-r--r--svtools/source/brwbox/brwhead.cxx107
-rw-r--r--svtools/source/brwbox/brwimpl.hxx80
-rw-r--r--svtools/source/brwbox/datwin.cxx694
-rw-r--r--svtools/source/brwbox/datwin.hxx107
-rw-r--r--svtools/source/brwbox/ebbcontrols.cxx768
-rw-r--r--svtools/source/brwbox/editbrowsebox.cxx1284
-rw-r--r--svtools/source/brwbox/editbrowsebox2.cxx200
-rw-r--r--svtools/source/brwbox/editbrowseboximpl.hxx35
-rw-r--r--svtools/source/brwbox/recorditemwindow.cxx113
12 files changed, 8317 insertions, 0 deletions
diff --git a/svtools/source/brwbox/brwbox1.cxx b/svtools/source/brwbox/brwbox1.cxx
new file mode 100644
index 000000000..4eafda7e9
--- /dev/null
+++ b/svtools/source/brwbox/brwbox1.cxx
@@ -0,0 +1,2397 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <svtools/brwbox.hxx>
+#include <svtools/brwhead.hxx>
+#include <o3tl/numeric.hxx>
+#include <o3tl/safeint.hxx>
+#include "datwin.hxx"
+#include <tools/debug.hxx>
+#include <tools/fract.hxx>
+#include <sal/log.hxx>
+#include <vcl/InterimItemWindow.hxx>
+#include <vcl/scrbar.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, ScrollHdl ) );
+ 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
+{
+ // 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(), static_cast<tools::Long>(GetSettings().GetStyleSettings().GetScrollBarSize()));
+}
+
+BrowseBox::BrowseBox( vcl::Window* pParent, WinBits nBits, BrowserMode nMode )
+ :Control( pParent, nBits | WB_3DLOOK )
+ ,DragSourceHelper( this )
+ ,DropTargetHelper( this )
+ ,aHScroll( VclPtr<ScrollBar>::Create(this, WB_HSCROLL) )
+ // see NavigationBar ctor, here we just want to know its height
+ ,aStatusBarHeight(VclPtr<MeasureStatusBar>::Create(this))
+{
+ 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->pCornerWin.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();
+}
+
+sal_uLong BrowseBox::GetDefaultColumnWidth( const OUString& _rText ) const
+{
+ return pDataWin->GetTextWidth( _rText ) + pDataWin->GetTextWidth(OUString('0')) * 4;
+}
+
+
+void BrowseBox::InsertHandleColumn( sal_uLong nWidth )
+{
+
+#if OSL_DEBUG_LEVEL > 0
+ OSL_ENSURE( ColCount() == 0 || mvCols[0]->GetId() != HandleColumnId , "BrowseBox::InsertHandleColumn: there is already a handle column" );
+ {
+ for (auto const & col : mvCols)
+ OSL_ENSURE( col->GetId() != HandleColumnId, "BrowseBox::InsertHandleColumn: there is a non-Handle column with handle ID" );
+ }
+#endif
+
+ mvCols.insert( mvCols.begin(), std::unique_ptr<BrowserColumn>(new BrowserColumn( 0, OUString(), nWidth, GetZoom() )) );
+ FreezeColumn( 0 );
+
+ // adjust headerbar
+ if ( pDataWin->pHeaderBar )
+ {
+ pDataWin->pHeaderBar->SetPosSizePixel(
+ Point(nWidth, 0),
+ Size( GetOutputSizePixel().Width() - nWidth, GetTitleHeight() )
+ );
+ }
+
+ ColumnInserted( 0 );
+}
+
+
+void BrowseBox::InsertDataColumn( sal_uInt16 nItemId, const OUString& rText,
+ 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, sal_uLong nWidth )
+{
+
+ // get the position in the current array
+ size_t nItemPos = GetColumnPos( nItemId );
+ if ( nItemPos >= mvCols.size() )
+ return;
+
+ // does the state change?
+ if ( nWidth < LONG_MAX && mvCols[ nItemPos ]->Width() == nWidth )
+ return;
+
+ 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 > o3tl::make_unsigned(nMaxWidth) )
+ {
+ nWidth = nMaxWidth > 16 ? nMaxWidth : nOldWidth;
+ }
+ }
+
+ // OV
+ // In AutoSizeLastColumn(), we call SetColumnWidth with nWidth==0xffff.
+ // Thus, check here, if the width has actually changed.
+ if( static_cast<sal_uLong>(nOldWidth) == nWidth )
+ return;
+
+ // do we want to display the change immediately?
+ bool bUpdate = GetUpdateMode() &&
+ ( mvCols[ nItemPos ]->IsFrozen() || nItemPos >= nFirstCol );
+
+ if ( bUpdate )
+ {
+ // Selection hidden
+ DoHideCursor();
+ 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( static_cast<sal_uLong>(nOldWidth), nWidth ), 0,
+ GetSizePixel().Width() , // the header is longer than the datawin
+ pDataWin->GetPosPixel().Y() - 1 );
+ Control::Scroll( nWidth-nOldWidth, 0, aScrRect, SCROLL_FLAGS );
+ aScrRect.SetBottom( pDataWin->GetSizePixel().Height() );
+ pDataWin->Scroll( nWidth-nOldWidth, 0, aScrRect, SCROLL_FLAGS );
+ tools::Rectangle aInvRect( nX, 0, nX + std::max( nWidth, static_cast<sal_uLong>(nOldWidth) ), USHRT_MAX );
+ Control::Invalidate( aInvRect, InvalidateFlags::NoChildren );
+ pDataWin->Invalidate( aInvRect );
+ }
+ else
+ {
+ Control::Invalidate( InvalidateFlags::NoChildren );
+ pDataWin->Window::Invalidate( InvalidateFlags::NoChildren );
+ }
+
+
+ //!pDataWin->Update();
+ //!Control::Update();
+ bSelectionIsVisible = bSelVis;
+ ToggleSelection();
+ DoShowCursor();
+ }
+ 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;
+
+ constexpr WinBits nVScrollWinBits = WB_VSCROLL;
+ pVScroll = ( nMode & BrowserMode::TRACKING_TIPS ) == BrowserMode::TRACKING_TIPS
+ ? VclPtr<BrowserScrollBar>::Create( this, nVScrollWinBits, pDataWin.get() )
+ : VclPtr<ScrollBar>::Create( this, nVScrollWinBits );
+ pVScroll->SetLineSize( 1 );
+ pVScroll->SetPageSize(1);
+ pVScroll->SetScrollHdl( LINK( this, BrowseBox, ScrollHdl ) );
+
+ pDataWin->bAutoSizeLastCol =
+ BrowserMode::AUTOSIZE_LASTCOL == ( nMode & BrowserMode::AUTOSIZE_LASTCOL );
+
+ // create a headerbar. what happens, if a headerbar has to be created and
+ // there already are columns?
+ if ( BrowserMode::HEADERBAR_NEW == ( nMode & BrowserMode::HEADERBAR_NEW ) )
+ {
+ if (!pDataWin->pHeaderBar)
+ pDataWin->pHeaderBar = CreateHeaderBar( this );
+ }
+ else
+ {
+ pDataWin->pHeaderBar.disposeAndClear();
+ }
+
+ if ( bColumnCursor )
+ {
+ if (!pColSel)
+ pColSel.reset(new MultiSelection);
+ pColSel->SetTotalRange( Range( 0, mvCols.size()-1 ) );
+ }
+ else
+ {
+ pColSel.reset();
+ }
+
+ if ( bMultiSelection )
+ {
+ if ( pOldRowSel )
+ uRow.pSel = pOldRowSel;
+ else
+ uRow.pSel = new MultiSelection;
+ }
+ else
+ {
+ uRow.nSel = nOldRowSel;
+ delete pOldRowSel;
+ }
+
+ if ( bBootstrapped )
+ {
+ StateChanged( StateChangedType::InitShow );
+ if ( bMultiSelection && !pOldRowSel &&
+ nOldRowSel != BROWSER_ENDOFSELECTION )
+ uRow.pSel->Select( nOldRowSel );
+ }
+
+ if ( pDataWin )
+ pDataWin->Invalidate();
+
+ // no cursor on handle column
+ if ( nCurColId == HandleColumnId )
+ nCurColId = GetColumnId( 1 );
+
+ m_nCurrentMode = nMode;
+}
+
+
+void BrowseBox::VisibleRowsChanged( 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 000000000..98eb5e88d
--- /dev/null
+++ b/svtools/source/brwbox/brwbox2.cxx
@@ -0,0 +1,1971 @@
+/* -*- 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 "datwin.hxx"
+#include <svtools/colorcfg.hxx>
+#include <vcl/commandevent.hxx>
+#include <vcl/ptrstyle.hxx>
+#include <vcl/scrbar.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 );
+ }
+}
+
+
+sal_uLong BrowseBox::GetColumnWidth( sal_uInt16 nId ) const
+{
+
+ sal_uInt16 nItemPos = GetColumnPos( nId );
+ if ( nItemPos >= mvCols.size() )
+ return 0;
+ return mvCols[ nItemPos ]->Width();
+}
+
+
+sal_uInt16 BrowseBox::GetColumnId( sal_uInt16 nPos ) const
+{
+
+ if ( nPos >= mvCols.size() )
+ return BROWSER_INVALIDID;
+ return mvCols[ nPos ]->GetId();
+}
+
+
+sal_uInt16 BrowseBox::GetColumnPos( sal_uInt16 nId ) const
+{
+ for ( size_t nPos = 0; nPos < mvCols.size(); ++nPos )
+ if ( mvCols[ nPos ]->GetId() == nId )
+ return nPos;
+ return BROWSER_INVALIDID;
+}
+
+
+bool BrowseBox::IsFrozen( sal_uInt16 nColumnId ) const
+{
+ for (auto const & pCol : mvCols)
+ if ( pCol->GetId() == nColumnId )
+ return pCol->IsFrozen();
+ return false;
+}
+
+
+void BrowseBox::ExpandRowSelection( const BrowserMouseEvent& rEvt )
+{
+ DoHideCursor();
+
+ // expand the last selection
+ if ( bMultiSelection )
+ {
+ Range aJustifiedRange( aSelRange );
+ aJustifiedRange.Justify();
+
+ 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 nSBSize = GetBarHeight();
+ if (IsZoom())
+ nSBSize = static_cast<sal_uLong>(nSBSize * 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 -= nSBSize;
+
+ tools::Long nDataWidth = GetOutputSizePixel().Width();
+ if ( pVScroll->IsVisible() )
+ nDataWidth -= nSBSize;
+
+ // adjust position and size of data window
+ pDataWin->SetPosSizePixel(
+ Point( 0, GetTitleHeight() ),
+ Size( nDataWidth, nDataHeight ) );
+
+ sal_uInt16 nVisibleRows = 0;
+
+ if (GetDataRowHeight())
+ nVisibleRows = static_cast<sal_uInt16>(pDataWin->GetOutputSizePixel().Height() / GetDataRowHeight() + 1);
+
+ // TopRow is unchanged, but the number of visible lines has changed.
+ if ( nVisibleRows != nOldVisibleRows )
+ VisibleRowsChanged(nTopRow, nVisibleRows);
+
+ UpdateScrollbars();
+
+ // Control-Area
+ tools::Rectangle aInvalidArea( GetControlArea() );
+ aInvalidArea.SetRight( GetOutputSizePixel().Width() );
+ aInvalidArea.SetLeft( 0 );
+ Invalidate( aInvalidArea );
+
+ // external header-bar
+ HeaderBar* pHeaderBar = pDataWin->pHeaderBar;
+ if ( pHeaderBar )
+ {
+ // take the handle column into account
+ BrowserColumn *pFirstCol = mvCols[ 0 ].get();
+ 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();
+ }
+}
+
+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)
+ sal_uLong nCornerSize = GetBarHeight();
+ if (IsZoom())
+ nCornerSize = static_cast<sal_uLong>(nCornerSize * 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() - nCornerSize );
+ aDataWinSize = aNewSize;
+ }
+
+ // needs HScroll?
+ sal_uLong nLastCol = GetColumnAtXPosPixel( aDataWinSize.Width() - 1 );
+
+ sal_uInt16 nFrozenCols = FrozenColCount();
+ bool bNeedsHScroll = pDataWin->bAutoHScroll
+ ? ( nFirstCol > nFrozenCols ) || ( nLastCol <= mvCols.size() )
+ : !pDataWin->bNoHScroll;
+ if ( !bNeedsHScroll )
+ {
+ if ( aHScroll->IsVisible() )
+ {
+ aHScroll->Hide();
+ }
+ aDataWinSize.setHeight( GetOutputSizePixel().Height() - GetTitleHeight() );
+ if ( nControlAreaWidth != USHRT_MAX )
+ aDataWinSize.AdjustHeight( -sal_Int32(nCornerSize) );
+ }
+ else if ( !aHScroll->IsVisible() )
+ {
+ Size aNewSize( aDataWinSize );
+ aNewSize.setHeight( GetOutputSizePixel().Height() - GetTitleHeight() - nCornerSize );
+ aDataWinSize = aNewSize;
+ }
+
+ // adjust position and Width of horizontal scrollbar
+ sal_uLong nHScrX = nControlAreaWidth == USHRT_MAX
+ ? 0
+ : nControlAreaWidth;
+
+ aHScroll->SetPosSizePixel(
+ Point( nHScrX, GetOutputSizePixel().Height() - nCornerSize ),
+ Size( aDataWinSize.Width() - nHScrX, nCornerSize ) );
+
+ // total scrollable columns
+ short nScrollCols = short(mvCols.size()) - static_cast<short>(nFrozenCols);
+
+ // visible columns
+ short nVisibleHSize = nLastCol == BROWSER_INVALIDID
+ ? static_cast<short>( mvCols.size() - nFirstCol )
+ : static_cast<short>( nLastCol - nFirstCol );
+
+ short nRange = std::max( nScrollCols, short(0) );
+ aHScroll->SetVisibleSize( nVisibleHSize );
+ aHScroll->SetRange( Range( 0, nRange ));
+ if ( bNeedsHScroll && !aHScroll->IsVisible() )
+ aHScroll->Show();
+
+ // adjust position and height of vertical scrollbar
+ pVScroll->SetPageSize( nMaxRows );
+
+ if ( nTopRow > nRowCount )
+ {
+ nTopRow = nRowCount - 1;
+ OSL_FAIL("BrowseBox: nTopRow > nRowCount");
+ }
+
+ if ( pVScroll->GetThumbPos() != nTopRow )
+ pVScroll->SetThumbPos( nTopRow );
+ 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( nCornerSize, 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)
+ sal_uLong nActualCorderWidth = 0;
+ if (aHScroll->IsVisible() && pVScroll && pVScroll->IsVisible() )
+ {
+ // if we have both scrollbars, the corner window fills the point of intersection of these two
+ nActualCorderWidth = nCornerSize;
+ }
+ else if ( !aHScroll->IsVisible() && ( nControlAreaWidth != USHRT_MAX ) )
+ {
+ // if we have no horizontal scrollbar, but a control area, we need the corner window to
+ // fill the space between the control are and the right border
+ nActualCorderWidth = GetOutputSizePixel().Width() - nControlAreaWidth;
+ }
+ if ( nActualCorderWidth )
+ {
+ if ( !pDataWin->pCornerWin )
+ pDataWin->pCornerWin = VclPtr<ScrollBarBox>::Create( this, 0 );
+ pDataWin->pCornerWin->SetPosSizePixel(
+ Point( GetOutputSizePixel().Width() - nActualCorderWidth, aHScroll->GetPosPixel().Y() ),
+ Size( nActualCorderWidth, nCornerSize ) );
+ pDataWin->pCornerWin->Show();
+ }
+ else
+ pDataWin->pCornerWin.disposeAndClear();
+
+ // scroll headerbar, if necessary
+ if ( pDataWin->pHeaderBar )
+ {
+ 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, ScrollHdl, ScrollBar*, pBar, void)
+{
+ if ( pBar->GetDelta() == 0 )
+ return;
+
+ if ( pBar == aHScroll.get() )
+ ScrollColumns( aHScroll->GetDelta() );
+ if ( pBar == pVScroll )
+ ScrollRows( pVScroll->GetDelta() );
+}
+
+
+IMPL_LINK( BrowseBox, StartDragHdl, HeaderBar*, pBar, void )
+{
+ pBar->SetDragSize( pDataWin->GetOutputSizePixel().Height() );
+}
+
+
+// usually only the first column was resized
+
+void BrowseBox::MouseButtonDown( const MouseEvent& rEvt )
+{
+
+ GrabFocus();
+
+ // only mouse events in the title-line are supported
+ const Point &rEvtPos = rEvt.GetPosPixel();
+ if ( rEvtPos.Y() >= GetTitleHeight() )
+ return;
+
+ 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() ) < o3tl::make_unsigned(GetOutputSizePixel().Width());
+ ++nCol )
+ // is this column visible?
+ if ( mvCols[ nCol ]->IsFrozen() || nCol >= nFirstCol )
+ {
+ // compute right end of column
+ BrowserColumn *pCol = mvCols[ nCol ].get();
+ sal_uInt16 nR = static_cast<sal_uInt16>(nX + pCol->Width() - 1);
+
+ // show resize-pointer?
+ if ( bResizing || ( pCol->GetId() &&
+ std::abs( static_cast<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);
+ sal_uLong nOldWidth = GetColumnWidth(nId);
+ nDragX = nOldWidth + nDeltaX + nResizeX - nOldWidth;
+
+ // draw new auxiliary line
+ pDataWin->ShowTracking( tools::Rectangle( Point( nDragX, 0 ),
+ Size( 1, pDataWin->GetSizePixel().Height() ) ),
+ ShowTrackFlags::Split|ShowTrackFlags::TrackWindow );
+ }
+
+ }
+
+ nX = nR + 1;
+ }
+
+ SetPointer( aNewPointer );
+}
+
+
+void BrowseBox::MouseButtonUp( const MouseEvent & rEvt )
+{
+
+ if ( bResizing )
+ {
+ // delete auxiliary line
+ pDataWin->HideTracking();
+
+ // width changed?
+ nDragX = std::max( rEvt.GetPosPixel().X(), nMinResizeX );
+ if ( (nDragX - nResizeX) != static_cast<tools::Long>(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);
+ nHeight = GetWindowExtentsRelative( pParent ).GetHeight() - aTopLeft.Y() - GetControlArea().GetSize().Height();
+ }
+ aTopLeft += GetWindowExtentsRelative( pParent ).TopLeft();
+ return tools::Rectangle(aTopLeft,Size(nWidth,nHeight));
+}
+
+tools::Rectangle BrowseBox::calcTableRect(bool _bOnScreen)
+{
+ vcl::Window* pParent = nullptr;
+ if ( !_bOnScreen )
+ pParent = GetAccessibleParentWindow();
+
+ tools::Rectangle aRect( GetWindowExtentsRelative( pParent ) );
+ tools::Rectangle aRowBar = calcHeaderRect(false, pParent == nullptr);
+
+ 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::GetFieldRectPixelAbs( sal_Int32 _nRowId, sal_uInt16 _nColId, bool /*_bIsHeader*/, bool _bOnScreen )
+{
+ vcl::Window* pParent = nullptr;
+ if ( !_bOnScreen )
+ pParent = GetAccessibleParentWindow();
+
+ tools::Rectangle aRect = GetFieldRectPixel(_nRowId,_nColId,_bOnScreen);
+
+ Point aTopLeft = aRect.TopLeft();
+ aTopLeft += GetWindowExtentsRelative( pParent ).TopLeft();
+
+ return tools::Rectangle(aTopLeft,aRect.GetSize());
+}
+
+// ------------------------------------------------------------------------- EOF
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svtools/source/brwbox/brwbox3.cxx b/svtools/source/brwbox/brwbox3.cxx
new file mode 100644
index 000000000..32401d312
--- /dev/null
+++ b/svtools/source/brwbox/brwbox3.cxx
@@ -0,0 +1,561 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <svtools/brwbox.hxx>
+#include <vcl/AccessibleBrowseBoxObjType.hxx>
+#include <vcl/accessiblefactory.hxx>
+#include <unotools/accessiblestatesethelper.hxx>
+#include <sal/log.hxx>
+#include <tools/debug.hxx>
+#include <tools/multisel.hxx>
+#include "brwimpl.hxx"
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+
+// Accessibility ==============================================================
+
+using namespace ::com::sun::star::uno;
+using ::com::sun::star::accessibility::XAccessible;
+using namespace ::com::sun::star::accessibility;
+
+
+namespace svt
+{
+ using namespace ::com::sun::star::lang;
+ using namespace utl;
+
+ static Reference< XAccessible > getHeaderCell( BrowseBoxImpl::THeaderCellMap& _raHeaderCells,
+ sal_Int32 _nPos,
+ 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
+ );
+ }
+ }
+
+ Reference< XAccessible > xAccessible;
+ if ( m_pImpl->m_pAccessible )
+ xAccessible = m_pImpl->m_pAccessible->getMyself();
+
+ return xAccessible;
+}
+
+
+// Children -------------------------------------------------------------------
+
+Reference< XAccessible > BrowseBox::CreateAccessibleCell( sal_Int32 _nRow, sal_uInt16 _nColumnPos )
+{
+ // BBINDEX_TABLE must be the table
+ OSL_ENSURE(m_pImpl->m_pAccessible,"Invalid call: Accessible is null");
+
+ return m_pImpl->m_aFactoryAccess.getFactory().createAccessibleBrowseBoxTableCell(
+ m_pImpl->getAccessibleTable(),
+ *this,
+ nullptr,
+ _nRow,
+ _nColumnPos,
+ OFFSET_DEFAULT
+ );
+}
+
+
+Reference< XAccessible > BrowseBox::CreateAccessibleRowHeader( sal_Int32 _nRow )
+{
+ return svt::getHeaderCell(
+ m_pImpl->m_aRowHeaderCellMap,
+ _nRow,
+ 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(
+ ::utl::AccessibleStateSetHelper& rStateSet,
+ AccessibleBrowseBoxObjType eObjType ) const
+{
+ switch( eObjType )
+ {
+ case AccessibleBrowseBoxObjType::BrowseBox:
+ case AccessibleBrowseBoxObjType::Table:
+
+ rStateSet.AddState( AccessibleStateType::FOCUSABLE );
+ if ( HasFocus() )
+ rStateSet.AddState( AccessibleStateType::FOCUSED );
+ if ( IsActive() )
+ rStateSet.AddState( AccessibleStateType::ACTIVE );
+ if ( GetUpdateMode() )
+ rStateSet.AddState( AccessibleStateType::EDITABLE );
+ if ( IsEnabled() )
+ {
+ rStateSet.AddState( AccessibleStateType::ENABLED );
+ rStateSet.AddState( AccessibleStateType::SENSITIVE );
+ }
+ if ( IsReallyVisible() )
+ rStateSet.AddState( AccessibleStateType::VISIBLE );
+ if ( eObjType == AccessibleBrowseBoxObjType::Table )
+ rStateSet.AddState( AccessibleStateType::MANAGES_DESCENDANTS );
+
+ break;
+ case AccessibleBrowseBoxObjType::RowHeaderBar:
+ rStateSet.AddState( AccessibleStateType::FOCUSABLE );
+ rStateSet.AddState( AccessibleStateType::VISIBLE );
+ if ( GetSelectRowCount() )
+ rStateSet.AddState( AccessibleStateType::FOCUSED );
+ rStateSet.AddState( AccessibleStateType::MANAGES_DESCENDANTS );
+ break;
+ case AccessibleBrowseBoxObjType::ColumnHeaderBar:
+ rStateSet.AddState( AccessibleStateType::FOCUSABLE );
+ rStateSet.AddState( AccessibleStateType::VISIBLE );
+ if ( GetSelectColumnCount() )
+ rStateSet.AddState( AccessibleStateType::FOCUSED );
+ rStateSet.AddState( AccessibleStateType::MANAGES_DESCENDANTS );
+ break;
+ case AccessibleBrowseBoxObjType::TableCell:
+ {
+ sal_Int32 nRow = GetCurRow();
+ sal_uInt16 nColumn = GetCurColumnId();
+ if ( IsFieldVisible(nRow,nColumn) )
+ rStateSet.AddState( AccessibleStateType::VISIBLE );
+ if ( !IsFrozen( nColumn ) )
+ rStateSet.AddState( AccessibleStateType::FOCUSABLE );
+ rStateSet.AddState( AccessibleStateType::TRANSIENT );
+ }
+ break;
+ case AccessibleBrowseBoxObjType::RowHeaderCell:
+ case AccessibleBrowseBoxObjType::ColumnHeaderCell:
+ case AccessibleBrowseBoxObjType::CheckBoxCell:
+ OSL_FAIL("Illegal call here!");
+ break;
+ }
+}
+
+void BrowseBox::FillAccessibleStateSetForCell( ::utl::AccessibleStateSetHelper& _rStateSet,
+ sal_Int32 _nRow, sal_uInt16 _nColumnPos ) const
+{
+ //! TODO check if the state is valid for table cells
+ if ( IsCellVisible( _nRow, _nColumnPos ) )
+ _rStateSet.AddState( AccessibleStateType::VISIBLE );
+ if ( GetCurrRow() == _nRow && GetCurrColumn() == _nColumnPos )
+ _rStateSet.AddState( AccessibleStateType::FOCUSED );
+ else // only transient when column is not focused
+ _rStateSet.AddState( AccessibleStateType::TRANSIENT );
+}
+
+
+void BrowseBox::GrabTableFocus()
+{
+ GrabFocus();
+}
+
+OUString BrowseBox::GetCellText(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 ( nullptr != m_pImpl->m_pAccessible ) && m_pImpl->m_pAccessible->isAlive();
+}
+
+// IAccessibleTableProvider
+
+sal_Int32 BrowseBox::GetCurrRow() const
+{
+ return GetCurRow();
+}
+
+sal_uInt16 BrowseBox::GetCurrColumn() const
+{
+ return GetColumnPos( GetCurColumnId() );
+}
+
+bool BrowseBox::HasRowHeader() const
+{
+ return ( GetColumnId( 0 ) == HandleColumnId ); // HandleColumn == RowHeader
+}
+
+bool BrowseBox::GoToCell( sal_Int32 _nRow, sal_uInt16 _nColumn )
+{
+ return GoToRowColumnId( _nRow, GetColumnId( _nColumn ) );
+}
+
+void BrowseBox::SelectColumn( sal_uInt16 _nColumn, bool _bSelect )
+{
+ SelectColumnPos( _nColumn, _bSelect );
+}
+
+bool BrowseBox::IsColumnSelected( 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 );
+}
+
+tools::Rectangle BrowseBox::GetWindowExtentsRelative(const vcl::Window *pRelativeWindow) const
+{
+ return Control::GetWindowExtentsRelative( pRelativeWindow );
+}
+
+void BrowseBox::GrabFocus()
+{
+ Control::GrabFocus();
+}
+
+Reference< XAccessible > BrowseBox::GetAccessible()
+{
+ return Control::GetAccessible();
+}
+
+vcl::Window* BrowseBox::GetAccessibleParentWindow() const
+{
+ return Control::GetAccessibleParentWindow();
+}
+
+vcl::Window* BrowseBox::GetWindowInstance()
+{
+ return this;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svtools/source/brwbox/brwhead.cxx b/svtools/source/brwbox/brwhead.cxx
new file mode 100644
index 000000000..86ed55d0d
--- /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 000000000..af591fbcb
--- /dev/null
+++ b/svtools/source/brwbox/brwimpl.hxx
@@ -0,0 +1,80 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <vcl/accessibletableprovider.hxx>
+#include <vcl/svtaccessiblefactory.hxx>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/accessibility/XAccessible.hpp>
+#include <osl/diagnose.h>
+#include <tools/diagnose_ex.h>
+
+#include <map>
+
+namespace svt
+{
+ class BrowseBoxImpl
+ {
+ // member
+ public:
+ typedef ::std::map< sal_Int32, css::uno::Reference< css::accessibility::XAccessible > > THeaderCellMap;
+
+ struct THeaderCellMapFunctorDispose
+ {
+ void operator()(const THeaderCellMap::value_type& _aType)
+ {
+ css::uno::Reference< css::lang::XComponent > xComp( _aType.second, css::uno::UNO_QUERY );
+ OSL_ENSURE( xComp.is() || !_aType.second.is(), "THeaderCellMapFunctorDispose: invalid accessible cell (no XComponent)!" );
+ if ( xComp.is() )
+ try
+ {
+ xComp->dispose();
+ }
+ catch( const css::uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svtools", "THeaderCellMapFunctorDispose" );
+ }
+ }
+ };
+
+ public:
+ vcl::AccessibleFactoryAccess m_aFactoryAccess;
+ vcl::IAccessibleBrowseBox* m_pAccessible;
+ THeaderCellMap m_aColHeaderCellMap;
+ THeaderCellMap m_aRowHeaderCellMap;
+
+ public:
+ BrowseBoxImpl() : m_pAccessible(nullptr)
+ {
+ }
+
+
+ /// @see AccessibleBrowseBox::getHeaderBar
+ css::uno::Reference< css::accessibility::XAccessible >
+ getAccessibleHeaderBar( AccessibleBrowseBoxObjType _eObjType );
+
+ /// @see AccessibleBrowseBox::getTable
+ css::uno::Reference< css::accessibility::XAccessible >
+ getAccessibleTable( );
+
+ };
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svtools/source/brwbox/datwin.cxx b/svtools/source/brwbox/datwin.cxx
new file mode 100644
index 000000000..47541b623
--- /dev/null
+++ b/svtools/source/brwbox/datwin.cxx
@@ -0,0 +1,694 @@
+/* -*- 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 <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,
+ const OUString& rTitle, sal_uLong nWidthPixel, const Fraction& rCurrentZoom )
+: _nId( nItemId ),
+ _nWidth( nWidthPixel ),
+ _aTitle( rTitle ),
+ _bFrozen( false )
+{
+ double n = static_cast<double>(_nWidth);
+ n *= static_cast<double>(rCurrentZoom.GetDenominator());
+ if (!rCurrentZoom.GetNumerator())
+ throw o3tl::divide_by_zero();
+ n /= static_cast<double>(rCurrentZoom.GetNumerator());
+ _nOriginalWidth = n>0 ? static_cast<tools::Long>(n+0.5) : -static_cast<tools::Long>(-n+0.5);
+}
+
+BrowserColumn::~BrowserColumn()
+{
+}
+
+void BrowserColumn::SetWidth(sal_uLong nNewWidthPixel, const Fraction& rCurrentZoom)
+{
+ _nWidth = nNewWidthPixel;
+ // Avoid overflow when called with LONG_MAX from
+ // BrowseBox::AutoSizeLastColumn:
+ if (_nWidth == LONG_MAX)
+ {
+ _nOriginalWidth = _nWidth;
+ }
+ else
+ {
+ double n = static_cast<double>(_nWidth);
+ n *= static_cast<double>(rCurrentZoom.GetDenominator());
+ if (!rCurrentZoom.GetNumerator())
+ throw o3tl::divide_by_zero();
+ n /= static_cast<double>(rCurrentZoom.GetNumerator());
+ _nOriginalWidth = n>0 ? static_cast<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 )
+ ,pCornerWin( 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();
+ pCornerWin.clear();
+ DragSourceHelper::dispose();
+ DropTargetHelper::dispose();
+ Control::dispose();
+}
+
+
+void BrowserDataWin::LeaveUpdateLock()
+{
+ if ( !--nUpdateLock )
+ {
+ DoOutstandingInvalidations();
+ if (bUpdateOnUnlock )
+ {
+ Control::PaintImmediately();
+ bUpdateOnUnlock = false;
+ }
+ }
+}
+
+void InitSettings_Impl(vcl::Window* pWin)
+{
+ const StyleSettings& rStyleSettings = pWin->GetSettings().GetStyleSettings();
+
+ pWin->ApplyControlFont(*pWin->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)
+ Control::Invalidate( rRect );
+ aInvalidRegion.clear();
+}
+
+
+void BrowserDataWin::Invalidate( InvalidateFlags nFlags )
+{
+ if ( !GetUpdateMode() )
+ {
+ aInvalidRegion.clear();
+ aInvalidRegion.emplace_back( Point( 0, 0 ), GetOutputSizePixel() );
+ }
+ else
+ Window::Invalidate( nFlags );
+}
+
+
+void BrowserDataWin::Invalidate( const tools::Rectangle& rRect, InvalidateFlags nFlags )
+{
+ if ( !GetUpdateMode() )
+ aInvalidRegion.emplace_back( rRect );
+ else
+ Window::Invalidate( rRect, nFlags );
+}
+
+BrowserScrollBar::~BrowserScrollBar()
+{
+ disposeOnce();
+}
+
+void BrowserScrollBar::dispose()
+{
+ _pDataWin.clear();
+ ScrollBar::dispose();
+}
+
+void BrowserScrollBar::Tracking( const TrackingEvent& rTEvt )
+{
+ tools::Long nPos = GetThumbPos();
+ if ( nPos != _nLastPos )
+ {
+ OUString aTip = OUString::number(nPos) + "/";
+ if ( !_pDataWin->GetRealRowCount().isEmpty() )
+ aTip += _pDataWin->GetRealRowCount();
+ else
+ aTip += OUString::number(GetRangeMax());
+
+ tools::Rectangle aRect(GetPointerPosPixel(), Size(GetTextWidth(aTip), GetTextHeight()));
+ Help::ShowQuickHelp(this, aRect, aTip);
+ _nLastPos = nPos;
+ }
+
+ ScrollBar::Tracking( rTEvt );
+}
+
+void BrowserScrollBar::EndScroll()
+{
+ Help::HideBalloonAndQuickHelp();
+ ScrollBar::EndScroll();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svtools/source/brwbox/datwin.hxx b/svtools/source/brwbox/datwin.hxx
new file mode 100644
index 000000000..789d96548
--- /dev/null
+++ b/svtools/source/brwbox/datwin.hxx
@@ -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 .
+ */
+
+#pragma once
+
+#include <svtools/brwbox.hxx>
+#include <tools/long.hxx>
+#include <vcl/scrbar.hxx>
+
+#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,
+ const OUString &rText,
+ bool _bDrawDisabled)
+ :aRect( rPt, rSz )
+ ,aInnerRect( Point( aRect.Left()+1, aRect.Top()+1 ),
+ Size( aRect.GetWidth()-2, aRect.GetHeight()-2 ) )
+ ,aText(rText)
+ ,m_bDrawDisabled(_bDrawDisabled)
+ {
+ }
+
+ void Draw( OutputDevice& rDev );
+};
+
+
+class BrowserColumn final
+{
+ sal_uInt16 _nId;
+ sal_uLong _nOriginalWidth;
+ sal_uLong _nWidth;
+ OUString _aTitle;
+ bool _bFrozen;
+
+public:
+ BrowserColumn( sal_uInt16 nItemId,
+ const OUString& rTitle, sal_uLong nWidthPixel, const Fraction& rCurrentZoom );
+ ~BrowserColumn();
+
+ sal_uInt16 GetId() const { return _nId; }
+
+ sal_uLong Width() 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(sal_uLong nNewWidthPixel, const Fraction& rCurrentZoom);
+ void ZoomChanged(const Fraction& rNewZoom);
+};
+
+
+class BrowserScrollBar: public ScrollBar
+{
+ tools::Long _nLastPos;
+ VclPtr<BrowserDataWin> _pDataWin;
+
+public:
+ BrowserScrollBar( vcl::Window* pParent, WinBits nStyle,
+ BrowserDataWin *pDataWin )
+ : ScrollBar( pParent, nStyle ),
+ _nLastPos( std::numeric_limits<tools::Long>::max() ),
+ _pDataWin( pDataWin )
+ {}
+ virtual ~BrowserScrollBar() override;
+ virtual void dispose() override;
+ //ScrollBar( vcl::Window* pParent, const ResId& rResId );
+
+ virtual void Tracking( const TrackingEvent& rTEvt ) override;
+ virtual void EndScroll() override;
+};
+
+
+void InitSettings_Impl( vcl::Window *pWin );
+
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svtools/source/brwbox/ebbcontrols.cxx b/svtools/source/brwbox/ebbcontrols.cxx
new file mode 100644
index 000000000..b0a2ad1bb
--- /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 OString& 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"))
+ , 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 000000000..8cadf4748
--- /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)
+ {
+ sal_uInt32 nAutoWidth = static_cast<EditBrowseBox*>(GetParent())->GetAutoColumnWidth(nColId);
+ if (nAutoWidth != static_cast<EditBrowseBox*>(GetParent())->GetColumnWidth(nColId))
+ {
+ static_cast<EditBrowseBox*>(GetParent())->SetColumnWidth(nColId, nAutoWidth);
+ static_cast<EditBrowseBox*>(GetParent())->ColumnResized(nColId);
+ }
+ }
+ }
+
+ //= EditBrowseBox
+
+ void EditBrowseBox::BrowserMouseEventPtr::Clear()
+ {
+ pEvent.reset();
+ }
+
+ void EditBrowseBox::BrowserMouseEventPtr::Set(const BrowserMouseEvent* pEvt, bool bIsDown)
+ {
+ if (pEvt == pEvent.get())
+ {
+ bDown = bIsDown;
+ return;
+ }
+ pEvent.reset();
+ if (pEvt)
+ {
+ pEvent.reset(new BrowserMouseEvent(pEvt->GetWindow(),
+ *pEvt,
+ pEvt->GetRow(),
+ pEvt->GetColumn(),
+ pEvt->GetColumnId(),
+ pEvt->GetRect()));
+ bDown = bIsDown;
+ }
+ }
+
+ EditBrowseBox::EditBrowseBox( vcl::Window* pParent, EditBrowseBoxFlags nBrowserFlags, WinBits nBits, BrowserMode _nMode )
+ :BrowseBox( pParent, nBits, _nMode )
+ ,nStartEvent(nullptr)
+ ,nEndEvent(nullptr)
+ ,nCellModifiedEvent(nullptr)
+ ,m_pFocusWhileRequest(nullptr)
+ ,nPaintRow(-1)
+ ,nEditRow(-1)
+ ,nEditCol(0)
+ ,bHasFocus(false)
+ ,bPaintStatus(true)
+ ,bActiveBeforeTracking( false )
+ ,m_nBrowserFlags(nBrowserFlags)
+ ,pHeader(nullptr)
+ {
+ m_aImpl.reset(new EditBrowseBoxImpl);
+
+ SetCompoundControl(true);
+
+ ImplInitSettings(true, true, true);
+
+ pCheckBoxPaint = VclPtr<CheckBoxControl>::Create(&GetDataWindow());
+ pCheckBoxPaint->SetPaintTransparent( true );
+ pCheckBoxPaint->SetBackground();
+ }
+
+ void EditBrowseBox::Init()
+ {
+ // late construction
+ }
+
+ EditBrowseBox::~EditBrowseBox()
+ {
+ disposeOnce();
+ }
+
+ void EditBrowseBox::dispose()
+ {
+ if (nStartEvent)
+ Application::RemoveUserEvent(nStartEvent);
+ if (nEndEvent)
+ Application::RemoveUserEvent(nEndEvent);
+ if (nCellModifiedEvent)
+ Application::RemoveUserEvent(nCellModifiedEvent);
+
+ pCheckBoxPaint.disposeAndClear();
+ m_pFocusWhileRequest.clear();
+ pHeader.clear();
+ BrowseBox::dispose();
+ }
+
+
+ void EditBrowseBox::RemoveRows()
+ {
+ BrowseBox::Clear();
+ nEditRow = nPaintRow = -1;
+ nEditCol = 0;
+ }
+
+ VclPtr<BrowserHeader> EditBrowseBox::CreateHeaderBar(BrowseBox* pParent)
+ {
+ pHeader = imp_CreateHeaderBar(pParent);
+ if (!IsUpdateMode())
+ pHeader->SetUpdateMode(false);
+ return pHeader;
+ }
+
+ VclPtr<BrowserHeader> EditBrowseBox::imp_CreateHeaderBar(BrowseBox* pParent)
+ {
+ return VclPtr<EditBrowserHeader>::Create(pParent);
+ }
+
+ void EditBrowseBox::LoseFocus()
+ {
+ BrowseBox::LoseFocus();
+ DetermineFocus();
+ }
+
+ void EditBrowseBox::GetFocus()
+ {
+ BrowseBox::GetFocus();
+
+ // This should handle the case that the BrowseBox (or one of its children)
+ // gets the focus from outside by pressing Tab
+ if (IsEditing() && Controller()->GetWindow().IsVisible())
+ Controller()->GetWindow().GrabFocus();
+
+ DetermineFocus(getRealGetFocusFlags(this));
+ }
+
+ bool EditBrowseBox::SeekRow(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() == MouseNotifyEvent::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 MouseNotifyEvent::GETFOCUS:
+ DetermineFocus(getRealGetFocusFlags(this));
+ break;
+
+ case MouseNotifyEvent::LOSEFOCUS:
+ DetermineFocus();
+ break;
+
+ default:
+ break;
+ }
+ return BrowseBox::EventNotify(rEvt);
+ }
+
+
+ void EditBrowseBox::StateChanged( StateChangedType nType )
+ {
+ BrowseBox::StateChanged( nType );
+
+ bool bNeedCellReActivation = false;
+ if ( nType == StateChangedType::Mirroring )
+ {
+ bNeedCellReActivation = true;
+ }
+ else if ( nType == StateChangedType::Zoom )
+ {
+ ImplInitSettings( true, false, false );
+ bNeedCellReActivation = true;
+ }
+ else if ( nType == StateChangedType::ControlFont )
+ {
+ ImplInitSettings( true, false, false );
+ Invalidate();
+ }
+ else if ( nType == StateChangedType::ControlForeground )
+ {
+ ImplInitSettings( false, true, false );
+ Invalidate();
+ }
+ else if ( nType == StateChangedType::ControlBackground )
+ {
+ ImplInitSettings( false, false, true );
+ Invalidate();
+ }
+ else if (nType == StateChangedType::Style)
+ {
+ WinBits nStyle = GetStyle();
+ if (!(nStyle & WB_NOTABSTOP) )
+ nStyle |= WB_TABSTOP;
+
+ SetStyle(nStyle);
+ }
+ if ( bNeedCellReActivation )
+ {
+ if ( IsEditing() )
+ {
+ DeactivateCell();
+ ActivateCell();
+ }
+ }
+ }
+
+
+ void EditBrowseBox::DataChanged( const DataChangedEvent& rDCEvt )
+ {
+ BrowseBox::DataChanged( rDCEvt );
+
+ if ((( rDCEvt.GetType() == DataChangedEventType::SETTINGS ) ||
+ ( rDCEvt.GetType() == DataChangedEventType::DISPLAY )) &&
+ ( rDCEvt.GetFlags() & AllSettingsFlags::STYLE ))
+ {
+ ImplInitSettings( true, true, true );
+ Invalidate();
+ }
+ }
+
+ void EditBrowseBox::ImplInitSettings( bool bFont, bool bForeground, bool bBackground )
+ {
+ const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
+
+ if (bFont)
+ {
+ vcl::Font aFont = rStyleSettings.GetFieldFont();
+ if (IsControlFont())
+ {
+ GetDataWindow().SetControlFont(GetControlFont());
+ aFont.Merge(GetControlFont());
+ }
+ else
+ GetDataWindow().SetControlFont();
+
+ GetDataWindow().SetZoomedPointFont(*GetDataWindow().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() )
+ {
+ 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 000000000..0c6adee04
--- /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 <tools/diagnose_ex.h>
+
+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 000000000..f71aab9b7
--- /dev/null
+++ b/svtools/source/brwbox/editbrowseboximpl.hxx
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <com/sun/star/accessibility/XAccessible.hpp>
+
+namespace svt
+{
+ // impl class for the class EditBrowseBox
+ class EditBrowseBoxImpl
+ {
+ public:
+ css::uno::Reference< css::accessibility::XAccessible > m_xActiveCell;
+
+ void clearActiveCell();
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svtools/source/brwbox/recorditemwindow.cxx b/svtools/source/brwbox/recorditemwindow.cxx
new file mode 100644
index 000000000..98ff79a27
--- /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: */