diff options
Diffstat (limited to 'svtools/source/table')
-rw-r--r-- | svtools/source/table/cellvalueconversion.cxx | 77 | ||||
-rw-r--r-- | svtools/source/table/cellvalueconversion.hxx | 52 | ||||
-rw-r--r-- | svtools/source/table/defaultinputhandler.cxx | 249 | ||||
-rw-r--r-- | svtools/source/table/gridtablerenderer.cxx | 621 | ||||
-rw-r--r-- | svtools/source/table/mousefunction.cxx | 320 | ||||
-rw-r--r-- | svtools/source/table/mousefunction.hxx | 161 | ||||
-rw-r--r-- | svtools/source/table/tablecontrol.cxx | 613 | ||||
-rw-r--r-- | svtools/source/table/tablecontrol_impl.cxx | 2745 | ||||
-rw-r--r-- | svtools/source/table/tablecontrol_impl.hxx | 488 | ||||
-rw-r--r-- | svtools/source/table/tabledatawindow.cxx | 243 | ||||
-rw-r--r-- | svtools/source/table/tabledatawindow.hxx | 92 | ||||
-rw-r--r-- | svtools/source/table/tablegeometry.cxx | 168 | ||||
-rw-r--r-- | svtools/source/table/tablegeometry.hxx | 175 |
13 files changed, 6004 insertions, 0 deletions
diff --git a/svtools/source/table/cellvalueconversion.cxx b/svtools/source/table/cellvalueconversion.cxx new file mode 100644 index 000000000000..286ca505bb30 --- /dev/null +++ b/svtools/source/table/cellvalueconversion.cxx @@ -0,0 +1,77 @@ +/************************************************************************* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#include "precompiled_svtools.hxx" + +#include "cellvalueconversion.hxx" + +/** === begin UNO includes === **/ +/** === end UNO includes === **/ + +//...................................................................................................................... +namespace svt +{ +//...................................................................................................................... + + /** === begin UNO using === **/ + using ::com::sun::star::uno::Any; + /** === end UNO using === **/ + + //================================================================================================================== + //= CellValueConversion + //================================================================================================================== + //------------------------------------------------------------------------------------------------------------------ + ::rtl::OUString CellValueConversion::convertToString( const Any& i_value ) + { + ::rtl::OUString sConvertString; + if ( !i_value.hasValue() ) + return sConvertString; + + + // TODO: use css.script.XTypeConverter? + + sal_Int32 nInt = 0; + sal_Bool bBool = false; + double fDouble = 0; + + ::rtl::OUString sStringValue; + if ( i_value >>= sConvertString ) + sStringValue = sConvertString; + else if ( i_value >>= nInt ) + sStringValue = sConvertString.valueOf( nInt ); + else if ( i_value >>= bBool ) + sStringValue = sConvertString.valueOf( bBool ); + else if ( i_value >>= fDouble ) + sStringValue = sConvertString.valueOf( fDouble ); + else + OSL_ENSURE( !i_value.hasValue(), "CellValueConversion::convertToString: cannot handle the given cell content type!" ); + + return sStringValue; + } + +//...................................................................................................................... +} // namespace svt +//...................................................................................................................... diff --git a/svtools/source/table/cellvalueconversion.hxx b/svtools/source/table/cellvalueconversion.hxx new file mode 100644 index 000000000000..4d6b8c8d6aac --- /dev/null +++ b/svtools/source/table/cellvalueconversion.hxx @@ -0,0 +1,52 @@ +/************************************************************************* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef SVTOOLS_CELLVALUECONVERSION_HXX +#define SVTOOLS_CELLVALUECONVERSION_HXX + +/** === begin UNO includes === **/ +#include <com/sun/star/uno/Any.hxx> +/** === end UNO includes === **/ + +//...................................................................................................................... +namespace svt +{ +//...................................................................................................................... + + //================================================================================================================== + //= CellValueConversion + //================================================================================================================== + class CellValueConversion + { + public: + static ::rtl::OUString convertToString( const ::com::sun::star::uno::Any& i_cellValue ); + }; + +//...................................................................................................................... +} // namespace svt +//...................................................................................................................... + +#endif // SVTOOLS_CELLVALUECONVERSION_HXX diff --git a/svtools/source/table/defaultinputhandler.cxx b/svtools/source/table/defaultinputhandler.cxx new file mode 100644 index 000000000000..d0ecdb546837 --- /dev/null +++ b/svtools/source/table/defaultinputhandler.cxx @@ -0,0 +1,249 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * +************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_svtools.hxx" + +#include "svtools/table/defaultinputhandler.hxx" +#include "svtools/table/tablecontrolinterface.hxx" + +#include "tabledatawindow.hxx" +#include "mousefunction.hxx" + +#include <tools/debug.hxx> +#include <vcl/event.hxx> +#include <vcl/cursor.hxx> + +//...................................................................................................................... +namespace svt { namespace table +{ +//...................................................................................................................... + + typedef ::rtl::Reference< IMouseFunction > PMouseFunction; + typedef ::std::vector< PMouseFunction > MouseFunctions; + struct DefaultInputHandler_Impl + { + PMouseFunction pActiveFunction; + MouseFunctions aMouseFunctions; + }; + + //================================================================================================================== + //= DefaultInputHandler + //================================================================================================================== + //------------------------------------------------------------------------------------------------------------------ + DefaultInputHandler::DefaultInputHandler() + :m_pImpl( new DefaultInputHandler_Impl ) + { + m_pImpl->aMouseFunctions.push_back( new ColumnResize ); + m_pImpl->aMouseFunctions.push_back( new RowSelection ); + m_pImpl->aMouseFunctions.push_back( new ColumnSortHandler ); + } + + //------------------------------------------------------------------------------------------------------------------ + DefaultInputHandler::~DefaultInputHandler() + { + } + + //------------------------------------------------------------------------------------------------------------------ + namespace + { + bool lcl_delegateMouseEvent( DefaultInputHandler_Impl& i_impl, ITableControl& i_control, const MouseEvent& i_event, + FunctionResult ( IMouseFunction::*i_handlerMethod )( ITableControl&, const MouseEvent& ) ) + { + if ( i_impl.pActiveFunction.is() ) + { + bool furtherHandler = false; + switch ( (i_impl.pActiveFunction.get()->*i_handlerMethod)( i_control, i_event ) ) + { + case ActivateFunction: + OSL_ENSURE( false, "lcl_delegateMouseEvent: unexpected - function already *is* active!" ); + break; + case ContinueFunction: + break; + case DeactivateFunction: + i_impl.pActiveFunction.clear(); + break; + case SkipFunction: + furtherHandler = true; + break; + } + if ( !furtherHandler ) + // handled the event + return true; + } + + // ask all other handlers + bool handled = false; + for ( MouseFunctions::iterator handler = i_impl.aMouseFunctions.begin(); + ( handler != i_impl.aMouseFunctions.end() ) && !handled; + ++handler + ) + { + if ( *handler == i_impl.pActiveFunction ) + // we already invoked this function + continue; + + switch ( (handler->get()->*i_handlerMethod)( i_control, i_event ) ) + { + case ActivateFunction: + i_impl.pActiveFunction = *handler; + handled = true; + break; + case ContinueFunction: + case DeactivateFunction: + OSL_ENSURE( false, "lcl_delegateMouseEvent: unexpected: inactivate handler cannot be continued or deactivated!" ); + break; + case SkipFunction: + handled = false; + break; + } + } + return handled; + } + } + + //------------------------------------------------------------------------------------------------------------------ + bool DefaultInputHandler::MouseMove( ITableControl& i_tableControl, const MouseEvent& i_event ) + { + return lcl_delegateMouseEvent( *m_pImpl, i_tableControl, i_event, &IMouseFunction::handleMouseMove ); + } + + //------------------------------------------------------------------------------------------------------------------ + bool DefaultInputHandler::MouseButtonDown( ITableControl& i_tableControl, const MouseEvent& i_event ) + { + return lcl_delegateMouseEvent( *m_pImpl, i_tableControl, i_event, &IMouseFunction::handleMouseDown ); + } + + //------------------------------------------------------------------------------------------------------------------ + bool DefaultInputHandler::MouseButtonUp( ITableControl& i_tableControl, const MouseEvent& i_event ) + { + return lcl_delegateMouseEvent( *m_pImpl, i_tableControl, i_event, &IMouseFunction::handleMouseUp ); + } + + //------------------------------------------------------------------------------------------------------------------ + bool DefaultInputHandler::KeyInput( ITableControl& _rControl, const KeyEvent& rKEvt ) + { + bool bHandled = false; + + const KeyCode& rKeyCode = rKEvt.GetKeyCode(); + sal_uInt16 nKeyCode = rKeyCode.GetCode(); + + struct _ActionMapEntry + { + sal_uInt16 nKeyCode; + sal_uInt16 nKeyModifier; + TableControlAction eAction; + } + static aKnownActions[] = { + { KEY_DOWN, 0, cursorDown }, + { KEY_UP, 0, cursorUp }, + { KEY_LEFT, 0, cursorLeft }, + { KEY_RIGHT, 0, cursorRight }, + { KEY_HOME, 0, cursorToLineStart }, + { KEY_END, 0, cursorToLineEnd }, + { KEY_PAGEUP, 0, cursorPageUp }, + { KEY_PAGEDOWN, 0, cursorPageDown }, + { KEY_PAGEUP, KEY_MOD1, cursorToFirstLine }, + { KEY_PAGEDOWN, KEY_MOD1, cursorToLastLine }, + { KEY_HOME, KEY_MOD1, cursorTopLeft }, + { KEY_END, KEY_MOD1, cursorBottomRight }, + { KEY_SPACE, KEY_MOD1, cursorSelectRow }, + { KEY_UP, KEY_SHIFT, cursorSelectRowUp }, + { KEY_DOWN, KEY_SHIFT, cursorSelectRowDown }, + { KEY_END, KEY_SHIFT, cursorSelectRowAreaBottom }, + { KEY_HOME, KEY_SHIFT, cursorSelectRowAreaTop }, + + { 0, 0, invalidTableControlAction } + }; + + const _ActionMapEntry* pActions = aKnownActions; + for ( ; pActions->eAction != invalidTableControlAction; ++pActions ) + { + if ( ( pActions->nKeyCode == nKeyCode ) && ( pActions->nKeyModifier == rKeyCode.GetAllModifier() ) ) + { + bHandled = _rControl.dispatchAction( pActions->eAction ); + break; + } + } + + return bHandled; + } + + //------------------------------------------------------------------------------------------------------------------ + bool DefaultInputHandler::GetFocus( ITableControl& _rControl ) + { + _rControl.showCursor(); + return false; // continue processing + } + + //------------------------------------------------------------------------------------------------------------------ + bool DefaultInputHandler::LoseFocus( ITableControl& _rControl ) + { + _rControl.hideCursor(); + return false; // continue processing + } + + //------------------------------------------------------------------------------------------------------------------ + bool DefaultInputHandler::RequestHelp( ITableControl& _rControl, const HelpEvent& _rHEvt ) + { + (void)_rControl; + (void)_rHEvt; + // TODO + return false; + } + + //------------------------------------------------------------------------------------------------------------------ + bool DefaultInputHandler::Command( ITableControl& _rControl, const CommandEvent& _rCEvt ) + { + (void)_rControl; + (void)_rCEvt; + // TODO + return false; + } + + //------------------------------------------------------------------------------------------------------------------ + bool DefaultInputHandler::PreNotify( ITableControl& _rControl, NotifyEvent& _rNEvt ) + { + (void)_rControl; + (void)_rNEvt; + // TODO + return false; + } + + //------------------------------------------------------------------------------------------------------------------ + bool DefaultInputHandler::Notify( ITableControl& _rControl, NotifyEvent& _rNEvt ) + { + (void)_rControl; + (void)_rNEvt; + // TODO + return false; + } +//...................................................................................................................... +} } // namespace svt::table +//...................................................................................................................... + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svtools/source/table/gridtablerenderer.cxx b/svtools/source/table/gridtablerenderer.cxx new file mode 100644 index 000000000000..e69ad5589087 --- /dev/null +++ b/svtools/source/table/gridtablerenderer.cxx @@ -0,0 +1,621 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * +************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_svtools.hxx" + +#include "cellvalueconversion.hxx" +#include "svtools/table/gridtablerenderer.hxx" +#include "svtools/colorcfg.hxx" + +/** === begin UNO includes === **/ +#include <com/sun/star/graphic/XGraphic.hpp> +/** === end UNO includes === **/ + +#include <tools/debug.hxx> +#include <tools/diagnose_ex.h> +#include <vcl/window.hxx> +#include <vcl/image.hxx> +#include <vcl/virdev.hxx> +#include <vcl/decoview.hxx> + +//...................................................................................................................... +namespace svt { namespace table +{ +//...................................................................................................................... + + /** === begin UNO using === **/ + using ::com::sun::star::uno::Any; + using ::com::sun::star::uno::Reference; + using ::com::sun::star::uno::UNO_QUERY; + using ::com::sun::star::uno::XInterface; + using ::com::sun::star::uno::TypeClass_INTERFACE; + using ::com::sun::star::graphic::XGraphic; + using ::com::sun::star::style::HorizontalAlignment; + using ::com::sun::star::style::HorizontalAlignment_LEFT; + using ::com::sun::star::style::HorizontalAlignment_CENTER; + using ::com::sun::star::style::HorizontalAlignment_RIGHT; + using ::com::sun::star::style::VerticalAlignment; + using ::com::sun::star::style::VerticalAlignment_TOP; + using ::com::sun::star::style::VerticalAlignment_MIDDLE; + using ::com::sun::star::style::VerticalAlignment_BOTTOM; + /** === end UNO using === **/ + + //================================================================================================================== + //= CachedSortIndicator + //================================================================================================================== + class CachedSortIndicator + { + public: + CachedSortIndicator() + :m_lastHeaderHeight( 0 ) + ,m_lastArrowColor( COL_TRANSPARENT ) + { + } + + BitmapEx const & getBitmapFor( OutputDevice const & i_device, long const i_headerHeight, StyleSettings const & i_style, bool const i_sortAscending ); + + private: + long m_lastHeaderHeight; + Color m_lastArrowColor; + BitmapEx m_sortAscending; + BitmapEx m_sortDescending; + }; + + //------------------------------------------------------------------------------------------------------------------ + BitmapEx const & CachedSortIndicator::getBitmapFor( OutputDevice const & i_device, long const i_headerHeight, + StyleSettings const & i_style, bool const i_sortAscending ) + { + BitmapEx & rBitmap( i_sortAscending ? m_sortAscending : m_sortDescending ); + if ( !rBitmap || ( i_headerHeight != m_lastHeaderHeight ) || ( i_style.GetActiveColor() != m_lastArrowColor ) ) + { + long const nSortIndicatorWidth = 2 * i_headerHeight / 3; + long const nSortIndicatorHeight = 2 * nSortIndicatorWidth / 3; + + Point const aBitmapPos( 0, 0 ); + Size const aBitmapSize( nSortIndicatorWidth, nSortIndicatorHeight ); + VirtualDevice aDevice( i_device, 0, 0 ); + aDevice.SetOutputSizePixel( aBitmapSize ); + + DecorationView aDecoView( &aDevice ); + aDecoView.DrawSymbol( + Rectangle( aBitmapPos, aBitmapSize ), + i_sortAscending ? SYMBOL_SPIN_UP : SYMBOL_SPIN_DOWN, + i_style.GetActiveColor() + ); + + rBitmap = aDevice.GetBitmapEx( aBitmapPos, aBitmapSize ); + m_lastHeaderHeight = i_headerHeight; + m_lastArrowColor = i_style.GetActiveColor(); + } + return rBitmap; + } + + //================================================================================================================== + //= GridTableRenderer_Impl + //================================================================================================================== + struct GridTableRenderer_Impl + { + ITableModel& rModel; + RowPos nCurrentRow; + bool bUseGridLines; + CachedSortIndicator aSortIndicator; + + GridTableRenderer_Impl( ITableModel& _rModel ) + :rModel( _rModel ) + ,nCurrentRow( ROW_INVALID ) + ,bUseGridLines( true ) + { + } + }; + + //================================================================================================================== + //= helper + //================================================================================================================== + namespace + { + static Rectangle lcl_getContentArea( GridTableRenderer_Impl const & i_impl, Rectangle const & i_cellArea ) + { + Rectangle aContentArea( i_cellArea ); + if ( i_impl.bUseGridLines ) + { + --aContentArea.Right(); + --aContentArea.Bottom(); + } + return aContentArea; + } + static Rectangle lcl_getTextRenderingArea( Rectangle const & i_contentArea ) + { + Rectangle aTextArea( i_contentArea ); + aTextArea.Left() += 2; aTextArea.Right() -= 2; + ++aTextArea.Top(); --aTextArea.Bottom(); + return aTextArea; + } + + static sal_uLong lcl_getAlignmentTextDrawFlags( GridTableRenderer_Impl const & i_impl, ColPos const i_columnPos ) + { + sal_uLong nVertFlag = TEXT_DRAW_TOP; + VerticalAlignment const eVertAlign = i_impl.rModel.getVerticalAlign(); + switch ( eVertAlign ) + { + case VerticalAlignment_MIDDLE: nVertFlag = TEXT_DRAW_VCENTER; break; + case VerticalAlignment_BOTTOM: nVertFlag = TEXT_DRAW_BOTTOM; break; + default: + break; + } + + sal_uLong nHorzFlag = TEXT_DRAW_LEFT; + HorizontalAlignment const eHorzAlign = i_impl.rModel.getColumnModel( i_columnPos )->getHorizontalAlign(); + switch ( eHorzAlign ) + { + case HorizontalAlignment_CENTER: nHorzFlag = TEXT_DRAW_CENTER; break; + case HorizontalAlignment_RIGHT: nHorzFlag = TEXT_DRAW_RIGHT; break; + default: + break; + } + + return nVertFlag | nHorzFlag; + } + + } + + //================================================================================================================== + //= GridTableRenderer + //================================================================================================================== + //------------------------------------------------------------------------------------------------------------------ + GridTableRenderer::GridTableRenderer( ITableModel& _rModel ) + :m_pImpl( new GridTableRenderer_Impl( _rModel ) ) + { + } + + //------------------------------------------------------------------------------------------------------------------ + GridTableRenderer::~GridTableRenderer() + { + } + + //------------------------------------------------------------------------------------------------------------------ + RowPos GridTableRenderer::getCurrentRow() const + { + return m_pImpl->nCurrentRow; + } + + //------------------------------------------------------------------------------------------------------------------ + bool GridTableRenderer::useGridLines() const + { + return m_pImpl->bUseGridLines; + } + + //------------------------------------------------------------------------------------------------------------------ + void GridTableRenderer::useGridLines( bool const i_use ) + { + m_pImpl->bUseGridLines = i_use; + } + + //------------------------------------------------------------------------------------------------------------------ + namespace + { + Color lcl_getEffectiveColor( + ::boost::optional< ::Color > const & i_modelColor, + StyleSettings const & i_styleSettings, + ::Color const & ( StyleSettings::*i_getDefaultColor ) () const + ) + { + if ( !!i_modelColor ) + return *i_modelColor; + return ( i_styleSettings.*i_getDefaultColor )(); + } + } + + //------------------------------------------------------------------------------------------------------------------ + void GridTableRenderer::PaintHeaderArea( + OutputDevice& _rDevice, const Rectangle& _rArea, bool _bIsColHeaderArea, bool _bIsRowHeaderArea, + const StyleSettings& _rStyle ) + { + OSL_PRECOND( _bIsColHeaderArea || _bIsRowHeaderArea, + "GridTableRenderer::PaintHeaderArea: invalid area flags!" ); + + _rDevice.Push( PUSH_FILLCOLOR | PUSH_LINECOLOR ); + + Color const background = lcl_getEffectiveColor( m_pImpl->rModel.getHeaderBackgroundColor(), _rStyle, &StyleSettings::GetDialogColor ); + _rDevice.SetFillColor( background ); + + _rDevice.SetLineColor(); + _rDevice.DrawRect( _rArea ); + + // delimiter lines at bottom/right + ::boost::optional< ::Color > aLineColor( m_pImpl->rModel.getLineColor() ); + ::Color const lineColor = !aLineColor ? _rStyle.GetSeparatorColor() : *aLineColor; + _rDevice.SetLineColor( lineColor ); + _rDevice.DrawLine( _rArea.BottomLeft(), _rArea.BottomRight() ); + _rDevice.DrawLine( _rArea.BottomRight(), _rArea.TopRight() ); + + _rDevice.Pop(); + (void)_bIsColHeaderArea; + (void)_bIsRowHeaderArea; + } + + //------------------------------------------------------------------------------------------------------------------ + void GridTableRenderer::PaintColumnHeader( ColPos _nCol, bool _bActive, bool _bSelected, + OutputDevice& _rDevice, const Rectangle& _rArea, const StyleSettings& _rStyle ) + { + _rDevice.Push( PUSH_LINECOLOR); + + String sHeaderText; + PColumnModel const pColumn = m_pImpl->rModel.getColumnModel( _nCol ); + DBG_ASSERT( !!pColumn, "GridTableRenderer::PaintColumnHeader: invalid column model object!" ); + if ( !!pColumn ) + sHeaderText = pColumn->getName(); + + ::Color const textColor = lcl_getEffectiveColor( m_pImpl->rModel.getTextColor(), _rStyle, &StyleSettings::GetFieldTextColor ); + _rDevice.SetTextColor( textColor ); + + Rectangle const aTextRect( lcl_getTextRenderingArea( lcl_getContentArea( *m_pImpl, _rArea ) ) ); + sal_uLong const nDrawTextFlags = lcl_getAlignmentTextDrawFlags( *m_pImpl, _nCol ) | TEXT_DRAW_CLIP; + _rDevice.DrawText( aTextRect, sHeaderText, nDrawTextFlags ); + + ::boost::optional< ::Color > const aLineColor( m_pImpl->rModel.getLineColor() ); + ::Color const lineColor = !aLineColor ? _rStyle.GetSeparatorColor() : *aLineColor; + _rDevice.SetLineColor( lineColor ); + _rDevice.DrawLine( _rArea.BottomRight(), _rArea.TopRight()); + _rDevice.DrawLine( _rArea.BottomLeft(), _rArea.BottomRight() ); + + // draw sort indicator if the model data is sorted by the given column + ITableDataSort const * pSortAdapter = m_pImpl->rModel.getSortAdapter(); + ColumnSort aCurrentSortOrder; + if ( pSortAdapter != NULL ) + aCurrentSortOrder = pSortAdapter->getCurrentSortOrder(); + if ( aCurrentSortOrder.nColumnPos == _nCol ) + { + long const nHeaderHeight( _rArea.GetHeight() ); + BitmapEx const aIndicatorBitmap = m_pImpl->aSortIndicator.getBitmapFor( _rDevice, nHeaderHeight, _rStyle, + aCurrentSortOrder.eSortDirection == ColumnSortAscending ); + Size const aBitmapSize( aIndicatorBitmap.GetSizePixel() ); + long const nSortIndicatorPaddingX = 2; + long const nSortIndicatorPaddingY = ( nHeaderHeight - aBitmapSize.Height() ) / 2; + + if ( ( nDrawTextFlags & TEXT_DRAW_RIGHT ) != 0 ) + { + // text is right aligned => draw the sort indicator at the left hand side + _rDevice.DrawBitmapEx( + Point( _rArea.Left() + nSortIndicatorPaddingX, _rArea.Top() + nSortIndicatorPaddingY ), + aIndicatorBitmap + ); + } + else + { + // text is left-aligned or centered => draw the sort indicator at the right hand side + _rDevice.DrawBitmapEx( + Point( _rArea.Right() - nSortIndicatorPaddingX - aBitmapSize.Width(), nSortIndicatorPaddingY ), + aIndicatorBitmap + ); + } + } + + _rDevice.Pop(); + + (void)_bActive; + // no special painting for the active column at the moment + + (void)_bSelected; + // selection for column header not yet implemented + } + + //------------------------------------------------------------------------------------------------------------------ + void GridTableRenderer::PrepareRow( RowPos _nRow, bool _bActive, bool _bSelected, + OutputDevice& _rDevice, const Rectangle& _rRowArea, const StyleSettings& _rStyle ) + { + // remember the row for subsequent calls to the other ->ITableRenderer methods + m_pImpl->nCurrentRow = _nRow; + + _rDevice.Push( PUSH_FILLCOLOR | PUSH_LINECOLOR); + + ::Color backgroundColor = _rStyle.GetFieldColor(); + + ::boost::optional< ::Color > aLineColor( m_pImpl->rModel.getLineColor() ); + ::Color lineColor = !aLineColor ? _rStyle.GetSeparatorColor() : *aLineColor; + + if ( _bSelected ) + { + // selected rows use the background color from the style + backgroundColor = _rStyle.GetHighlightColor(); + if ( !aLineColor ) + lineColor = backgroundColor; + } + else + { + ::boost::optional< ::std::vector< ::Color > > aRowColors = m_pImpl->rModel.getRowBackgroundColors(); + if ( !aRowColors ) + { + // use alternating default colors + Color const fieldColor = _rStyle.GetFieldColor(); + if ( _rStyle.GetHighContrastMode() || ( ( m_pImpl->nCurrentRow % 2 ) == 0 ) ) + { + backgroundColor = fieldColor; + } + else + { + Color hilightColor = _rStyle.GetHighlightColor(); + hilightColor.SetRed( 9 * ( fieldColor.GetRed() - hilightColor.GetRed() ) / 10 + hilightColor.GetRed() ); + hilightColor.SetGreen( 9 * ( fieldColor.GetGreen() - hilightColor.GetGreen() ) / 10 + hilightColor.GetGreen() ); + hilightColor.SetBlue( 9 * ( fieldColor.GetBlue() - hilightColor.GetBlue() ) / 10 + hilightColor.GetBlue() ); + backgroundColor = hilightColor; + } + } + else + { + if ( aRowColors->empty() ) + { + // all colors have the same background color + backgroundColor = _rStyle.GetFieldColor(); + } + else + { + backgroundColor = aRowColors->at( m_pImpl->nCurrentRow % aRowColors->size() ); + } + } + } + + //m_pImpl->bUseGridLines ? _rDevice.SetLineColor( lineColor ) : _rDevice.SetLineColor(); + _rDevice.SetLineColor(); + _rDevice.SetFillColor( backgroundColor ); + _rDevice.DrawRect( _rRowArea ); + + _rDevice.Pop(); + + (void)_bActive; + // row containing the active cell not rendered any special at the moment + } + + //------------------------------------------------------------------------------------------------------------------ + void GridTableRenderer::PaintRowHeader( bool _bActive, bool _bSelected, OutputDevice& _rDevice, const Rectangle& _rArea, + const StyleSettings& _rStyle ) + { + _rDevice.Push( PUSH_LINECOLOR | PUSH_TEXTCOLOR ); + + ::boost::optional< ::Color > const aLineColor( m_pImpl->rModel.getLineColor() ); + ::Color const lineColor = !aLineColor ? _rStyle.GetSeparatorColor() : *aLineColor; + _rDevice.SetLineColor( lineColor ); + _rDevice.DrawLine( _rArea.BottomLeft(), _rArea.BottomRight() ); + + Any const rowHeading( m_pImpl->rModel.getRowHeading( m_pImpl->nCurrentRow ) ); + ::rtl::OUString const rowTitle( CellValueConversion::convertToString( rowHeading ) ); + if ( rowTitle.getLength() ) + { + ::Color const textColor = lcl_getEffectiveColor( m_pImpl->rModel.getHeaderTextColor(), _rStyle, &StyleSettings::GetFieldTextColor ); + _rDevice.SetTextColor( textColor ); + + Rectangle const aTextRect( lcl_getTextRenderingArea( lcl_getContentArea( *m_pImpl, _rArea ) ) ); + sal_uLong const nDrawTextFlags = lcl_getAlignmentTextDrawFlags( *m_pImpl, 0 ) | TEXT_DRAW_CLIP; + // TODO: is using the horizontal alignment of the 0'th column a good idea here? This is pretty ... arbitray .. + _rDevice.DrawText( aTextRect, rowTitle, nDrawTextFlags ); + } + + // TODO: active? selected? + (void)_bActive; + (void)_bSelected; + _rDevice.Pop(); + } + + //------------------------------------------------------------------------------------------------------------------ + struct GridTableRenderer::CellRenderContext + { + OutputDevice& rDevice; + Rectangle const aContentArea; + StyleSettings const & rStyle; + ColPos const nColumn; + bool const bSelected; + + CellRenderContext( OutputDevice& i_device, Rectangle const & i_contentArea, + StyleSettings const & i_style, ColPos const i_column, bool const i_selected ) + :rDevice( i_device ) + ,aContentArea( i_contentArea ) + ,rStyle( i_style ) + ,nColumn( i_column ) + ,bSelected( i_selected ) + { + } + }; + + //------------------------------------------------------------------------------------------------------------------ + void GridTableRenderer::PaintCell( ColPos const i_column, bool _bSelected, bool _bActive, + OutputDevice& _rDevice, const Rectangle& _rArea, const StyleSettings& _rStyle ) + { + _rDevice.Push( PUSH_LINECOLOR | PUSH_FILLCOLOR ); + + Rectangle const aContentArea( lcl_getContentArea( *m_pImpl, _rArea ) ); + CellRenderContext const aRenderContext( _rDevice, aContentArea, _rStyle, i_column, _bSelected ); + impl_paintCellContent( aRenderContext ); + + if ( m_pImpl->bUseGridLines ) + { + ::boost::optional< ::Color > aLineColor( m_pImpl->rModel.getLineColor() ); + ::Color lineColor = !aLineColor ? _rStyle.GetSeparatorColor() : *aLineColor; + + if ( _bSelected && !aLineColor ) + { + // if no line color is specified by the model, use the usual selection color for lines in selected cells + lineColor = _rStyle.GetHighlightColor(); + } + + _rDevice.SetLineColor( lineColor ); + _rDevice.DrawLine( _rArea.BottomLeft(), _rArea.BottomRight() ); + _rDevice.DrawLine( _rArea.BottomRight(), _rArea.TopRight() ); + } + + _rDevice.Pop(); + + (void)_bActive; + // no special painting for the active cell at the moment + } + + //------------------------------------------------------------------------------------------------------------------ + void GridTableRenderer::impl_paintCellImage( CellRenderContext const & i_context, Image const & i_image ) + { + Point imagePos( Point( i_context.aContentArea.Left(), i_context.aContentArea.Top() ) ); + Size imageSize = i_image.GetSizePixel(); + if ( i_context.aContentArea.GetWidth() > imageSize.Width() ) + { + const HorizontalAlignment eHorzAlign = m_pImpl->rModel.getColumnModel( i_context.nColumn )->getHorizontalAlign(); + switch ( eHorzAlign ) + { + case HorizontalAlignment_CENTER: + imagePos.X() += ( i_context.aContentArea.GetWidth() - imageSize.Width() ) / 2; + break; + case HorizontalAlignment_RIGHT: + imagePos.X() = i_context.aContentArea.Right() - imageSize.Width(); + break; + default: + break; + } + + } + else + imageSize.Width() = i_context.aContentArea.GetWidth(); + + if ( i_context.aContentArea.GetHeight() > imageSize.Height() ) + { + const VerticalAlignment eVertAlign = m_pImpl->rModel.getVerticalAlign(); + switch ( eVertAlign ) + { + case VerticalAlignment_MIDDLE: + imagePos.Y() += ( i_context.aContentArea.GetHeight() - imageSize.Height() ) / 2; + break; + case VerticalAlignment_BOTTOM: + imagePos.Y() = i_context.aContentArea.Bottom() - imageSize.Height(); + break; + default: + break; + } + } + else + imageSize.Height() = i_context.aContentArea.GetHeight() - 1; + + i_context.rDevice.DrawImage( imagePos, imageSize, i_image, 0 ); + } + + //------------------------------------------------------------------------------------------------------------------ + void GridTableRenderer::impl_paintCellContent( CellRenderContext const & i_context ) + { + Any aCellContent; + m_pImpl->rModel.getCellContent( i_context.nColumn, m_pImpl->nCurrentRow, aCellContent ); + + if ( aCellContent.getValueTypeClass() == TypeClass_INTERFACE ) + { + Reference< XInterface > const xContentInterface( aCellContent, UNO_QUERY ); + if ( !xContentInterface.is() ) + // allowed. kind of. + return; + + Reference< XGraphic > const xGraphic( aCellContent, UNO_QUERY ); + ENSURE_OR_RETURN_VOID( xGraphic.is(), "GridTableRenderer::impl_paintCellContent: only XGraphic interfaces (or NULL) are supported for painting." ); + + const Image aImage( xGraphic ); + impl_paintCellImage( i_context, aImage ); + return; + } + + const ::rtl::OUString sText( CellValueConversion::convertToString( aCellContent ) ); + impl_paintCellText( i_context, sText ); + } + + //------------------------------------------------------------------------------------------------------------------ + void GridTableRenderer::impl_paintCellText( CellRenderContext const & i_context, ::rtl::OUString const & i_text ) + { + if ( i_context.bSelected ) + i_context.rDevice.SetTextColor( i_context.rStyle.GetHighlightTextColor() ); + else + { + ::Color const textColor = lcl_getEffectiveColor( m_pImpl->rModel.getTextColor(), i_context.rStyle, &StyleSettings::GetFieldTextColor ); + i_context.rDevice.SetTextColor( textColor ); + } + + Rectangle const textRect( lcl_getTextRenderingArea( i_context.aContentArea ) ); + sal_uLong const nDrawTextFlags = lcl_getAlignmentTextDrawFlags( *m_pImpl, i_context.nColumn ) | TEXT_DRAW_CLIP; + i_context.rDevice.DrawText( textRect, i_text, nDrawTextFlags ); + } + + //------------------------------------------------------------------------------------------------------------------ + void GridTableRenderer::ShowCellCursor( Window& _rView, const Rectangle& _rCursorRect) + { + _rView.ShowFocus( _rCursorRect ); + } + + //------------------------------------------------------------------------------------------------------------------ + void GridTableRenderer::HideCellCursor( Window& _rView, const Rectangle& _rCursorRect) + { + (void)_rCursorRect; + _rView.HideFocus(); + } + + //------------------------------------------------------------------------------------------------------------------ + bool GridTableRenderer::FitsIntoCell( Any const & i_cellContent, ColPos const i_colPos, RowPos const i_rowPos, + bool const i_active, bool const i_selected, OutputDevice& i_targetDevice, Rectangle const & i_targetArea ) + { + if ( !i_cellContent.hasValue() ) + return true; + + if ( i_cellContent.getValueTypeClass() == TypeClass_INTERFACE ) + { + Reference< XInterface > const xContentInterface( i_cellContent, UNO_QUERY ); + if ( !xContentInterface.is() ) + return true; + + Reference< XGraphic > const xGraphic( i_cellContent, UNO_QUERY ); + if ( xGraphic.is() ) + // for the moment, assume it fits. We can always scale it down during painting ... + return true; + + OSL_ENSURE( false, "GridTableRenderer::FitsIntoCell: only XGraphic interfaces (or NULL) are supported for painting." ); + return true; + } + + ::rtl::OUString const sText( CellValueConversion::convertToString( i_cellContent ) ); + if ( sText.getLength() == 0 ) + return true; + + Rectangle const aTargetArea( lcl_getTextRenderingArea( lcl_getContentArea( *m_pImpl, i_targetArea ) ) ); + + long const nTextHeight = i_targetDevice.GetTextHeight(); + if ( nTextHeight > aTargetArea.GetHeight() ) + return false; + + long const nTextWidth = i_targetDevice.GetTextWidth( sText ); + if ( nTextWidth > aTargetArea.GetWidth() ) + return false; + + OSL_UNUSED( i_active ); + OSL_UNUSED( i_selected ); + OSL_UNUSED( i_rowPos ); + OSL_UNUSED( i_colPos ); + return true; + } + +//...................................................................................................................... +} } // namespace svt::table +//...................................................................................................................... + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svtools/source/table/mousefunction.cxx b/svtools/source/table/mousefunction.cxx new file mode 100644 index 000000000000..20d505e911e9 --- /dev/null +++ b/svtools/source/table/mousefunction.cxx @@ -0,0 +1,320 @@ +/************************************************************************* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#include "precompiled_svtools.hxx" + +#include "mousefunction.hxx" +#include "svtools/table/tablecontrolinterface.hxx" + +#include <tools/diagnose_ex.h> +#include <vcl/window.hxx> + +//...................................................................................................................... +namespace svt { namespace table +{ +//...................................................................................................................... + + //================================================================================================================== + //= MouseFunction + //================================================================================================================== + //------------------------------------------------------------------------------------------------------------------ + oslInterlockedCount MouseFunction::acquire() + { + return osl_incrementInterlockedCount( &m_refCount ); + } + + //------------------------------------------------------------------------------------------------------------------ + oslInterlockedCount MouseFunction::release() + { + oslInterlockedCount newCount = osl_decrementInterlockedCount( &m_refCount ); + if ( newCount == 0 ) + { + delete this; + return 0; + } + return newCount; + } + + //================================================================================================================== + //= ColumnResize + //================================================================================================================== + //------------------------------------------------------------------------------------------------------------------ + FunctionResult ColumnResize::handleMouseMove( ITableControl& i_tableControl, MouseEvent const & i_event ) + { + Point const aPoint = i_event.GetPosPixel(); + + if ( m_nResizingColumn == COL_INVALID ) + { + // if we hit a column divider, change the mosue pointer accordingly + Pointer aNewPointer( POINTER_ARROW ); + TableCell const tableCell = i_tableControl.hitTest( aPoint ); + if ( ( tableCell.nRow == ROW_COL_HEADERS ) && ( tableCell.eArea == ColumnDivider ) ) + { + aNewPointer = Pointer( POINTER_HSPLIT ); + } + i_tableControl.setPointer( aNewPointer ); + + return SkipFunction; // TODO: is this correct? + } + + ::Size const tableSize = i_tableControl.getTableSizePixel(); + + // set proper pointer + Pointer aNewPointer( POINTER_ARROW ); + ColumnMetrics const & columnMetrics( i_tableControl.getColumnMetrics( m_nResizingColumn ) ); + if ( ( aPoint.X() > tableSize.Width() ) + || ( aPoint.X() < columnMetrics.nStartPixel ) + ) + { + aNewPointer = Pointer( POINTER_NOTALLOWED ); + } + else + { + aNewPointer = Pointer( POINTER_HSPLIT ); + } + i_tableControl.setPointer( aNewPointer ); + + // show tracking line + i_tableControl.hideTracking(); + i_tableControl.showTracking( + Rectangle( + Point( aPoint.X(), 0 ), + Size( 1, tableSize.Height() ) + ), + SHOWTRACK_SPLIT | SHOWTRACK_WINDOW + ); + + (void)i_event; + return ContinueFunction; + } + + //------------------------------------------------------------------------------------------------------------------ + FunctionResult ColumnResize::handleMouseDown( ITableControl& i_tableControl, MouseEvent const & i_event ) + { + if ( m_nResizingColumn != COL_INVALID ) + { + OSL_ENSURE( false, "ColumnResize::handleMouseDown: suspicious: MouseButtonDown while still tracking?" ); + return ContinueFunction; + } + + TableCell const tableCell( i_tableControl.hitTest( i_event.GetPosPixel() ) ); + if ( tableCell.nRow == ROW_COL_HEADERS ) + { + if ( ( tableCell.nColumn != COL_INVALID ) + && ( tableCell.eArea == ColumnDivider ) + ) + { + m_nResizingColumn = tableCell.nColumn; + i_tableControl.captureMouse(); + return ActivateFunction; + } + } + + return SkipFunction; + } + + //------------------------------------------------------------------------------------------------------------------ + FunctionResult ColumnResize::handleMouseUp( ITableControl& i_tableControl, MouseEvent const & i_event ) + { + if ( m_nResizingColumn == COL_INVALID ) + return SkipFunction; + + Point const aPoint = i_event.GetPosPixel(); + + i_tableControl.hideTracking(); + PColumnModel const pColumn = i_tableControl.getModel()->getColumnModel( m_nResizingColumn ); + long const maxWidthLogical = pColumn->getMaxWidth(); + long const minWidthLogical = pColumn->getMinWidth(); + + // new position of mouse + long const requestedEnd = aPoint.X(); + + // old position of right border + long const oldEnd = i_tableControl.getColumnMetrics( m_nResizingColumn ).nEndPixel; + + // position of left border if cursor in the to-be-resized column + long const columnStart = i_tableControl.getColumnMetrics( m_nResizingColumn ).nStartPixel; + long const requestedWidth = requestedEnd - columnStart; + // TODO: this is not correct, strictly: It assumes that the mouse was pressed exactly on the "end" pos, + // but for a while now, we have relaxed this, and allow clicking a few pixels aside, too + + if ( requestedEnd >= columnStart ) + { + long requestedWidthLogical = i_tableControl.pixelWidthToAppFont( requestedWidth ); + // respect column width limits + if ( oldEnd > requestedEnd ) + { + // column has become smaller, check against minimum width + if ( ( minWidthLogical != 0 ) && ( requestedWidthLogical < minWidthLogical ) ) + requestedWidthLogical = minWidthLogical; + } + else if ( oldEnd < requestedEnd ) + { + // column has become larger, check against max width + if ( ( maxWidthLogical != 0 ) && ( requestedWidthLogical >= maxWidthLogical ) ) + requestedWidthLogical = maxWidthLogical; + } + pColumn->setWidth( requestedWidthLogical ); + i_tableControl.invalidate( TableAreaAll ); + } + + i_tableControl.setPointer( Pointer() ); + i_tableControl.releaseMouse(); + + m_nResizingColumn = COL_INVALID; + return DeactivateFunction; + } + + //================================================================================================================== + //= RowSelection + //================================================================================================================== + //------------------------------------------------------------------------------------------------------------------ + FunctionResult RowSelection::handleMouseMove( ITableControl& i_tableControl, MouseEvent const & i_event ) + { + OSL_UNUSED( i_tableControl ); + OSL_UNUSED( i_event ); + return SkipFunction; + } + + //------------------------------------------------------------------------------------------------------------------ + FunctionResult RowSelection::handleMouseDown( ITableControl& i_tableControl, MouseEvent const & i_event ) + { + bool handled = false; + + TableCell const tableCell( i_tableControl.hitTest( i_event.GetPosPixel() ) ); + if ( tableCell.nRow >= 0 ) + { + bool bSetCursor = false; + if ( i_tableControl.getSelEngine()->GetSelectionMode() == NO_SELECTION ) + { + bSetCursor = true; + } + else + { + if ( !i_tableControl.isRowSelected( tableCell.nRow ) ) + { + handled = i_tableControl.getSelEngine()->SelMouseButtonDown( i_event ); + } + else + { + bSetCursor = true; + } + } + + if ( bSetCursor ) + { + i_tableControl.activateCell( tableCell.nColumn, tableCell.nRow ); + handled = true; + } + } + + if ( handled ) + m_bActive = true; + return handled ? ActivateFunction : SkipFunction; + } + + //------------------------------------------------------------------------------------------------------------------ + FunctionResult RowSelection::handleMouseUp( ITableControl& i_tableControl, MouseEvent const & i_event ) + { + TableCell const tableCell = i_tableControl.hitTest( i_event.GetPosPixel() ); + if ( tableCell.nRow >= 0 ) + { + if ( i_tableControl.getSelEngine()->GetSelectionMode() != NO_SELECTION ) + { + i_tableControl.getSelEngine()->SelMouseButtonUp( i_event ); + } + } + if ( m_bActive ) + { + m_bActive = false; + return DeactivateFunction; + } + return SkipFunction; + } + + //================================================================================================================== + //= ColumnSortHandler + //================================================================================================================== + //------------------------------------------------------------------------------------------------------------------ + FunctionResult ColumnSortHandler::handleMouseMove( ITableControl& i_tableControl, MouseEvent const & i_event ) + { + OSL_UNUSED( i_tableControl ); + OSL_UNUSED( i_event ); + return SkipFunction; + } + + //------------------------------------------------------------------------------------------------------------------ + FunctionResult ColumnSortHandler::handleMouseDown( ITableControl& i_tableControl, MouseEvent const & i_event ) + { + if ( m_nActiveColumn != COL_INVALID ) + { + OSL_ENSURE( false, "ColumnSortHandler::handleMouseDown: called while already active - suspicious!" ); + return ContinueFunction; + } + + if ( i_tableControl.getModel()->getSortAdapter() == NULL ) + // no sorting support at the model + return SkipFunction; + + TableCell const tableCell( i_tableControl.hitTest( i_event.GetPosPixel() ) ); + if ( ( tableCell.nRow != ROW_COL_HEADERS ) || ( tableCell.nColumn < 0 ) ) + return SkipFunction; + + // TODO: ensure the column header is rendered in some special way, indicating its current state + + m_nActiveColumn = tableCell.nColumn; + return ActivateFunction; + } + + //------------------------------------------------------------------------------------------------------------------ + FunctionResult ColumnSortHandler::handleMouseUp( ITableControl& i_tableControl, MouseEvent const & i_event ) + { + if ( m_nActiveColumn == COL_INVALID ) + return SkipFunction; + + TableCell const tableCell( i_tableControl.hitTest( i_event.GetPosPixel() ) ); + if ( ( tableCell.nRow == ROW_COL_HEADERS ) && ( tableCell.nColumn == m_nActiveColumn ) ) + { + ITableDataSort* pSort = i_tableControl.getModel()->getSortAdapter(); + ENSURE_OR_RETURN( pSort != NULL, "ColumnSortHandler::handleMouseUp: somebody is mocking with us!", DeactivateFunction ); + // in handleMousButtonDown, the model claimed to have sort support ... + + ColumnSortDirection eSortDirection = ColumnSortAscending; + ColumnSort const aCurrentSort = pSort->getCurrentSortOrder(); + if ( aCurrentSort.nColumnPos == m_nActiveColumn ) + // invert existing sort order + eSortDirection = ( aCurrentSort.eSortDirection == ColumnSortAscending ) ? ColumnSortDescending : ColumnSortAscending; + + pSort->sortByColumn( m_nActiveColumn, eSortDirection ); + } + + m_nActiveColumn = COL_INVALID; + return DeactivateFunction; + } + +//...................................................................................................................... +} } // namespace svt::table +//...................................................................................................................... diff --git a/svtools/source/table/mousefunction.hxx b/svtools/source/table/mousefunction.hxx new file mode 100644 index 000000000000..2149026a0923 --- /dev/null +++ b/svtools/source/table/mousefunction.hxx @@ -0,0 +1,161 @@ +/************************************************************************* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef SVTOOLS_MOUSEFUNCTION_HXX +#define SVTOOLS_MOUSEFUNCTION_HXX + +#include "svtools/table/tabletypes.hxx" + +#include <rtl/ref.hxx> + +#include <boost/noncopyable.hpp> + +class MouseEvent; + +//...................................................................................................................... +namespace svt { namespace table +{ +//...................................................................................................................... + + class ITableControl; + + //================================================================================================================== + //= FunctionResult + //================================================================================================================== + enum FunctionResult + { + ActivateFunction, + ContinueFunction, + DeactivateFunction, + + SkipFunction + }; + + //================================================================================================================== + //= IMouseFunction + //================================================================================================================== + class IMouseFunction : public ::rtl::IReference, public ::boost::noncopyable + { + public: + virtual FunctionResult handleMouseMove( ITableControl& i_tableControl, MouseEvent const & i_event ) = 0; + virtual FunctionResult handleMouseDown( ITableControl& i_tableControl, MouseEvent const & i_event ) = 0; + virtual FunctionResult handleMouseUp( ITableControl& i_tableControl, MouseEvent const & i_event ) = 0; + + protected: + virtual ~IMouseFunction() { } + }; + + //================================================================================================================== + //= MouseFunction + //================================================================================================================== + class MouseFunction : public IMouseFunction + { + public: + MouseFunction() + :m_refCount( 0 ) + { + } + protected: + ~MouseFunction() + { + } + + public: + virtual oslInterlockedCount SAL_CALL acquire(); + virtual oslInterlockedCount SAL_CALL release(); + + private: + oslInterlockedCount m_refCount; + }; + + //================================================================================================================== + //= ColumnResize + //================================================================================================================== + class ColumnResize : public MouseFunction + { + public: + ColumnResize() + :m_nResizingColumn( COL_INVALID ) + { + } + + public: + // IMouseFunction + virtual FunctionResult handleMouseMove( ITableControl& i_tableControl, MouseEvent const & i_event ); + virtual FunctionResult handleMouseDown( ITableControl& i_tableControl, MouseEvent const & i_event ); + virtual FunctionResult handleMouseUp( ITableControl& i_tableControl, MouseEvent const & i_event ); + + private: + ColPos m_nResizingColumn; + }; + + //================================================================================================================== + //= RowSelection + //================================================================================================================== + class RowSelection : public MouseFunction + { + public: + RowSelection() + :m_bActive( false ) + { + } + + public: + // IMouseFunction + virtual FunctionResult handleMouseMove( ITableControl& i_tableControl, MouseEvent const & i_event ); + virtual FunctionResult handleMouseDown( ITableControl& i_tableControl, MouseEvent const & i_event ); + virtual FunctionResult handleMouseUp( ITableControl& i_tableControl, MouseEvent const & i_event ); + + private: + bool m_bActive; + }; + + //================================================================================================================== + //= ColumnSortHandler + //================================================================================================================== + class ColumnSortHandler : public MouseFunction + { + public: + ColumnSortHandler() + :m_nActiveColumn( COL_INVALID ) + { + } + + public: + // IMouseFunction + virtual FunctionResult handleMouseMove( ITableControl& i_tableControl, MouseEvent const & i_event ); + virtual FunctionResult handleMouseDown( ITableControl& i_tableControl, MouseEvent const & i_event ); + virtual FunctionResult handleMouseUp( ITableControl& i_tableControl, MouseEvent const & i_event ); + + private: + ColPos m_nActiveColumn; + }; + +//...................................................................................................................... +} } // namespace svt::table +//...................................................................................................................... + +#endif // SVTOOLS_MOUSEFUNCTION_HXX diff --git a/svtools/source/table/tablecontrol.cxx b/svtools/source/table/tablecontrol.cxx new file mode 100644 index 000000000000..6ec1ec5e7121 --- /dev/null +++ b/svtools/source/table/tablecontrol.cxx @@ -0,0 +1,613 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * +************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_svtools.hxx" + +#include "svtools/table/tablecontrol.hxx" + +#include "tablegeometry.hxx" +#include "tablecontrol_impl.hxx" +#include "tabledatawindow.hxx" + +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#include <com/sun/star/accessibility/AccessibleRole.hpp> + +#include <tools/diagnose_ex.h> + +using namespace ::com::sun::star::uno; +using ::com::sun::star::accessibility::XAccessible; +using namespace ::com::sun::star::accessibility; +using namespace ::com::sun::star::lang; +using namespace utl; +//...................................................................................................................... +namespace svt { namespace table +{ +//...................................................................................................................... + + //================================================================================================================== + //= TableControl + //================================================================================================================== + // ----------------------------------------------------------------------------------------------------------------- + TableControl::TableControl( Window* _pParent, WinBits _nStyle ) + :Control( _pParent, _nStyle ) + ,m_pImpl( new TableControl_Impl( *this ) ) + { + TableDataWindow& rDataWindow = m_pImpl->getDataWindow(); + rDataWindow.SetMouseButtonDownHdl( LINK( this, TableControl, ImplMouseButtonDownHdl ) ); + rDataWindow.SetMouseButtonUpHdl( LINK( this, TableControl, ImplMouseButtonUpHdl ) ); + rDataWindow.SetSelectHdl( LINK( this, TableControl, ImplSelectHdl ) ); + + // by default, use the background as determined by the style settings + const Color aWindowColor( GetSettings().GetStyleSettings().GetFieldColor() ); + SetBackground( Wallpaper( aWindowColor ) ); + SetFillColor( aWindowColor ); + } + + // ----------------------------------------------------------------------------------------------------------------- + TableControl::~TableControl() + { + ImplCallEventListeners( VCLEVENT_OBJECT_DYING ); + + m_pImpl->setModel( PTableModel() ); + m_pImpl->disposeAccessible(); + m_pImpl.reset(); + } + + // ----------------------------------------------------------------------------------------------------------------- + void TableControl::GetFocus() + { + if ( !m_pImpl->getInputHandler()->GetFocus( *m_pImpl ) ) + Control::GetFocus(); + } + + // ----------------------------------------------------------------------------------------------------------------- + void TableControl::LoseFocus() + { + if ( !m_pImpl->getInputHandler()->LoseFocus( *m_pImpl ) ) + Control::LoseFocus(); + } + + // ----------------------------------------------------------------------------------------------------------------- + void TableControl::KeyInput( const KeyEvent& rKEvt ) + { + if ( !m_pImpl->getInputHandler()->KeyInput( *m_pImpl, rKEvt ) ) + Control::KeyInput( rKEvt ); + } + + + // ----------------------------------------------------------------------------------------------------------------- + void TableControl::StateChanged( StateChangedType i_nStateChange ) + { + Control::StateChanged( i_nStateChange ); + + // forward certain settings to the data window + switch ( i_nStateChange ) + { + case STATE_CHANGE_CONTROLBACKGROUND: + if ( IsControlBackground() ) + getDataWindow().SetControlBackground( GetControlBackground() ); + else + getDataWindow().SetControlBackground(); + break; + + case STATE_CHANGE_CONTROLFOREGROUND: + if ( IsControlForeground() ) + getDataWindow().SetControlForeground( GetControlForeground() ); + else + getDataWindow().SetControlForeground(); + break; + + case STATE_CHANGE_CONTROLFONT: + if ( IsControlFont() ) + getDataWindow().SetControlFont( GetControlFont() ); + else + getDataWindow().SetControlFont(); + break; + } + } + + // ----------------------------------------------------------------------------------------------------------------- + void TableControl::Resize() + { + Control::Resize(); + m_pImpl->onResize(); + } + + // ----------------------------------------------------------------------------------------------------------------- + void TableControl::SetModel( PTableModel _pModel ) + { + m_pImpl->setModel( _pModel ); + } + + // ----------------------------------------------------------------------------------------------------------------- + PTableModel TableControl::GetModel() const + { + return m_pImpl->getModel(); + } + + // ----------------------------------------------------------------------------------------------------------------- + RowPos TableControl::GetTopRow() const + { + return m_pImpl->getTopRow(); + } + + // ----------------------------------------------------------------------------------------------------------------- + void TableControl::SetTopRow( RowPos _nRow ) + { + OSL_ENSURE( false, "TableControl::SetTopRow: not implemented!" ); + OSL_UNUSED( _nRow ); + } + + // ----------------------------------------------------------------------------------------------------------------- + sal_Int32 TableControl::GetCurrentRow() const + { + return m_pImpl->getCurrentRow(); + } + + // ----------------------------------------------------------------------------------------------------------------- + sal_Int32 TableControl::GetCurrentColumn() const + { + return m_pImpl->getCurrentColumn(); + } + + // ----------------------------------------------------------------------------------------------------------------- + bool TableControl::GoTo( ColPos _nColumn, RowPos _nRow ) + { + return m_pImpl->goTo( _nColumn, _nRow ); + } + + // ----------------------------------------------------------------------------------------------------------------- + sal_Bool TableControl::GoToCell(sal_Int32 _nColPos, sal_Int32 _nRowPos) + { + return m_pImpl->goTo( _nColPos, _nRowPos ); + } + + //------------------------------------------------------------------------------------------------------------------ + sal_Int32 TableControl::GetSelectedRowCount() const + { + return sal_Int32( m_pImpl->getSelectedRowCount() ); + } + + //------------------------------------------------------------------------------------------------------------------ + sal_Int32 TableControl::GetSelectedRowIndex( sal_Int32 const i_selectionIndex ) const + { + return sal_Int32( m_pImpl->getSelectedRowIndex( i_selectionIndex ) ); + } + + //------------------------------------------------------------------------------------------------------------------ + bool TableControl::IsRowSelected( sal_Int32 const i_rowIndex ) const + { + return m_pImpl->isRowSelected( i_rowIndex ); + } + + // ----------------------------------------------------------------------------------------------------------------- + void TableControl::SelectRow( RowPos const i_rowIndex, bool const i_select ) + { + ENSURE_OR_RETURN_VOID( ( i_rowIndex >= 0 ) && ( i_rowIndex < m_pImpl->getModel()->getRowCount() ), + "TableControl::SelectRow: no control (anymore)!" ); + + if ( i_select ) + { + if ( !m_pImpl->markRowAsSelected( i_rowIndex ) ) + // nothing to do + return; + } + else + { + m_pImpl->markRowAsDeselected( i_rowIndex ); + } + + m_pImpl->invalidateRowRange( i_rowIndex, i_rowIndex ); + Select(); + } + + // ----------------------------------------------------------------------------------------------------------------- + void TableControl::SelectAllRows( bool const i_select ) + { + if ( i_select ) + { + if ( !m_pImpl->markAllRowsAsSelected() ) + // nothing to do + return; + } + else + { + if ( !m_pImpl->markAllRowsAsDeselected() ) + // nothing to do + return; + } + + + Invalidate(); + // TODO: can't we do better than this, and invalidate only the rows which changed? + Select(); + } + + // ----------------------------------------------------------------------------------------------------------------- + ITableControl& TableControl::getTableControlInterface() + { + return *m_pImpl; + } + + // ----------------------------------------------------------------------------------------------------------------- + SelectionEngine* TableControl::getSelEngine() + { + return m_pImpl->getSelEngine(); + } + + // ----------------------------------------------------------------------------------------------------------------- + Window& TableControl::getDataWindow() + { + return m_pImpl->getDataWindow(); + } + + // ----------------------------------------------------------------------------------------------------------------- + Reference< XAccessible > TableControl::CreateAccessible() + { + Window* pParent = GetAccessibleParentWindow(); + ENSURE_OR_RETURN( pParent, "TableControl::CreateAccessible - parent not found", NULL ); + + return m_pImpl->getAccessible( *pParent ); + } + + // ----------------------------------------------------------------------------------------------------------------- + Reference<XAccessible> TableControl::CreateAccessibleControl( sal_Int32 _nIndex ) + { + (void)_nIndex; + DBG_ASSERT( sal_False, "TableControl::CreateAccessibleControl: to be overwritten!" ); + return NULL; + } + + // ----------------------------------------------------------------------------------------------------------------- + ::rtl::OUString TableControl::GetAccessibleObjectName( AccessibleTableControlObjType eObjType, sal_Int32 _nRow, sal_Int32 _nCol) const + { + ::rtl::OUString aRetText; + //Window* pWin; + switch( eObjType ) + { + case TCTYPE_GRIDCONTROL: + aRetText = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "GridControl" ) ); + break; + case TCTYPE_TABLE: + aRetText = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Table" ) ); + break; + case TCTYPE_ROWHEADERBAR: + aRetText = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "RowHeaderBar" ) ); + break; + case TCTYPE_COLUMNHEADERBAR: + aRetText = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "ColumnHeaderBar" ) ); + break; + case TCTYPE_TABLECELL: + aRetText = GetAccessibleCellText(_nRow, _nCol); + break; + case TCTYPE_ROWHEADERCELL: + aRetText = GetRowName(_nRow); + break; + case TCTYPE_COLUMNHEADERCELL: + aRetText = GetColumnName(_nCol); + break; + default: + OSL_FAIL("GridControl::GetAccessibleName: invalid enum!"); + } + return aRetText; + } + + //------------------------------------------------------------------------------------------------------------------ + ::rtl::OUString TableControl::GetAccessibleObjectDescription( AccessibleTableControlObjType eObjType, sal_Int32 ) const + { + ::rtl::OUString aRetText; + switch( eObjType ) + { + case TCTYPE_GRIDCONTROL: + aRetText = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "GridControl description" ) ); + break; + case TCTYPE_TABLE: + aRetText = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "TABLE description" ) ); + break; + case TCTYPE_ROWHEADERBAR: + aRetText = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "ROWHEADERBAR description" ) ); + break; + case TCTYPE_COLUMNHEADERBAR: + aRetText = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "COLUMNHEADERBAR description" ) ); + break; + case TCTYPE_TABLECELL: + aRetText = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "TABLECELL description" ) ); + break; + case TCTYPE_ROWHEADERCELL: + aRetText = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "ROWHEADERCELL description" ) ); + break; + case TCTYPE_COLUMNHEADERCELL: + aRetText = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "COLUMNHEADERCELL description" ) ); + break; + } + return aRetText; + } + + //------------------------------------------------------------------------------------------------------------------ + ::rtl::OUString TableControl::GetRowDescription( sal_Int32 _nRow) const + { + (void)_nRow; + return rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "row description" ) ); + } + + //------------------------------------------------------------------------------------------------------------------ + ::rtl::OUString TableControl::GetRowName( sal_Int32 _nIndex) const + { + ::rtl::OUString sRowName; + GetModel()->getRowHeading( _nIndex ) >>= sRowName; + return sRowName; + } + + //------------------------------------------------------------------------------------------------------------------ + ::rtl::OUString TableControl::GetColumnDescription( sal_uInt16 _nColumn) const + { + (void)_nColumn; + return rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "col description" ) ); + } + + //------------------------------------------------------------------------------------------------------------------ + ::rtl::OUString TableControl::GetColumnName( sal_Int32 _nIndex) const + { + return GetModel()->getColumnModel(_nIndex)->getName(); + } + + //------------------------------------------------------------------------------------------------------------------ + ::com::sun::star::uno::Any TableControl::GetCellContent( sal_Int32 _nRowPos, sal_Int32 _nColPos ) const + { + Any aCellContent; + GetModel()->getCellContent( _nColPos, _nRowPos, aCellContent ); + return aCellContent; + } + + //------------------------------------------------------------------------------------------------------------------ + ::rtl::OUString TableControl::GetAccessibleCellText( sal_Int32 _nRowPos, sal_Int32 _nColPos) const + { + return m_pImpl->getCellContentAsString( _nRowPos, _nColPos ); + } + + //------------------------------------------------------------------------------------------------------------------ + void TableControl::FillAccessibleStateSet( + ::utl::AccessibleStateSetHelper& rStateSet, + AccessibleTableControlObjType eObjType ) const + { + switch( eObjType ) + { + case TCTYPE_GRIDCONTROL: + case TCTYPE_TABLE: + + rStateSet.AddState( AccessibleStateType::FOCUSABLE ); + rStateSet.AddState( AccessibleStateType::MULTI_SELECTABLE); + if ( HasFocus() ) + rStateSet.AddState( AccessibleStateType::FOCUSED ); + if ( IsActive() ) + rStateSet.AddState( AccessibleStateType::ACTIVE ); + if ( IsEnabled() ) + rStateSet.AddState( AccessibleStateType::ENABLED ); + if ( IsReallyVisible() ) + rStateSet.AddState( AccessibleStateType::VISIBLE ); + rStateSet.AddState( AccessibleStateType::MANAGES_DESCENDANTS ); + + break; + case TCTYPE_ROWHEADERBAR: + rStateSet.AddState( AccessibleStateType::VISIBLE ); + rStateSet.AddState( AccessibleStateType::MANAGES_DESCENDANTS ); + break; + case TCTYPE_COLUMNHEADERBAR: + rStateSet.AddState( AccessibleStateType::VISIBLE ); + rStateSet.AddState( AccessibleStateType::MANAGES_DESCENDANTS ); + break; + case TCTYPE_TABLECELL: + { + rStateSet.AddState( AccessibleStateType::TRANSIENT ); + rStateSet.AddState( AccessibleStateType::SELECTABLE); + if( GetSelectedRowCount()>0) + rStateSet.AddState( AccessibleStateType::SELECTED); + } + break; + case TCTYPE_ROWHEADERCELL: + rStateSet.AddState( AccessibleStateType::VISIBLE ); + rStateSet.AddState( AccessibleStateType::TRANSIENT ); + break; + case TCTYPE_COLUMNHEADERCELL: + rStateSet.AddState( AccessibleStateType::VISIBLE ); + break; + } + } + + //------------------------------------------------------------------------------------------------------------------ + Rectangle TableControl::GetWindowExtentsRelative( Window *pRelativeWindow ) const + { + return Control::GetWindowExtentsRelative( pRelativeWindow ); + } + + //------------------------------------------------------------------------------------------------------------------ + void TableControl::GrabFocus() + { + Control::GrabFocus(); + } + + //------------------------------------------------------------------------------------------------------------------ + Reference< XAccessible > TableControl::GetAccessible( sal_Bool bCreate ) + { + return Control::GetAccessible( bCreate ); + } + + //------------------------------------------------------------------------------------------------------------------ + Window* TableControl::GetAccessibleParentWindow() const + { + return Control::GetAccessibleParentWindow(); + } + + //------------------------------------------------------------------------------------------------------------------ + Window* TableControl::GetWindowInstance() + { + return this; + } + + //------------------------------------------------------------------------------------------------------------------ + sal_Bool TableControl::HasRowHeader() + { + return GetModel()->hasRowHeaders(); + } + + //------------------------------------------------------------------------------------------------------------------ + sal_Bool TableControl::HasColHeader() + { + return GetModel()->hasColumnHeaders(); + } + + //------------------------------------------------------------------------------------------------------------------ + sal_Int32 TableControl::GetAccessibleControlCount() const + { + sal_Int32 count = 0; + if(GetRowCount()>0) + count+=1; + if(GetModel()->hasRowHeaders()) + count+=1; + if(GetModel()->hasColumnHeaders()) + count+=1; + return count; + } + + //------------------------------------------------------------------------------------------------------------------ + sal_Bool TableControl::ConvertPointToControlIndex( sal_Int32& _rnIndex, const Point& _rPoint ) + { + sal_Int32 nRow = m_pImpl->getRowAtPoint( _rPoint ); + sal_Int32 nCol = m_pImpl->getColAtPoint( _rPoint ); + _rnIndex = nRow * GetColumnCount() + nCol; + return nRow >= 0 ? sal_True : sal_False; + } + + //------------------------------------------------------------------------------------------------------------------ + long TableControl::GetRowCount() const + { + return GetModel()->getRowCount(); + } + + //------------------------------------------------------------------------------------------------------------------ + long TableControl::GetColumnCount() const + { + return GetModel()->getColumnCount(); + } + + //------------------------------------------------------------------------------------------------------------------ + sal_Bool TableControl::HasRowHeader() const + { + return GetModel()->hasRowHeaders(); + } + + //------------------------------------------------------------------------------------------------------------------ + sal_Bool TableControl::ConvertPointToCellAddress( sal_Int32& _rnRow, sal_Int32& _rnColPos, const Point& _rPoint ) + { + _rnRow = m_pImpl->getRowAtPoint( _rPoint ); + _rnColPos = m_pImpl->getColAtPoint( _rPoint ); + return _rnRow >= 0 ? sal_True : sal_False; + } + + //------------------------------------------------------------------------------------------------------------------ + void TableControl::FillAccessibleStateSetForCell( ::utl::AccessibleStateSetHelper& _rStateSet, sal_Int32 _nRow, sal_uInt16 _nColumnPos ) const + { + if ( GetCurrentRow() == _nRow && GetCurrentColumn() == _nColumnPos ) + _rStateSet.AddState( AccessibleStateType::FOCUSED ); + else // only transient when column is not focused + _rStateSet.AddState( AccessibleStateType::TRANSIENT ); + } + + //------------------------------------------------------------------------------------------------------------------ + Rectangle TableControl::GetFieldCharacterBounds(sal_Int32 _nRow,sal_Int32 _nColumnPos,sal_Int32 nIndex) + { + (void)_nRow; + (void)_nColumnPos; + return GetCharacterBounds(nIndex); + } + + //------------------------------------------------------------------------------------------------------------------ + sal_Int32 TableControl::GetFieldIndexAtPoint(sal_Int32 _nRow,sal_Int32 _nColumnPos,const Point& _rPoint) + { + (void)_nRow; + (void)_nColumnPos; + return GetIndexForPoint(_rPoint); + } + + //------------------------------------------------------------------------------------------------------------------ + Rectangle TableControl::calcHeaderRect(sal_Bool _bIsColumnBar,sal_Bool _bOnScreen) + { + (void)_bOnScreen; + return m_pImpl->calcHeaderRect( _bIsColumnBar ? false : true ); + } + + //------------------------------------------------------------------------------------------------------------------ + Rectangle TableControl::calcTableRect(sal_Bool _bOnScreen) + { + (void)_bOnScreen; + return m_pImpl->calcTableRect(); + } + + //------------------------------------------------------------------------------------------------------------------ + IMPL_LINK( TableControl, ImplSelectHdl, void*, EMPTYARG ) + { + Select(); + return 1; + } + + //------------------------------------------------------------------------------------------------------------------ + IMPL_LINK( TableControl, ImplMouseButtonDownHdl, MouseEvent*, pData ) + { + CallEventListeners( VCLEVENT_WINDOW_MOUSEBUTTONDOWN, pData ); + return 1; + } + + //------------------------------------------------------------------------------------------------------------------ + IMPL_LINK( TableControl, ImplMouseButtonUpHdl, MouseEvent*, pData ) + { + CallEventListeners( VCLEVENT_WINDOW_MOUSEBUTTONUP, pData ); + return 1; + } + + //------------------------------------------------------------------------------------------------------------------ + void TableControl::Select() + { + ImplCallEventListenersAndHandler( VCLEVENT_TABLEROW_SELECT, m_pImpl->getSelectHandler(), this ); + } +//........................................................................ + + //------------------------------------------------------------------------------------------------------------------ + void TableControl::SetSelectHdl( const Link& i_selectHandler ) + { + m_pImpl->setSelectHandler( i_selectHandler ); + } + + //------------------------------------------------------------------------------------------------------------------ + const Link& TableControl::GetSelectHdl() const + { + return m_pImpl->getSelectHandler(); + } +}} // namespace svt::table + +//...................................................................................................................... +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svtools/source/table/tablecontrol_impl.cxx b/svtools/source/table/tablecontrol_impl.cxx new file mode 100644 index 000000000000..bbcdbfaddeb6 --- /dev/null +++ b/svtools/source/table/tablecontrol_impl.cxx @@ -0,0 +1,2745 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * +************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_svtools.hxx" + +#include "svtools/table/tablecontrol.hxx" +#include "svtools/table/defaultinputhandler.hxx" +#include "svtools/table/tablemodel.hxx" + +#include "tabledatawindow.hxx" +#include "tablecontrol_impl.hxx" +#include "tablegeometry.hxx" +#include "cellvalueconversion.hxx" + +/** === begin UNO includes === **/ +#include <com/sun/star/accessibility/XAccessible.hpp> +#include <com/sun/star/accessibility/AccessibleTableModelChange.hpp> +#include <com/sun/star/accessibility/AccessibleEventId.hpp> +#include <com/sun/star/accessibility/AccessibleTableModelChangeType.hpp> +/** === end UNO includes === **/ + +#include <comphelper/flagguard.hxx> +#include <vcl/scrbar.hxx> +#include <vcl/seleng.hxx> +#include <rtl/ref.hxx> +#include <vcl/image.hxx> +#include <tools/diagnose_ex.h> + +#include <functional> + +#define MIN_COLUMN_WIDTH_PIXEL 4 + +//...................................................................................................................... +namespace svt { namespace table +{ +//...................................................................................................................... + + /** === begin UNO using === **/ + using ::com::sun::star::accessibility::AccessibleTableModelChange; + using ::com::sun::star::uno::makeAny; + using ::com::sun::star::uno::Any; + using ::com::sun::star::accessibility::XAccessible; + using ::com::sun::star::uno::Reference; + /** === end UNO using === **/ + namespace AccessibleEventId = ::com::sun::star::accessibility::AccessibleEventId; + namespace AccessibleTableModelChangeType = ::com::sun::star::accessibility::AccessibleTableModelChangeType; + + //================================================================================================================== + //= SuppressCursor + //================================================================================================================== + class SuppressCursor + { + private: + ITableControl& m_rTable; + + public: + SuppressCursor( ITableControl& _rTable ) + :m_rTable( _rTable ) + { + m_rTable.hideCursor(); + } + ~SuppressCursor() + { + m_rTable.showCursor(); + } + }; + + //==================================================================== + //= EmptyTableModel + //==================================================================== + /** default implementation of an ->ITableModel, used as fallback when no + real model is present + + Instances of this class are static in any way, and provide the least + necessary default functionality for a table model. + */ + class EmptyTableModel : public ITableModel + { + public: + EmptyTableModel() + { + } + + // ITableModel overridables + virtual TableSize getColumnCount() const + { + return 0; + } + virtual TableSize getRowCount() const + { + return 0; + } + virtual bool hasColumnHeaders() const + { + return false; + } + virtual bool hasRowHeaders() const + { + return false; + } + virtual bool isCellEditable( ColPos col, RowPos row ) const + { + (void)col; + (void)row; + return false; + } + virtual PColumnModel getColumnModel( ColPos column ) + { + OSL_FAIL( "EmptyTableModel::getColumnModel: invalid call!" ); + (void)column; + return PColumnModel(); + } + virtual PTableRenderer getRenderer() const + { + return PTableRenderer(); + } + virtual PTableInputHandler getInputHandler() const + { + return PTableInputHandler(); + } + virtual TableMetrics getRowHeight() const + { + return 5 * 100; + } + virtual void setRowHeight(TableMetrics _nRowHeight) + { + (void)_nRowHeight; + } + virtual TableMetrics getColumnHeaderHeight() const + { + return 0; + } + virtual TableMetrics getRowHeaderWidth() const + { + return 0; + } + virtual ScrollbarVisibility getVerticalScrollbarVisibility() const + { + return ScrollbarShowNever; + } + virtual ScrollbarVisibility getHorizontalScrollbarVisibility() const + { + return ScrollbarShowNever; + } + virtual void addTableModelListener( const PTableModelListener& i_listener ) + { + (void)i_listener; + } + virtual void removeTableModelListener( const PTableModelListener& i_listener ) + { + (void)i_listener; + } + virtual ::boost::optional< ::Color > getLineColor() const + { + return ::boost::optional< ::Color >(); + } + virtual ::boost::optional< ::Color > getHeaderBackgroundColor() const + { + return ::boost::optional< ::Color >(); + } + virtual ::boost::optional< ::Color > getHeaderTextColor() const + { + return ::boost::optional< ::Color >(); + } + virtual ::boost::optional< ::Color > getTextColor() const + { + return ::boost::optional< ::Color >(); + } + virtual ::boost::optional< ::Color > getTextLineColor() const + { + return ::boost::optional< ::Color >(); + } + virtual ::boost::optional< ::std::vector< ::Color > > getRowBackgroundColors() const + { + return ::boost::optional< ::std::vector< ::Color > >(); + } + virtual ::com::sun::star::style::VerticalAlignment getVerticalAlign() const + { + return com::sun::star::style::VerticalAlignment(0); + } + virtual ITableDataSort* getSortAdapter() + { + return NULL; + } + virtual void getCellContent( ColPos const i_col, RowPos const i_row, ::com::sun::star::uno::Any& o_cellContent ) + { + (void)i_row; + (void)i_col; + o_cellContent.clear(); + } + virtual void getCellToolTip( ColPos const, RowPos const, ::com::sun::star::uno::Any& ) + { + } + virtual Any getRowHeading( RowPos const i_rowPos ) const + { + (void)i_rowPos; + return Any(); + } + }; + + + //==================================================================== + //= TableControl_Impl + //==================================================================== + DBG_NAME( TableControl_Impl ) + +#if DBG_UTIL + //==================================================================== + //= SuspendInvariants + //==================================================================== + class SuspendInvariants + { + private: + const TableControl_Impl& m_rTable; + sal_Int32 m_nSuspendFlags; + + public: + SuspendInvariants( const TableControl_Impl& _rTable, sal_Int32 _nSuspendFlags ) + :m_rTable( _rTable ) + ,m_nSuspendFlags( _nSuspendFlags ) + { + //DBG_ASSERT( ( m_rTable.m_nRequiredInvariants & m_nSuspendFlags ) == m_nSuspendFlags, + // "SuspendInvariants: cannot suspend what is already suspended!" ); + const_cast< TableControl_Impl& >( m_rTable ).m_nRequiredInvariants &= ~m_nSuspendFlags; + } + ~SuspendInvariants() + { + const_cast< TableControl_Impl& >( m_rTable ).m_nRequiredInvariants |= m_nSuspendFlags; + } + }; + #define DBG_SUSPEND_INV( flags ) \ + SuspendInvariants aSuspendInv( *this, flags ); +#else + #define DBG_SUSPEND_INV( flags ) +#endif + +#if DBG_UTIL + //==================================================================== + const char* TableControl_Impl_checkInvariants( const void* _pInstance ) + { + return static_cast< const TableControl_Impl* >( _pInstance )->impl_checkInvariants(); + } + + namespace + { + template< typename SCALAR_TYPE > + bool lcl_checkLimitsExclusive( SCALAR_TYPE _nValue, SCALAR_TYPE _nMin, SCALAR_TYPE _nMax ) + { + return ( _nValue > _nMin ) && ( _nValue < _nMax ); + } + + template< typename SCALAR_TYPE > + bool lcl_checkLimitsExclusive_OrDefault_OrFallback( SCALAR_TYPE _nValue, SCALAR_TYPE _nMin, SCALAR_TYPE _nMax, + PTableModel _pModel, SCALAR_TYPE _nDefaultOrFallback ) + { + if ( !_pModel ) + return _nValue == _nDefaultOrFallback; + if ( _nMax <= _nMin ) + return _nDefaultOrFallback == _nValue; + return lcl_checkLimitsExclusive( _nValue, _nMin, _nMax ); + } + } + + //------------------------------------------------------------------------------------------------------------------ + const sal_Char* TableControl_Impl::impl_checkInvariants() const + { + if ( !m_pModel ) + return "no model, not even an EmptyTableModel"; + + if ( !m_pDataWindow ) + return "invalid data window!"; + + if ( m_pModel->getColumnCount() != m_nColumnCount ) + return "column counts are inconsistent!"; + + if ( m_pModel->getRowCount() != m_nRowCount ) + return "row counts are inconsistent!"; + + if ( ( ( m_nCurColumn != COL_INVALID ) && !m_aColumnWidths.empty() && ( m_nCurColumn < 0 ) ) || ( m_nCurColumn >= (ColPos)m_aColumnWidths.size() ) ) + return "current column is invalid!"; + + if ( !lcl_checkLimitsExclusive_OrDefault_OrFallback( m_nTopRow, (RowPos)-1, m_nRowCount, getModel(), (RowPos)0 ) ) + return "invalid top row value!"; + + if ( !lcl_checkLimitsExclusive_OrDefault_OrFallback( m_nCurRow, (RowPos)-1, m_nRowCount, getModel(), ROW_INVALID ) ) + return "invalid current row value!"; + + if ( !lcl_checkLimitsExclusive_OrDefault_OrFallback( m_nLeftColumn, (ColPos)-1, m_nColumnCount, getModel(), (ColPos)0 ) ) + return "invalid current column value!"; + + if ( !lcl_checkLimitsExclusive_OrDefault_OrFallback( m_nCurColumn, (ColPos)-1, m_nColumnCount, getModel(), COL_INVALID ) ) + return "invalid current column value!"; + + if ( m_pInputHandler != m_pModel->getInputHandler() ) + return "input handler is not the model-provided one!"; + + // m_aSelectedRows should have reasonable content + { + if ( m_aSelectedRows.size() > size_t( m_pModel->getRowCount() ) ) + return "there are more rows selected than actually exist"; + for ( ::std::vector< RowPos >::const_iterator selRow = m_aSelectedRows.begin(); + selRow != m_aSelectedRows.end(); + ++selRow + ) + { + if ( ( *selRow < 0 ) || ( *selRow >= m_pModel->getRowCount() ) ) + return "a non-existent row is selected"; + } + } + + // m_nColHeaderHeightPixel consistent with the model's value? + { + TableMetrics nHeaderHeight = m_pModel->hasColumnHeaders() ? m_pModel->getColumnHeaderHeight() : 0; + nHeaderHeight = m_rAntiImpl.LogicToPixel( Size( 0, nHeaderHeight ), MAP_APPFONT ).Height(); + if ( nHeaderHeight != m_nColHeaderHeightPixel ) + return "column header heights are inconsistent!"; + } + + bool isDummyModel = dynamic_cast< const EmptyTableModel* >( m_pModel.get() ) != NULL; + if ( !isDummyModel ) + { + TableMetrics nRowHeight = m_pModel->getRowHeight(); + nRowHeight = m_rAntiImpl.LogicToPixel( Size( 0, nRowHeight ), MAP_APPFONT).Height(); + if ( nRowHeight != m_nRowHeightPixel ) + return "row heights are inconsistent!"; + } + + // m_nRowHeaderWidthPixel consistent with the model's value? + { + TableMetrics nHeaderWidth = m_pModel->hasRowHeaders() ? m_pModel->getRowHeaderWidth() : 0; + nHeaderWidth = m_rAntiImpl.LogicToPixel( Size( nHeaderWidth, 0 ), MAP_APPFONT ).Width(); + if ( nHeaderWidth != m_nRowHeaderWidthPixel ) + return "row header widths are inconsistent!"; + } + + // m_aColumnWidths consistency + if ( size_t( m_nColumnCount ) != m_aColumnWidths.size() ) + return "wrong number of cached column widths"; + + for ( ColumnPositions::const_iterator col = m_aColumnWidths.begin(); + col != m_aColumnWidths.end(); + ) + { + if ( col->getEnd() < col->getStart() ) + return "column widths: 'end' is expected to not be smaller than start"; + + ColumnPositions::const_iterator nextCol = col + 1; + if ( nextCol != m_aColumnWidths.end() ) + if ( col->getEnd() != nextCol->getStart() ) + return "column widths: one column's end should be the next column's start"; + col = nextCol; + } + + if ( m_nLeftColumn < m_nColumnCount ) + if ( m_aColumnWidths[ m_nLeftColumn ].getStart() != m_nRowHeaderWidthPixel ) + return "the left-most column should start immediately after the row header"; + + if ( m_nCursorHidden < 0 ) + return "invalid hidden count for the cursor!"; + + if ( ( m_nRequiredInvariants & INV_SCROLL_POSITION ) && m_pVScroll ) + { + DBG_SUSPEND_INV( INV_SCROLL_POSITION ); + // prevent infinite recursion + + if ( m_nLeftColumn < 0 ) + return "invalid left-most column index"; + if ( m_pVScroll->GetThumbPos() != m_nTopRow ) + return "vertical scroll bar |position| is incorrect!"; + if ( m_pVScroll->GetRange().Max() != m_nRowCount ) + return "vertical scroll bar |range| is incorrect!"; + if ( m_pVScroll->GetVisibleSize() != impl_getVisibleRows( false ) ) + return "vertical scroll bar |visible size| is incorrect!"; + } + + if ( ( m_nRequiredInvariants & INV_SCROLL_POSITION ) && m_pHScroll ) + { + DBG_SUSPEND_INV( INV_SCROLL_POSITION ); + // prevent infinite recursion + + if ( m_pHScroll->GetThumbPos() != m_nLeftColumn ) + return "horizontal scroll bar |position| is incorrect!"; + if ( m_pHScroll->GetRange().Max() != m_nColumnCount ) + return "horizontal scroll bar |range| is incorrect!"; + if ( m_pHScroll->GetVisibleSize() != impl_getVisibleColumns( false ) ) + return "horizontal scroll bar |visible size| is incorrect!"; + } + + return NULL; + } +#endif + +#define DBG_CHECK_ME() \ + DBG_CHKTHIS( TableControl_Impl, TableControl_Impl_checkInvariants ) + + //------------------------------------------------------------------------------------------------------------------ + TableControl_Impl::TableControl_Impl( TableControl& _rAntiImpl ) + :m_rAntiImpl ( _rAntiImpl ) + ,m_pModel ( new EmptyTableModel ) + ,m_pInputHandler ( ) + ,m_nRowHeightPixel ( 15 ) + ,m_nColHeaderHeightPixel( 0 ) + ,m_nRowHeaderWidthPixel ( 0 ) + ,m_nColumnCount ( 0 ) + ,m_nRowCount ( 0 ) + ,m_bColumnsFit ( true ) + ,m_nCurColumn ( COL_INVALID ) + ,m_nCurRow ( ROW_INVALID ) + ,m_nLeftColumn ( 0 ) + ,m_nTopRow ( 0 ) + ,m_nCursorHidden ( 1 ) + ,m_pDataWindow ( new TableDataWindow( *this ) ) + ,m_pVScroll ( NULL ) + ,m_pHScroll ( NULL ) + ,m_pScrollCorner ( NULL ) + ,m_pSelEngine ( ) + ,m_aSelectedRows ( ) + ,m_pTableFunctionSet ( new TableFunctionSet( this ) ) + ,m_nAnchor ( -1 ) + ,m_bUpdatingColWidths ( false ) + ,m_pAccessibleTable ( NULL ) +#if DBG_UTIL + ,m_nRequiredInvariants ( INV_SCROLL_POSITION ) +#endif + { + DBG_CTOR( TableControl_Impl, TableControl_Impl_checkInvariants ); + m_pSelEngine = new SelectionEngine( m_pDataWindow.get(), m_pTableFunctionSet ); + m_pSelEngine->SetSelectionMode(SINGLE_SELECTION); + m_pDataWindow->SetPosPixel( Point( 0, 0 ) ); + m_pDataWindow->Show(); + } + + //------------------------------------------------------------------------------------------------------------------ + TableControl_Impl::~TableControl_Impl() + { + DBG_DTOR( TableControl_Impl, TableControl_Impl_checkInvariants ); + + DELETEZ( m_pVScroll ); + DELETEZ( m_pHScroll ); + DELETEZ( m_pScrollCorner ); + DELETEZ( m_pTableFunctionSet ); + DELETEZ( m_pSelEngine ); + } + + //------------------------------------------------------------------------------------------------------------------ + void TableControl_Impl::setModel( PTableModel _pModel ) + { + DBG_CHECK_ME(); + + SuppressCursor aHideCursor( *this ); + + if ( !!m_pModel ) + m_pModel->removeTableModelListener( shared_from_this() ); + + m_pModel = _pModel; + if ( !m_pModel) + m_pModel.reset( new EmptyTableModel ); + + m_pModel->addTableModelListener( shared_from_this() ); + + m_nCurRow = ROW_INVALID; + m_nCurColumn = COL_INVALID; + + // recalc some model-dependent cached info + impl_ni_updateCachedModelValues(); + impl_ni_updateScrollbars(); + + // completely invalidate + m_rAntiImpl.Invalidate(); + + // reset cursor to (0,0) + if ( m_nRowCount ) m_nCurRow = 0; + if ( m_nColumnCount ) m_nCurColumn = 0; + } + + //------------------------------------------------------------------------------------------------------------------ + namespace + { + bool lcl_adjustSelectedRows( ::std::vector< RowPos >& io_selectionIndexes, RowPos const i_firstAffectedRowIndex, TableSize const i_offset ) + { + bool didChanges = false; + for ( ::std::vector< RowPos >::iterator selPos = io_selectionIndexes.begin(); + selPos != io_selectionIndexes.end(); + ++selPos + ) + { + if ( *selPos < i_firstAffectedRowIndex ) + continue; + *selPos += i_offset; + didChanges = true; + } + return didChanges; + } + } + + //------------------------------------------------------------------------------------------------------------------ + void TableControl_Impl::rowsInserted( RowPos i_first, RowPos i_last ) + { + DBG_CHECK_ME(); + OSL_PRECOND( i_last >= i_first, "TableControl_Impl::rowsInserted: invalid row indexes!" ); + + TableSize const insertedRows = i_last - i_first + 1; + + // adjust selection, if necessary + bool const selectionChanged = lcl_adjustSelectedRows( m_aSelectedRows, i_first, insertedRows ); + + // adjust our cached row count + m_nRowCount = m_pModel->getRowCount(); + + // if the rows have been inserted before the current row, adjust this + if ( i_first <= m_nCurRow ) + goTo( m_nCurColumn, m_nCurRow + insertedRows ); + + // adjust scrollbars + impl_ni_updateScrollbars(); + + // notify A1YY events + if ( impl_isAccessibleAlive() ) + { + impl_commitAccessibleEvent( AccessibleEventId::TABLE_MODEL_CHANGED, + makeAny( AccessibleTableModelChange( AccessibleTableModelChangeType::INSERT, i_first, i_last, 0, m_pModel->getColumnCount() ) ), + Any() + ); + impl_commitAccessibleEvent( AccessibleEventId::CHILD, + makeAny( m_pAccessibleTable->getTableHeader( TCTYPE_ROWHEADERBAR ) ), + Any() + ); + +// for ( sal_Int32 i = 0 ; i <= m_pModel->getColumnCount(); ++i ) +// { +// impl_commitAccessibleEvent( +// CHILD, +// makeAny( m_pAccessibleTable->getTable() ), +// Any()); +// } + // Huh? What's that? We're notifying |columnCount| CHILD events here, claiming the *table* itself + // has been inserted. Doesn't make much sense, does it? + } + + // schedule repaint + invalidateRowRange( i_first, ROW_INVALID ); + + // call selection handlers, if necessary + if ( selectionChanged ) + m_rAntiImpl.Select(); + } + + //------------------------------------------------------------------------------------------------------------------ + void TableControl_Impl::rowsRemoved( RowPos i_first, RowPos i_last ) + { + sal_Int32 firstRemovedRow = i_first; + sal_Int32 lastRemovedRow = i_last; + + // adjust selection, if necessary + bool selectionChanged = false; + if ( i_first == -1 ) + { + selectionChanged = markAllRowsAsDeselected(); + + firstRemovedRow = 0; + lastRemovedRow = m_nRowCount - 1; + } + else + { + ENSURE_OR_RETURN_VOID( i_last >= i_first, "TableControl_Impl::rowsRemoved: illegal indexes!" ); + + for ( sal_Int32 row = i_first; row <= i_last; ++row ) + { + if ( markRowAsDeselected( row ) ) + selectionChanged = true; + } + + if ( lcl_adjustSelectedRows( m_aSelectedRows, i_last + 1, i_first - i_last - 1 ) ) + selectionChanged = true; + } + + // adjust cached row count + m_nRowCount = m_pModel->getRowCount(); + + // adjust the current row, if it is larger than the row count now + if ( m_nCurRow >= m_nRowCount ) + { + if ( m_nRowCount > 0 ) + goTo( m_nCurColumn, m_nRowCount - 1 ); + else + m_nCurRow = ROW_INVALID; + } + + // adjust scrollbars + impl_ni_updateScrollbars(); + + // notify A11Y events + if ( impl_isAccessibleAlive() ) + { + impl_commitAccessibleEvent( + AccessibleEventId::TABLE_MODEL_CHANGED, + makeAny( AccessibleTableModelChange( + AccessibleTableModelChangeType::DELETE, + firstRemovedRow, + lastRemovedRow, + 0, + m_pModel->getColumnCount() + ) ), + Any() + ); + } + + // schedule a repaint + invalidateRowRange( firstRemovedRow, ROW_INVALID ); + + // call selection handlers, if necessary + if ( selectionChanged ) + m_rAntiImpl.Select(); + } + + //------------------------------------------------------------------------------------------------------------------ + void TableControl_Impl::columnInserted( ColPos const i_colIndex ) + { + m_nColumnCount = m_pModel->getColumnCount(); + impl_ni_updateColumnWidths(); + impl_ni_updateScrollbars(); + + m_rAntiImpl.Invalidate(); + + OSL_UNUSED( i_colIndex ); + } + + //------------------------------------------------------------------------------------------------------------------ + void TableControl_Impl::columnRemoved( ColPos const i_colIndex ) + { + m_nColumnCount = m_pModel->getColumnCount(); + impl_ni_updateColumnWidths(); + impl_ni_updateScrollbars(); + + m_rAntiImpl.Invalidate(); + + OSL_UNUSED( i_colIndex ); + } + + //------------------------------------------------------------------------------------------------------------------ + void TableControl_Impl::allColumnsRemoved() + { + m_nColumnCount = m_pModel->getColumnCount(); + impl_ni_updateColumnWidths(); + impl_ni_updateScrollbars(); + + m_rAntiImpl.Invalidate(); + } + + //------------------------------------------------------------------------------------------------------------------ + void TableControl_Impl::cellsUpdated( ColPos const i_firstCol, ColPos i_lastCol, RowPos const i_firstRow, RowPos const i_lastRow ) + { + invalidateRowRange( i_firstRow, i_lastRow ); + + OSL_UNUSED( i_firstCol ); + OSL_UNUSED( i_lastCol ); + } + + //------------------------------------------------------------------------------------------------------------------ + void TableControl_Impl::tableMetricsChanged() + { + long const oldRowHeaderWidthPixel = m_nRowHeaderWidthPixel; + impl_ni_updateCachedTableMetrics(); + if ( oldRowHeaderWidthPixel != m_nRowHeaderWidthPixel ) + impl_ni_updateColumnWidths(); + impl_ni_updateScrollbars(); + m_rAntiImpl.Invalidate(); + } + + //------------------------------------------------------------------------------------------------------------------ + void TableControl_Impl::impl_invalidateColumn( ColPos const i_column ) + { + DBG_CHECK_ME(); + + Rectangle const aAllCellsArea( impl_getAllVisibleCellsArea() ); + + const TableColumnGeometry aColumn( *this, aAllCellsArea, i_column ); + if ( aColumn.isValid() ) + m_rAntiImpl.Invalidate( aColumn.getRect() ); + } + + //------------------------------------------------------------------------------------------------------------------ + void TableControl_Impl::columnChanged( ColPos const i_column, ColumnAttributeGroup const i_attributeGroup ) + { + ColumnAttributeGroup nGroup( i_attributeGroup ); + if ( nGroup & COL_ATTRS_APPEARANCE ) + { + impl_invalidateColumn( i_column ); + nGroup &= ~COL_ATTRS_APPEARANCE; + } + + if ( nGroup & COL_ATTRS_WIDTH ) + { + if ( !m_bUpdatingColWidths ) + { + impl_ni_updateColumnWidths( i_column ); + invalidate( TableAreaAll ); + impl_ni_updateScrollbars(); + } + + nGroup &= ~COL_ATTRS_WIDTH; + } + + OSL_ENSURE( ( nGroup == COL_ATTRS_NONE ) || ( i_attributeGroup == COL_ATTRS_ALL ), + "TableControl_Impl::columnChanged: don't know how to handle this change!" ); + } + + //------------------------------------------------------------------------------------------------------------------ + Rectangle TableControl_Impl::impl_getAllVisibleCellsArea() const + { + DBG_CHECK_ME(); + + Rectangle aArea( Point( 0, 0 ), Size( 0, 0 ) ); + + // determine the right-most border of the last column which is + // at least partially visible + aArea.Right() = m_nRowHeaderWidthPixel; + if ( !m_aColumnWidths.empty() ) + { + // the number of pixels which are scrolled out of the left hand + // side of the window + const long nScrolledOutLeft = m_nLeftColumn == 0 ? 0 : m_aColumnWidths[ m_nLeftColumn - 1 ].getEnd(); + + ColumnPositions::const_reverse_iterator loop = m_aColumnWidths.rbegin(); + do + { + aArea.Right() = loop->getEnd() - nScrolledOutLeft + m_nRowHeaderWidthPixel; + ++loop; + } + while ( ( loop != m_aColumnWidths.rend() ) + && ( loop->getEnd() - nScrolledOutLeft >= aArea.Right() ) + ); + } + // so far, aArea.Right() denotes the first pixel *after* the cell area + --aArea.Right(); + + // determine the last row which is at least partially visible + aArea.Bottom() = + m_nColHeaderHeightPixel + + impl_getVisibleRows( true ) * m_nRowHeightPixel + - 1; + + return aArea; + } + + //------------------------------------------------------------------------------------------------------------------ + Rectangle TableControl_Impl::impl_getAllVisibleDataCellArea() const + { + DBG_CHECK_ME(); + + Rectangle aArea( impl_getAllVisibleCellsArea() ); + aArea.Left() = m_nRowHeaderWidthPixel; + aArea.Top() = m_nColHeaderHeightPixel; + return aArea; + } + + //------------------------------------------------------------------------------------------------------------------ + void TableControl_Impl::impl_ni_updateCachedTableMetrics() + { + m_nRowHeightPixel = m_rAntiImpl.LogicToPixel( Size( 0, m_pModel->getRowHeight() ), MAP_APPFONT ).Height(); + + m_nColHeaderHeightPixel = 0; + if ( m_pModel->hasColumnHeaders() ) + m_nColHeaderHeightPixel = m_rAntiImpl.LogicToPixel( Size( 0, m_pModel->getColumnHeaderHeight() ), MAP_APPFONT ).Height(); + + m_nRowHeaderWidthPixel = 0; + if ( m_pModel->hasRowHeaders() ) + m_nRowHeaderWidthPixel = m_rAntiImpl.LogicToPixel( Size( m_pModel->getRowHeaderWidth(), 0 ), MAP_APPFONT).Width(); + } + + //------------------------------------------------------------------------------------------------------------------ + void TableControl_Impl::impl_ni_updateCachedModelValues() + { + m_pInputHandler.reset(); + m_nColumnCount = m_nRowCount = 0; + + impl_ni_updateCachedTableMetrics(); + impl_ni_updateColumnWidths(); + + m_pInputHandler = m_pModel->getInputHandler(); + if ( !m_pInputHandler ) + m_pInputHandler.reset( new DefaultInputHandler ); + + m_nColumnCount = m_pModel->getColumnCount(); + m_nRowCount = m_pModel->getRowCount(); + } + + //------------------------------------------------------------------------------------------------------------------ + void TableControl_Impl::impl_ni_updateColumnWidths( ColPos const i_assumeInflexibleColumnsUpToIncluding ) + { + ENSURE_OR_RETURN_VOID( !m_bUpdatingColWidths, "TableControl_Impl::impl_ni_updateColumnWidths: recursive call detected!" ); + + m_aColumnWidths.resize( 0 ); + if ( !m_pModel ) + return; + + const TableSize colCount = m_pModel->getColumnCount(); + if ( colCount == 0 ) + return; + + m_bUpdatingColWidths = true; + const ::comphelper::FlagGuard aWidthUpdateFlag( m_bUpdatingColWidths ); + + m_aColumnWidths.reserve( colCount ); + + // the available horizontal space + long gridWidthPixel = m_rAntiImpl.GetOutputSizePixel().Width(); + if ( m_pModel->hasRowHeaders() && ( gridWidthPixel != 0 ) ) + { + gridWidthPixel -= m_nRowHeaderWidthPixel; + } + if ( m_pModel->getVerticalScrollbarVisibility() != ScrollbarShowNever ) + { + long nScrollbarMetrics = m_rAntiImpl.GetSettings().GetStyleSettings().GetScrollBarSize(); + gridWidthPixel -= nScrollbarMetrics; + } + + // TODO: shouldn't we take the visibility of the vertical scroll bar into account here, too? + long const gridWidthAppFont = m_rAntiImpl.PixelToLogic( Size( gridWidthPixel, 0 ), MAP_APPFONT ).Width(); + + // determine the accumulated current width of all columns + for ( ColPos col = 0; col < colCount; ++col ) + { + const PColumnModel pColumn = m_pModel->getColumnModel( col ); + ENSURE_OR_THROW( !!pColumn, "invalid column returned by the model!" ); + + } + + // collect some meta data for our columns: + // - their current (appt-font) metrics + long accumulatedCurrentWidth = 0; + ::std::vector< long > currentColWidths; + currentColWidths.reserve( colCount ); + // - their effective minimal and maximal width (app-font!) + typedef ::std::vector< ::std::pair< long, long > > ColumnLimits; + ColumnLimits effectiveColumnLimits; + effectiveColumnLimits.reserve( colCount ); + long accumulatedMinWidth = 0; + long accumulatedMaxWidth = 0; + // - their relative flexibility + ::std::vector< ::sal_Int32 > columnFlexibilities; + columnFlexibilities.reserve( colCount ); + long flexibilityDenominator = 0; + for ( ColPos col = 0; col < colCount; ++col ) + { + PColumnModel const pColumn = m_pModel->getColumnModel( col ); + ENSURE_OR_THROW( !!pColumn, "invalid column returned by the model!" ); + + // current width + TableMetrics const currentWidth = pColumn->getWidth(); + currentColWidths.push_back( currentWidth ); + + // accumulated width + accumulatedCurrentWidth += currentWidth; + + // flexibility + ::sal_Int32 flexibility = pColumn->getFlexibility(); + OSL_ENSURE( flexibility >= 0, "TableControl_Impl::impl_ni_updateColumnWidths: a column's flexibility should be non-negative." ); + if ( ( flexibility < 0 ) // normalization + || ( !pColumn->isResizable() ) // column not resizeable => no auto-resize + || ( col <= i_assumeInflexibleColumnsUpToIncluding ) // column shall be treated as inflexible => respec this + ) + flexibility = 0; + + // min/max width + long effectiveMin = currentWidth, effectiveMax = currentWidth; + // if the column is not flexible, it will not be asked for min/max, but we assume the current width as limit then + if ( flexibility > 0 ) + { + long const minWidth = pColumn->getMinWidth(); + if ( minWidth > 0 ) + effectiveMin = minWidth; + else + effectiveMin = MIN_COLUMN_WIDTH_PIXEL; + + long const maxWidth = pColumn->getMaxWidth(); + OSL_ENSURE( minWidth <= maxWidth, "TableControl_Impl::impl_ni_updateColumnWidths: pretty undecided 'bout its width limits, this column!" ); + if ( ( maxWidth > 0 ) && ( maxWidth >= minWidth ) ) + effectiveMax = maxWidth; + else + effectiveMax = gridWidthAppFont; // TODO: any better guess here? + + if ( effectiveMin == effectiveMax ) + // if the min and the max are identical, this implies no flexibility at all + flexibility = 0; + } + + columnFlexibilities.push_back( flexibility ); + flexibilityDenominator += flexibility; + + effectiveColumnLimits.push_back( ::std::pair< long, long >( effectiveMin, effectiveMax ) ); + accumulatedMinWidth += effectiveMin; + accumulatedMaxWidth += effectiveMax; + } + + ::std::vector< long > newWidths( currentColWidths ); + if ( flexibilityDenominator == 0 ) + { + // no column is flexible => don't adjust anything + } + else if ( gridWidthAppFont > accumulatedCurrentWidth ) + { // we have space to give away ... + long distributeAppFontUnits = gridWidthAppFont - accumulatedCurrentWidth; + if ( gridWidthAppFont > accumulatedMaxWidth ) + { + // ... but the column's maximal widths are still less than we have + // => set them all to max + for ( size_t i = 0; i < size_t( colCount ); ++i ) + { + newWidths[i] = effectiveColumnLimits[i].second; + } + } + else + { + bool startOver = false; + do + { + startOver = false; + // distribute the remaining space amongst all columns with a positive flexibility + for ( size_t i=0; i<newWidths.size() && !startOver; ++i ) + { + long const columnFlexibility = columnFlexibilities[i]; + if ( columnFlexibility == 0 ) + continue; + + long newColWidth = currentColWidths[i] + columnFlexibility * distributeAppFontUnits / flexibilityDenominator; + + if ( newColWidth > effectiveColumnLimits[i].second ) + { // that was too much, we hit the col's maximum + // set the new width to exactly this maximum + newColWidth = effectiveColumnLimits[i].second; + // adjust the flexibility denominator ... + flexibilityDenominator -= columnFlexibility; + columnFlexibilities[i] = 0; + // ... and the remaining width ... + long const difference = newColWidth - currentColWidths[i]; + distributeAppFontUnits -= difference; + // ... this way, we ensure that the width not taken up by this column is consumed by the other + // flexible ones (if there are some) + + // and start over with the first column, since there might be earlier columns which need + // to be recalculated now + startOver = true; + } + + newWidths[i] = newColWidth; + } + } + while ( startOver ); + } + } + else if ( gridWidthAppFont < accumulatedCurrentWidth ) + { // we need to take away some space from the columns which allow it ... + long takeAwayAppFontUnits = accumulatedCurrentWidth - gridWidthAppFont; + if ( gridWidthAppFont < accumulatedMinWidth ) + { + // ... but the column's minimal widths are still more than we have + // => set them all to min + for ( size_t i = 0; i < size_t( colCount ); ++i ) + { + newWidths[i] = effectiveColumnLimits[i].first; + } + } + else + { + bool startOver = false; + do + { + startOver = false; + // take away the space we need from the columns with a positive flexibility + for ( size_t i=0; i<newWidths.size() && !startOver; ++i ) + { + long const columnFlexibility = columnFlexibilities[i]; + if ( columnFlexibility == 0 ) + continue; + + long newColWidth = currentColWidths[i] - columnFlexibility * takeAwayAppFontUnits / flexibilityDenominator; + + if ( newColWidth < effectiveColumnLimits[i].first ) + { // that was too much, we hit the col's minimum + // set the new width to exactly this minimum + newColWidth = effectiveColumnLimits[i].first; + // adjust the flexibility denominator ... + flexibilityDenominator -= columnFlexibility; + columnFlexibilities[i] = 0; + // ... and the remaining width ... + long const difference = currentColWidths[i] - newColWidth; + takeAwayAppFontUnits -= difference; + + // and start over with the first column, since there might be earlier columns which need + // to be recalculated now + startOver = true; + } + + newWidths[i] = newColWidth; + } + } + while ( startOver ); + } + } + + // now that we have calculated the app-font widths, get the actual pixels + long accumulatedWidthPixel = m_nRowHeaderWidthPixel; + for ( ColPos col = 0; col < colCount; ++col ) + { + long const colWidth = m_rAntiImpl.LogicToPixel( Size( newWidths[col], 0 ), MAP_APPFONT ).Width(); + const long columnStart = accumulatedWidthPixel; + const long columnEnd = columnStart + colWidth; + m_aColumnWidths.push_back( MutableColumnMetrics( columnStart, columnEnd ) ); + accumulatedWidthPixel = columnEnd; + + // and don't forget to forward this to the column models + PColumnModel const pColumn = m_pModel->getColumnModel( col ); + ENSURE_OR_THROW( !!pColumn, "invalid column returned by the model!" ); + pColumn->setWidth( newWidths[col] ); + } + + // if the column resizing happened to leave some space at the right, but there are columns + // scrolled out to the left, scroll them in + while ( ( m_nLeftColumn > 0 ) + && ( accumulatedWidthPixel - m_aColumnWidths[ m_nLeftColumn - 1 ].getStart() <= gridWidthPixel ) + ) + { + --m_nLeftColumn; + } + + // now adjust the column metrics, since they currently ignore the horizontal scroll position + if ( m_nLeftColumn > 0 ) + { + const long offsetPixel = m_aColumnWidths[ 0 ].getStart() - m_aColumnWidths[ m_nLeftColumn ].getStart(); + for ( ColumnPositions::iterator colPos = m_aColumnWidths.begin(); + colPos != m_aColumnWidths.end(); + ++colPos + ) + { + colPos->move( offsetPixel ); + } + } + } + + //------------------------------------------------------------------------------------------------------------------ + namespace + { + //.............................................................................................................. + /// determines whether a scrollbar is needed for the given values + bool lcl_determineScrollbarNeed( long const i_position, ScrollbarVisibility const i_visibility, + long const i_availableSpace, long const i_neededSpace ) + { + if ( i_visibility == ScrollbarShowNever ) + return false; + if ( i_visibility == ScrollbarShowAlways ) + return true; + if ( i_position > 0 ) + return true; + if ( i_availableSpace >= i_neededSpace ) + return false; + return true; + } + + //.............................................................................................................. + void lcl_setButtonRepeat( Window& _rWindow, sal_uLong _nDelay ) + { + AllSettings aSettings = _rWindow.GetSettings(); + MouseSettings aMouseSettings = aSettings.GetMouseSettings(); + + aMouseSettings.SetButtonRepeat( _nDelay ); + aSettings.SetMouseSettings( aMouseSettings ); + + _rWindow.SetSettings( aSettings, sal_True ); + } + + //.............................................................................................................. + void lcl_updateScrollbar( Window& _rParent, ScrollBar*& _rpBar, + bool const i_needBar, long _nVisibleUnits, + long _nPosition, long _nLineSize, long _nRange, + bool _bHorizontal, const Link& _rScrollHandler ) + { + // do we currently have the scrollbar? + bool bHaveBar = _rpBar != NULL; + + // do we need to correct the scrollbar visibility? + if ( bHaveBar && !i_needBar ) + { + if ( _rpBar->IsTracking() ) + _rpBar->EndTracking(); + DELETEZ( _rpBar ); + } + else if ( !bHaveBar && i_needBar ) + { + _rpBar = new ScrollBar( + &_rParent, + WB_DRAG | ( _bHorizontal ? WB_HSCROLL : WB_VSCROLL ) + ); + _rpBar->SetScrollHdl( _rScrollHandler ); + // get some speed into the scrolling .... + lcl_setButtonRepeat( *_rpBar, 0 ); + } + + if ( _rpBar ) + { + _rpBar->SetRange( Range( 0, _nRange ) ); + _rpBar->SetVisibleSize( _nVisibleUnits ); + _rpBar->SetPageSize( _nVisibleUnits ); + _rpBar->SetLineSize( _nLineSize ); + _rpBar->SetThumbPos( _nPosition ); + _rpBar->Show(); + } + } + + //.............................................................................................................. + /** returns the number of rows fitting into the given range, + for the given row height. Partially fitting rows are counted, too, if the + respective parameter says so. + */ + TableSize lcl_getRowsFittingInto( long _nOverallHeight, long _nRowHeightPixel, bool _bAcceptPartialRow = false ) + { + return _bAcceptPartialRow + ? ( _nOverallHeight + ( _nRowHeightPixel - 1 ) ) / _nRowHeightPixel + : _nOverallHeight / _nRowHeightPixel; + } + + //.............................................................................................................. + /** returns the number of columns fitting into the given area, + with the first visible column as given. Partially fitting columns are counted, too, + if the respective parameter says so. + */ + TableSize lcl_getColumnsVisibleWithin( const Rectangle& _rArea, ColPos _nFirstVisibleColumn, + const TableControl_Impl& _rControl, bool _bAcceptPartialRow ) + { + TableSize visibleColumns = 0; + TableColumnGeometry aColumn( _rControl, _rArea, _nFirstVisibleColumn ); + while ( aColumn.isValid() ) + { + if ( !_bAcceptPartialRow ) + if ( aColumn.getRect().Right() > _rArea.Right() ) + // this column is only partially visible, and this is not allowed + break; + + aColumn.moveRight(); + ++visibleColumns; + } + return visibleColumns; + } + + } + + //------------------------------------------------------------------------------------------------------------------ + void TableControl_Impl::impl_ni_updateScrollbars() + { + SuppressCursor aHideCursor( *this ); + + // the width/height of a scrollbar, needed several times below + long const nScrollbarMetrics = m_rAntiImpl.GetSettings().GetStyleSettings().GetScrollBarSize(); + + // determine the playground for the data cells (excluding headers) + // TODO: what if the control is smaller than needed for the headers/scrollbars? + Rectangle aDataCellPlayground( Point( 0, 0 ), m_rAntiImpl.GetOutputSizePixel() ); + aDataCellPlayground.Left() = m_nRowHeaderWidthPixel; + aDataCellPlayground.Top() = m_nColHeaderHeightPixel; + m_nRowCount = m_pModel->getRowCount(); + m_nColumnCount = m_pModel->getColumnCount(); + + if ( m_aColumnWidths.empty() ) + impl_ni_updateColumnWidths(); + OSL_ENSURE( m_aColumnWidths.size() == size_t( m_nColumnCount ), "TableControl_Impl::impl_ni_updateScrollbars: inconsistency!" ); + const long nAllColumnsWidth = m_aColumnWidths.empty() + ? 0 + : m_aColumnWidths[ m_nColumnCount - 1 ].getEnd() - m_aColumnWidths[ 0 ].getStart(); + + const ScrollbarVisibility eVertScrollbar = m_pModel->getVerticalScrollbarVisibility(); + const ScrollbarVisibility eHorzScrollbar = m_pModel->getHorizontalScrollbarVisibility(); + + // do we need a vertical scrollbar? + bool bNeedVerticalScrollbar = lcl_determineScrollbarNeed( + m_nTopRow, eVertScrollbar, aDataCellPlayground.GetHeight(), m_nRowHeightPixel * m_nRowCount ); + bool bFirstRoundVScrollNeed = false; + if ( bNeedVerticalScrollbar ) + { + aDataCellPlayground.Right() -= nScrollbarMetrics; + bFirstRoundVScrollNeed = true; + } + // do we need a horizontal scrollbar? + const bool bNeedHorizontalScrollbar = lcl_determineScrollbarNeed( m_nLeftColumn, eHorzScrollbar, aDataCellPlayground.GetWidth(), nAllColumnsWidth ); + if ( bNeedHorizontalScrollbar ) + { + aDataCellPlayground.Bottom() -= nScrollbarMetrics; + + // now that we just found that we need a horizontal scrollbar, + // the need for a vertical one may have changed, since the horizontal + // SB might just occupy enough space so that not all rows do fit + // anymore + if ( !bFirstRoundVScrollNeed ) + { + bNeedVerticalScrollbar = lcl_determineScrollbarNeed( + m_nTopRow, eVertScrollbar, aDataCellPlayground.GetHeight(), m_nRowHeightPixel * m_nRowCount ); + if ( bNeedVerticalScrollbar ) + { + aDataCellPlayground.Right() -= nScrollbarMetrics; + } + } + } + // create or destroy the vertical scrollbar, as needed + lcl_updateScrollbar( + m_rAntiImpl, + m_pVScroll, + bNeedVerticalScrollbar, + lcl_getRowsFittingInto( aDataCellPlayground.GetHeight(), m_nRowHeightPixel ), + // visible units + m_nTopRow, // current position + 1, // line size + m_nRowCount, // range + false, // vertical + LINK( this, TableControl_Impl, OnScroll ) // scroll handler + ); + // position it + if ( m_pVScroll ) + { + Rectangle aScrollbarArea( + Point( aDataCellPlayground.Right() + 1, 0 ), + Size( nScrollbarMetrics, aDataCellPlayground.Bottom() + 1 ) + ); + m_pVScroll->SetPosSizePixel( + aScrollbarArea.TopLeft(), aScrollbarArea.GetSize() ); + } + + // create or destroy the horizontal scrollbar, as needed + lcl_updateScrollbar( + m_rAntiImpl, + m_pHScroll, + bNeedHorizontalScrollbar, + lcl_getColumnsVisibleWithin( aDataCellPlayground, m_nLeftColumn, *this, false ), + // visible units + m_nLeftColumn, // current position + 1, // line size + m_nColumnCount, // range + true, // horizontal + LINK( this, TableControl_Impl, OnScroll ) // scroll handler + ); + // position it + if ( m_pHScroll ) + { + TableSize const nVisibleUnits = lcl_getColumnsVisibleWithin( aDataCellPlayground, m_nLeftColumn, *this, false ); + TableMetrics const nRange = m_nColumnCount; + if( m_nLeftColumn + nVisibleUnits == nRange - 1 ) + { + if ( m_aColumnWidths[ nRange - 1 ].getStart() - m_aColumnWidths[ m_nLeftColumn ].getEnd() + m_aColumnWidths[ nRange-1 ].getWidth() > aDataCellPlayground.GetWidth() ) + { + m_pHScroll->SetVisibleSize( nVisibleUnits -1 ); + m_pHScroll->SetPageSize( nVisibleUnits - 1 ); + } + } + Rectangle aScrollbarArea( + Point( 0, aDataCellPlayground.Bottom() + 1 ), + Size( aDataCellPlayground.Right() + 1, nScrollbarMetrics ) + ); + m_pHScroll->SetPosSizePixel( + aScrollbarArea.TopLeft(), aScrollbarArea.GetSize() ); + } + + // the corner window connecting the two scrollbars in the lower right corner + bool bHaveScrollCorner = NULL != m_pScrollCorner; + bool bNeedScrollCorner = ( NULL != m_pHScroll ) && ( NULL != m_pVScroll ); + if ( bHaveScrollCorner && !bNeedScrollCorner ) + { + DELETEZ( m_pScrollCorner ); + } + else if ( !bHaveScrollCorner && bNeedScrollCorner ) + { + m_pScrollCorner = new ScrollBarBox( &m_rAntiImpl ); + m_pScrollCorner->SetSizePixel( Size( nScrollbarMetrics, nScrollbarMetrics ) ); + m_pScrollCorner->SetPosPixel( Point( aDataCellPlayground.Right() + 1, aDataCellPlayground.Bottom() + 1 ) ); + m_pScrollCorner->Show(); + } + else if(bHaveScrollCorner && bNeedScrollCorner) + { + m_pScrollCorner->SetPosPixel( Point( aDataCellPlayground.Right() + 1, aDataCellPlayground.Bottom() + 1 ) ); + m_pScrollCorner->Show(); + } + + // resize the data window + m_pDataWindow->SetSizePixel( Size( + aDataCellPlayground.GetWidth() + m_nRowHeaderWidthPixel, + aDataCellPlayground.GetHeight() + m_nColHeaderHeightPixel + ) ); + } + + //------------------------------------------------------------------------------------------------------------------ + void TableControl_Impl::onResize() + { + DBG_CHECK_ME(); + + impl_ni_updateColumnWidths(); + impl_ni_updateScrollbars(); + checkCursorPosition(); + } + + //------------------------------------------------------------------------------------------------------------------ + void TableControl_Impl::doPaintContent( const Rectangle& _rUpdateRect ) + { + DBG_CHECK_ME(); + + if ( !getModel() ) + return; + PTableRenderer pRenderer = getModel()->getRenderer(); + DBG_ASSERT( !!pRenderer, "TableDataWindow::doPaintContent: invalid renderer!" ); + if ( !pRenderer ) + return; + + // our current style settings, to be passed to the renderer + const StyleSettings& rStyle = m_rAntiImpl.GetSettings().GetStyleSettings(); + m_nRowCount = m_pModel->getRowCount(); + // the area occupied by all (at least partially) visible cells, including + // headers + Rectangle const aAllCellsWithHeaders( impl_getAllVisibleCellsArea() ); + + // ............................ + // draw the header column area + if ( m_pModel->hasColumnHeaders() ) + { + TableRowGeometry const aHeaderRow( *this, Rectangle( Point( 0, 0 ), + aAllCellsWithHeaders.BottomRight() ), ROW_COL_HEADERS ); + Rectangle const aColRect(aHeaderRow.getRect()); + pRenderer->PaintHeaderArea( + *m_pDataWindow, aColRect, true, false, rStyle + ); + // Note that strictly, aHeaderRow.getRect() also contains the intersection between column + // and row header area. However, below we go to paint this intersection, again, + // so this hopefully doesn't hurt if we already paint it here. + + for ( TableCellGeometry aCell( aHeaderRow, m_nLeftColumn ); + aCell.isValid(); + aCell.moveRight() + ) + { + if ( _rUpdateRect.GetIntersection( aCell.getRect() ).IsEmpty() ) + continue; + + bool isActiveColumn = ( aCell.getColumn() == getCurrentColumn() ); + bool isSelectedColumn = false; + pRenderer->PaintColumnHeader( aCell.getColumn(), isActiveColumn, isSelectedColumn, + *m_pDataWindow, aCell.getRect(), rStyle ); + } + } + // the area occupied by the row header, if any + Rectangle aRowHeaderArea; + if ( m_pModel->hasRowHeaders() ) + { + aRowHeaderArea = aAllCellsWithHeaders; + aRowHeaderArea.Right() = m_nRowHeaderWidthPixel - 1; + + TableSize const nVisibleRows = impl_getVisibleRows( true ); + TableSize nActualRows = nVisibleRows; + if ( m_nTopRow + nActualRows > m_nRowCount ) + nActualRows = m_nRowCount - m_nTopRow; + aRowHeaderArea.Bottom() = m_nColHeaderHeightPixel + m_nRowHeightPixel * nActualRows - 1; + + pRenderer->PaintHeaderArea( *m_pDataWindow, aRowHeaderArea, false, true, rStyle ); + // Note that strictly, aRowHeaderArea also contains the intersection between column + // and row header area. However, below we go to paint this intersection, again, + // so this hopefully doesn't hurt if we already paint it here. + + if ( m_pModel->hasColumnHeaders() ) + { + TableCellGeometry const aIntersection( *this, Rectangle( Point( 0, 0 ), + aAllCellsWithHeaders.BottomRight() ), COL_ROW_HEADERS, ROW_COL_HEADERS ); + Rectangle const aInters( aIntersection.getRect() ); + pRenderer->PaintHeaderArea( + *m_pDataWindow, aInters, true, true, rStyle + ); + } + } + + // ............................ + // draw the table content row by row + + TableSize colCount = getModel()->getColumnCount(); + + // paint all rows + Rectangle const aAllDataCellsArea( impl_getAllVisibleDataCellArea() ); + for ( TableRowGeometry aRowIterator( *this, aAllCellsWithHeaders, getTopRow() ); + aRowIterator.isValid(); + aRowIterator.moveDown() ) + { + if ( _rUpdateRect.GetIntersection( aRowIterator.getRect() ).IsEmpty() ) + continue; + + bool const isActiveRow = ( aRowIterator.getRow() == getCurrentRow() ); + bool const isSelectedRow = isRowSelected( aRowIterator.getRow() ); + + Rectangle const aRect = aRowIterator.getRect().GetIntersection( aAllDataCellsArea ); + + // give the redenderer a chance to prepare the row + pRenderer->PrepareRow( + aRowIterator.getRow(), isActiveRow, isSelectedRow, + *m_pDataWindow, aRect, rStyle + ); + + // paint the row header + if ( m_pModel->hasRowHeaders() ) + { + const Rectangle aCurrentRowHeader( aRowHeaderArea.GetIntersection( aRowIterator.getRect() ) ); + pRenderer->PaintRowHeader( isActiveRow, isSelectedRow, *m_pDataWindow, aCurrentRowHeader, + rStyle ); + } + + if ( !colCount ) + continue; + + // paint all cells in this row + for ( TableCellGeometry aCell( aRowIterator, m_nLeftColumn ); + aCell.isValid(); + aCell.moveRight() + ) + { + bool isSelectedColumn = false; + pRenderer->PaintCell( aCell.getColumn(), isSelectedRow || isSelectedColumn, isActiveRow, + *m_pDataWindow, aCell.getRect(), rStyle ); + } + } + } + //------------------------------------------------------------------------------------------------------------------ + void TableControl_Impl::hideCursor() + { + DBG_CHECK_ME(); + + if ( ++m_nCursorHidden == 1 ) + impl_ni_doSwitchCursor( false ); + } + + //------------------------------------------------------------------------------------------------------------------ + void TableControl_Impl::showCursor() + { + DBG_CHECK_ME(); + + DBG_ASSERT( m_nCursorHidden > 0, "TableControl_Impl::showCursor: cursor not hidden!" ); + if ( --m_nCursorHidden == 0 ) + impl_ni_doSwitchCursor( true ); + } + + //------------------------------------------------------------------------------------------------------------------ + bool TableControl_Impl::dispatchAction( TableControlAction _eAction ) + { + DBG_CHECK_ME(); + + bool bSuccess = false; + bool selectionChanged = false; + + Rectangle rCells; + switch ( _eAction ) + { + case cursorDown: + if(m_pSelEngine->GetSelectionMode() == SINGLE_SELECTION) + { + //if other rows already selected, deselect them + if(m_aSelectedRows.size()>0) + { + for(std::vector<RowPos>::iterator it=m_aSelectedRows.begin(); + it!=m_aSelectedRows.end();++it) + { + invalidateSelectedRegion(*it, *it, rCells); + } + m_aSelectedRows.clear(); + } + if(m_nCurRow < m_nRowCount-1) + { + ++m_nCurRow; + m_aSelectedRows.push_back(m_nCurRow); + } + else + m_aSelectedRows.push_back(m_nCurRow); + invalidateSelectedRegion(m_nCurRow, m_nCurRow, rCells); + ensureVisible(m_nCurColumn,m_nCurRow,false); + selectionChanged = true; + bSuccess = true; + } + else + { + if ( m_nCurRow < m_nRowCount - 1 ) + bSuccess = goTo( m_nCurColumn, m_nCurRow + 1 ); + } + break; + + case cursorUp: + if(m_pSelEngine->GetSelectionMode() == SINGLE_SELECTION) + { + if(m_aSelectedRows.size()>0) + { + for(std::vector<RowPos>::iterator it=m_aSelectedRows.begin(); + it!=m_aSelectedRows.end();++it) + { + invalidateSelectedRegion(*it, *it, rCells); + } + m_aSelectedRows.clear(); + } + if(m_nCurRow>0) + { + --m_nCurRow; + m_aSelectedRows.push_back(m_nCurRow); + invalidateSelectedRegion(m_nCurRow, m_nCurRow, rCells); + } + else + { + m_aSelectedRows.push_back(m_nCurRow); + invalidateSelectedRegion(m_nCurRow, m_nCurRow, rCells); + } + ensureVisible(m_nCurColumn,m_nCurRow,false); + selectionChanged = true; + bSuccess = true; + } + else + { + if ( m_nCurRow > 0 ) + bSuccess = goTo( m_nCurColumn, m_nCurRow - 1 ); + } + break; + case cursorLeft: + if ( m_nCurColumn > 0 ) + bSuccess = goTo( m_nCurColumn - 1, m_nCurRow ); + else + if ( ( m_nCurColumn == 0) && ( m_nCurRow > 0 ) ) + bSuccess = goTo( m_nColumnCount - 1, m_nCurRow - 1 ); + break; + + case cursorRight: + if ( m_nCurColumn < m_nColumnCount - 1 ) + bSuccess = goTo( m_nCurColumn + 1, m_nCurRow ); + else + if ( ( m_nCurColumn == m_nColumnCount - 1 ) && ( m_nCurRow < m_nRowCount - 1 ) ) + bSuccess = goTo( 0, m_nCurRow + 1 ); + break; + + case cursorToLineStart: + bSuccess = goTo( 0, m_nCurRow ); + break; + + case cursorToLineEnd: + bSuccess = goTo( m_nColumnCount - 1, m_nCurRow ); + break; + + case cursorToFirstLine: + bSuccess = goTo( m_nCurColumn, 0 ); + break; + + case cursorToLastLine: + bSuccess = goTo( m_nCurColumn, m_nRowCount - 1 ); + break; + + case cursorPageUp: + { + RowPos nNewRow = ::std::max( (RowPos)0, m_nCurRow - impl_getVisibleRows( false ) ); + bSuccess = goTo( m_nCurColumn, nNewRow ); + } + break; + + case cursorPageDown: + { + RowPos nNewRow = ::std::min( m_nRowCount - 1, m_nCurRow + impl_getVisibleRows( false ) ); + bSuccess = goTo( m_nCurColumn, nNewRow ); + } + break; + + case cursorTopLeft: + bSuccess = goTo( 0, 0 ); + break; + + case cursorBottomRight: + bSuccess = goTo( m_nColumnCount - 1, m_nRowCount - 1 ); + break; + + case cursorSelectRow: + { + if(m_pSelEngine->GetSelectionMode() == NO_SELECTION) + return bSuccess = false; + //pos is the position of the current row in the vector of selected rows, if current row is selected + int pos = getRowSelectedNumber(m_aSelectedRows, m_nCurRow); + //if current row is selected, it should be deselected, when ALT+SPACE are pressed + if(pos>-1) + { + m_aSelectedRows.erase(m_aSelectedRows.begin()+pos); + if(m_aSelectedRows.empty() && m_nAnchor != -1) + m_nAnchor = -1; + } + //else select the row->put it in the vector + else + m_aSelectedRows.push_back(m_nCurRow); + invalidateSelectedRegion(m_nCurRow, m_nCurRow, rCells); + selectionChanged = true; + bSuccess = true; + } + break; + case cursorSelectRowUp: + { + if(m_pSelEngine->GetSelectionMode() == NO_SELECTION) + return bSuccess = false; + else if(m_pSelEngine->GetSelectionMode() == SINGLE_SELECTION) + { + //if there are other selected rows, deselect them + return false; + } + else + { + //there are other selected rows + if(m_aSelectedRows.size()>0) + { + //the anchor wasn't set -> a region is not selected, that's why clear all selection + //and select the current row + if(m_nAnchor==-1) + { + for(std::vector<RowPos>::iterator it=m_aSelectedRows.begin(); + it!=m_aSelectedRows.end();++it) + { + invalidateSelectedRegion(*it, *it, rCells); + } + m_aSelectedRows.clear(); + m_aSelectedRows.push_back(m_nCurRow); + invalidateSelectedRegion(m_nCurRow, m_nCurRow, rCells); + } + else + { + //a region is already selected, prevRow is last selected row and the row above - nextRow - should be selected + int prevRow = getRowSelectedNumber(m_aSelectedRows, m_nCurRow); + int nextRow = getRowSelectedNumber(m_aSelectedRows, m_nCurRow-1); + if(prevRow>-1) + { + //if m_nCurRow isn't the upper one, can move up, otherwise not + if(m_nCurRow>0) + m_nCurRow--; + else + return bSuccess = true; + //if nextRow already selected, deselect it, otherwise select it + if(nextRow>-1 && m_aSelectedRows[nextRow] == m_nCurRow) + { + m_aSelectedRows.erase(m_aSelectedRows.begin()+prevRow); + invalidateSelectedRegion(m_nCurRow+1, m_nCurRow+1, rCells); + } + else + { + m_aSelectedRows.push_back(m_nCurRow); + invalidateSelectedRegion(m_nCurRow, m_nCurRow, rCells); + } + } + else + { + if(m_nCurRow>0) + { + m_aSelectedRows.push_back(m_nCurRow); + m_nCurRow--; + m_aSelectedRows.push_back(m_nCurRow); + invalidateSelectedRegion(m_nCurRow+1, m_nCurRow, rCells); + } + } + } + } + else + { + //if nothing is selected and the current row isn't the upper one + //select the current and one row above + //otherwise select only the upper row + if(m_nCurRow>0) + { + m_aSelectedRows.push_back(m_nCurRow); + m_nCurRow--; + m_aSelectedRows.push_back(m_nCurRow); + invalidateSelectedRegion(m_nCurRow+1, m_nCurRow, rCells); + } + else + { + m_aSelectedRows.push_back(m_nCurRow); + invalidateSelectedRegion(m_nCurRow, m_nCurRow, rCells); + } + } + m_pSelEngine->SetAnchor(sal_True); + m_nAnchor = m_nCurRow; + ensureVisible(m_nCurColumn, m_nCurRow, false); + selectionChanged = true; + bSuccess = true; + } + } + break; + case cursorSelectRowDown: + { + if(m_pSelEngine->GetSelectionMode() == NO_SELECTION) + bSuccess = false; + else if(m_pSelEngine->GetSelectionMode() == SINGLE_SELECTION) + { + bSuccess = false; + } + else + { + if(m_aSelectedRows.size()>0) + { + //the anchor wasn't set -> a region is not selected, that's why clear all selection + //and select the current row + if(m_nAnchor==-1) + { + for(std::vector<RowPos>::iterator it=m_aSelectedRows.begin(); + it!=m_aSelectedRows.end();++it) + { + invalidateSelectedRegion(*it, *it, rCells); + } + m_aSelectedRows.clear(); + m_aSelectedRows.push_back(m_nCurRow); + invalidateSelectedRegion(m_nCurRow, m_nCurRow, rCells); + } + else + { + //a region is already selected, prevRow is last selected row and the row beneath - nextRow - should be selected + int prevRow = getRowSelectedNumber(m_aSelectedRows, m_nCurRow); + int nextRow = getRowSelectedNumber(m_aSelectedRows, m_nCurRow+1); + if(prevRow>-1) + { + //if m_nCurRow isn't the last one, can move down, otherwise not + if(m_nCurRow<m_nRowCount-1) + m_nCurRow++; + else + return bSuccess = true; + //if next row already selected, deselect it, otherwise select it + if(nextRow>-1 && m_aSelectedRows[nextRow] == m_nCurRow) + { + m_aSelectedRows.erase(m_aSelectedRows.begin()+prevRow); + invalidateSelectedRegion(m_nCurRow-1, m_nCurRow-1, rCells); + } + else + { + m_aSelectedRows.push_back(m_nCurRow); + invalidateSelectedRegion(m_nCurRow, m_nCurRow, rCells); + } + } + else + { + if(m_nCurRow<m_nRowCount-1) + { + m_aSelectedRows.push_back(m_nCurRow); + m_nCurRow++; + m_aSelectedRows.push_back(m_nCurRow); + invalidateSelectedRegion(m_nCurRow-1, m_nCurRow, rCells); + } + } + } + } + else + { + //there wasn't any selection, select current and row beneath, otherwise only row beneath + if(m_nCurRow<m_nRowCount-1) + { + m_aSelectedRows.push_back(m_nCurRow); + m_nCurRow++; + m_aSelectedRows.push_back(m_nCurRow); + invalidateSelectedRegion(m_nCurRow-1, m_nCurRow, rCells); + } + else + { + m_aSelectedRows.push_back(m_nCurRow); + invalidateSelectedRegion(m_nCurRow, m_nCurRow, rCells); + } + } + m_pSelEngine->SetAnchor(sal_True); + m_nAnchor = m_nCurRow; + ensureVisible(m_nCurColumn, m_nCurRow, false); + selectionChanged = true; + bSuccess = true; + } + } + break; + + case cursorSelectRowAreaTop: + { + if(m_pSelEngine->GetSelectionMode() == NO_SELECTION) + bSuccess = false; + else if(m_pSelEngine->GetSelectionMode() == SINGLE_SELECTION) + bSuccess = false; + else + { + //select the region between the current and the upper row + RowPos iter = m_nCurRow; + invalidateSelectedRegion(m_nCurRow, 0, rCells); + //put the rows in vector + while(iter>=0) + { + if ( !isRowSelected( iter ) ) + m_aSelectedRows.push_back(iter); + --iter; + } + m_nCurRow = 0; + m_nAnchor = m_nCurRow; + m_pSelEngine->SetAnchor(sal_True); + ensureVisible(m_nCurColumn, 0, false); + selectionChanged = true; + bSuccess = true; + } + } + break; + + case cursorSelectRowAreaBottom: + { + if(m_pSelEngine->GetSelectionMode() == NO_SELECTION) + return bSuccess = false; + else if(m_pSelEngine->GetSelectionMode() == SINGLE_SELECTION) + return bSuccess = false; + //select the region between the current and the last row + RowPos iter = m_nCurRow; + invalidateSelectedRegion(m_nCurRow, m_nRowCount-1, rCells); + //put the rows in the vector + while(iter<=m_nRowCount) + { + if ( !isRowSelected( iter ) ) + m_aSelectedRows.push_back(iter); + ++iter; + } + m_nCurRow = m_nRowCount-1; + m_nAnchor = m_nCurRow; + m_pSelEngine->SetAnchor(sal_True); + ensureVisible(m_nCurColumn, m_nRowCount-1, false); + selectionChanged = true; + bSuccess = true; + } + break; + default: + OSL_FAIL( "TableControl_Impl::dispatchAction: unsupported action!" ); + break; + } + + if ( bSuccess && selectionChanged ) + { + m_rAntiImpl.Select(); + } + + return bSuccess; + } + + //------------------------------------------------------------------------------------------------------------------ + void TableControl_Impl::impl_ni_doSwitchCursor( bool _bShow ) + { + PTableRenderer pRenderer = !!m_pModel ? m_pModel->getRenderer() : PTableRenderer(); + if ( !!pRenderer ) + { + Rectangle aCellRect; + impl_getCellRect( m_nCurColumn, m_nCurRow, aCellRect ); + if ( _bShow ) + pRenderer->ShowCellCursor( *m_pDataWindow, aCellRect ); + else + pRenderer->HideCellCursor( *m_pDataWindow, aCellRect ); + } + } + + //------------------------------------------------------------------------------------------------------------------ + void TableControl_Impl::impl_getCellRect( ColPos _nColumn, RowPos _nRow, Rectangle& _rCellRect ) const + { + DBG_CHECK_ME(); + + if ( !m_pModel + || ( COL_INVALID == _nColumn ) + || ( ROW_INVALID == _nRow ) + ) + { + _rCellRect.SetEmpty(); + return; + } + + TableCellGeometry aCell( *this, impl_getAllVisibleCellsArea(), _nColumn, _nRow ); + _rCellRect = aCell.getRect(); + } + + //------------------------------------------------------------------------------------------------------------------ + RowPos TableControl_Impl::getRowAtPoint( const Point& rPoint ) const + { + DBG_CHECK_ME(); + return impl_getRowForAbscissa( rPoint.Y() ); + } + + //------------------------------------------------------------------------------------------------------------------ + ColPos TableControl_Impl::getColAtPoint( const Point& rPoint ) const + { + DBG_CHECK_ME(); + return impl_getColumnForOrdinate( rPoint.X() ); + } + + //------------------------------------------------------------------------------------------------------------------ + TableCell TableControl_Impl::hitTest( Point const & i_point ) const + { + TableCell aCell( getColAtPoint( i_point ), getRowAtPoint( i_point ) ); + if ( aCell.nColumn > COL_ROW_HEADERS ) + { + PColumnModel const pColumn = m_pModel->getColumnModel( aCell.nColumn ); + MutableColumnMetrics const & rColInfo( m_aColumnWidths[ aCell.nColumn ] ); + if ( ( rColInfo.getEnd() - 3 <= i_point.X() ) + && ( rColInfo.getEnd() >= i_point.X() ) + && pColumn->isResizable() + ) + { + aCell.eArea = ColumnDivider; + } + } + return aCell; + } + + //------------------------------------------------------------------------------------------------------------------ + ColumnMetrics TableControl_Impl::getColumnMetrics( ColPos const i_column ) const + { + DBG_CHECK_ME(); + + ENSURE_OR_RETURN( ( i_column >= 0 ) && ( i_column < m_pModel->getColumnCount() ), + "TableControl_Impl::getColumnMetrics: illegal column index!", ColumnMetrics() ); + return (ColumnMetrics const &)m_aColumnWidths[ i_column ]; + } + + //------------------------------------------------------------------------------------------------------------------ + PTableModel TableControl_Impl::getModel() const + { + return m_pModel; + } + + //------------------------------------------------------------------------------------------------------------------ + RowPos TableControl_Impl::getCurrentColumn() const + { + return m_nCurColumn; + } + + //------------------------------------------------------------------------------------------------------------------ + RowPos TableControl_Impl::getCurrentRow() const + { + return m_nCurRow; + } + + //------------------------------------------------------------------------------------------------------------------ + ::Size TableControl_Impl::getTableSizePixel() const + { + return m_pDataWindow->GetOutputSizePixel(); + } + + //------------------------------------------------------------------------------------------------------------------ + void TableControl_Impl::setPointer( Pointer const & i_pointer ) + { + DBG_CHECK_ME(); + m_pDataWindow->SetPointer( i_pointer ); + } + + //------------------------------------------------------------------------------------------------------------------ + void TableControl_Impl::captureMouse() + { + m_pDataWindow->CaptureMouse(); + } + + //------------------------------------------------------------------------------------------------------------------ + void TableControl_Impl::releaseMouse() + { + m_pDataWindow->ReleaseMouse(); + } + + //------------------------------------------------------------------------------------------------------------------ + void TableControl_Impl::invalidate( TableArea const i_what ) + { + switch ( i_what ) + { + case TableAreaColumnHeaders: + m_pDataWindow->Invalidate( calcHeaderRect( true ) ); + break; + + case TableAreaRowHeaders: + m_pDataWindow->Invalidate( calcHeaderRect( false ) ); + break; + + case TableAreaDataArea: + m_pDataWindow->Invalidate( impl_getAllVisibleDataCellArea() ); + break; + + case TableAreaAll: + m_pDataWindow->Invalidate(); + break; + } + } + + //------------------------------------------------------------------------------------------------------------------ + long TableControl_Impl::pixelWidthToAppFont( long const i_pixels ) const + { + return m_pDataWindow->PixelToLogic( Size( i_pixels, 0 ), MAP_APPFONT ).Width(); + } + + //------------------------------------------------------------------------------------------------------------------ + void TableControl_Impl::hideTracking() + { + m_pDataWindow->HideTracking(); + } + + //------------------------------------------------------------------------------------------------------------------ + void TableControl_Impl::showTracking( Rectangle const & i_location, sal_uInt16 const i_flags ) + { + m_pDataWindow->ShowTracking( i_location, i_flags ); + } + + //------------------------------------------------------------------------------------------------------------------ + bool TableControl_Impl::activateCell( ColPos const i_col, RowPos const i_row ) + { + DBG_CHECK_ME(); + return goTo( i_col, i_row ); + } + + //------------------------------------------------------------------------------------------------------------------ + void TableControl_Impl::invalidateSelectedRegion(RowPos _nPrevRow, RowPos _nCurRow, Rectangle& _rCellRect) + { + DBG_CHECK_ME(); + //get the visible area of the table control and set the Left and right border of the region to be repainted + Rectangle const aAllCells( impl_getAllVisibleCellsArea() ); + _rCellRect.Left() = aAllCells.Left(); + _rCellRect.Right() = aAllCells.Right(); + //if only one row is selected + if(_nPrevRow == _nCurRow) + { + Rectangle aCellRect; + impl_getCellRect( m_nCurColumn, _nCurRow, aCellRect ); + _rCellRect.Top() = aCellRect.Top(); + _rCellRect.Bottom() = aCellRect.Bottom(); + } + //if the region is above the current row + else if(_nPrevRow < _nCurRow ) + { + Rectangle aCellRect; + impl_getCellRect( m_nCurColumn, _nPrevRow, aCellRect ); + _rCellRect.Top() = aCellRect.Top(); + impl_getCellRect( m_nCurColumn, _nCurRow, aCellRect ); + _rCellRect.Bottom() = aCellRect.Bottom(); + } + //if the region is beneath the current row + else + { + Rectangle aCellRect; + impl_getCellRect( m_nCurColumn, _nCurRow, aCellRect ); + _rCellRect.Top() = aCellRect.Top(); + impl_getCellRect( m_nCurColumn, _nPrevRow, aCellRect ); + _rCellRect.Bottom() = aCellRect.Bottom(); + } + m_pDataWindow->Invalidate(_rCellRect); + } + //------------------------------------------------------------------------------------------------------------------ + void TableControl_Impl::invalidateRowRange( RowPos const i_firstRow, RowPos const i_lastRow ) + { + if ( m_nCursorHidden == 2 ) + // WTF? what kind of hack is this? + --m_nCursorHidden; + + RowPos const firstRow = i_firstRow < m_nTopRow ? m_nTopRow : i_firstRow; + RowPos const lastVisibleRow = m_nTopRow + impl_getVisibleRows( true ) - 1; + RowPos const lastRow = ( ( i_lastRow == ROW_INVALID ) || ( i_lastRow > lastVisibleRow ) ) ? lastVisibleRow : i_lastRow; + + Rectangle aInvalidateRect; + + Rectangle const aVisibleCellsArea( impl_getAllVisibleCellsArea() ); + TableRowGeometry aRow( *this, aVisibleCellsArea, firstRow, true ); + while ( aRow.isValid() && ( aRow.getRow() <= lastRow ) ) + { + aInvalidateRect.Union( aRow.getRect() ); + aRow.moveDown(); + } + + if ( i_lastRow == ROW_INVALID ) + aInvalidateRect.Bottom() = m_pDataWindow->GetOutputSizePixel().Height(); + + m_pDataWindow->Invalidate( aInvalidateRect ); + } + + //------------------------------------------------------------------------------ + void TableControl_Impl::checkCursorPosition() + { + DBG_CHECK_ME(); + + TableSize nVisibleRows = impl_getVisibleRows(true); + TableSize nVisibleCols = impl_getVisibleColumns(true); + if ( ( m_nTopRow + nVisibleRows > m_nRowCount ) + && ( m_nRowCount >= nVisibleRows ) + ) + { + --m_nTopRow; + } + else + { + m_nTopRow = 0; + } + + if ( ( m_nLeftColumn + nVisibleCols > m_nColumnCount ) + && ( m_nColumnCount >= nVisibleCols ) + ) + { + --m_nLeftColumn; + } + else + { + m_nLeftColumn = 0; + } + + m_pDataWindow->Invalidate(); + } + + //-------------------------------------------------------------------- + TableSize TableControl_Impl::impl_getVisibleRows( bool _bAcceptPartialRow ) const + { + DBG_CHECK_ME(); + + DBG_ASSERT( m_pDataWindow, "TableControl_Impl::impl_getVisibleRows: no data window!" ); + + return lcl_getRowsFittingInto( + m_pDataWindow->GetOutputSizePixel().Height() - m_nColHeaderHeightPixel, + m_nRowHeightPixel, + _bAcceptPartialRow + ); + } + + //-------------------------------------------------------------------- + TableSize TableControl_Impl::impl_getVisibleColumns( bool _bAcceptPartialCol ) const + { + DBG_CHECK_ME(); + + DBG_ASSERT( m_pDataWindow, "TableControl_Impl::impl_getVisibleColumns: no data window!" ); + + return lcl_getColumnsVisibleWithin( + Rectangle( Point( 0, 0 ), m_pDataWindow->GetOutputSizePixel() ), + m_nLeftColumn, + *this, + _bAcceptPartialCol + ); + } + + //-------------------------------------------------------------------- + bool TableControl_Impl::goTo( ColPos _nColumn, RowPos _nRow ) + { + DBG_CHECK_ME(); + + // TODO: give veto listeners a chance + + if ( ( _nColumn < 0 ) || ( _nColumn >= m_nColumnCount ) + || ( _nRow < 0 ) || ( _nRow >= m_nRowCount ) + ) + { + OSL_ENSURE( false, "TableControl_Impl::goTo: invalid row or column index!" ); + return false; + } + + SuppressCursor aHideCursor( *this ); + m_nCurColumn = _nColumn; + m_nCurRow = _nRow; + + // ensure that the new cell is visible + ensureVisible( m_nCurColumn, m_nCurRow, false ); + return true; + } + + //-------------------------------------------------------------------- + void TableControl_Impl::ensureVisible( ColPos _nColumn, RowPos _nRow, bool _bAcceptPartialVisibility ) + { + DBG_CHECK_ME(); + DBG_ASSERT( ( _nColumn >= 0 ) && ( _nColumn < m_nColumnCount ) + && ( _nRow >= 0 ) && ( _nRow < m_nRowCount ), + "TableControl_Impl::ensureVisible: invalid coordinates!" ); + + SuppressCursor aHideCursor( *this ); + + if ( _nColumn < m_nLeftColumn ) + impl_scrollColumns( _nColumn - m_nLeftColumn ); + else + { + TableSize nVisibleColumns = impl_getVisibleColumns( _bAcceptPartialVisibility ); + if ( _nColumn > m_nLeftColumn + nVisibleColumns - 1 ) + { + impl_scrollColumns( _nColumn - ( m_nLeftColumn + nVisibleColumns - 1 ) ); + // TODO: since not all columns have the same width, this might in theory result + // in the column still not being visible. + } + } + + if ( _nRow < m_nTopRow ) + impl_scrollRows( _nRow - m_nTopRow ); + else + { + TableSize nVisibleRows = impl_getVisibleRows( _bAcceptPartialVisibility ); + if ( _nRow > m_nTopRow + nVisibleRows - 1 ) + impl_scrollRows( _nRow - ( m_nTopRow + nVisibleRows - 1 ) ); + } + } + + //-------------------------------------------------------------------- + ::rtl::OUString TableControl_Impl::getCellContentAsString( RowPos const i_row, ColPos const i_col ) + { + ::com::sun::star::uno::Any content; + m_pModel->getCellContent( i_col, i_row, content ); + return CellValueConversion::convertToString( content ); + } + + //-------------------------------------------------------------------- + TableSize TableControl_Impl::impl_ni_ScrollRows( TableSize _nRowDelta ) + { + // compute new top row + RowPos nNewTopRow = + ::std::max( + ::std::min( (RowPos)( m_nTopRow + _nRowDelta ), (RowPos)( m_nRowCount - 1 ) ), + (RowPos)0 + ); + + RowPos nOldTopRow = m_nTopRow; + m_nTopRow = nNewTopRow; + + // if updates are enabled currently, scroll the viewport + if ( m_nTopRow != nOldTopRow ) + { + DBG_SUSPEND_INV( INV_SCROLL_POSITION ); + SuppressCursor aHideCursor( *this ); + // TODO: call a onStartScroll at our listener (or better an own onStartScroll, + // which hides the cursor and then calls the listener) + // Same for onEndScroll + + // scroll the view port, if possible + long nPixelDelta = m_nRowHeightPixel * ( m_nTopRow - nOldTopRow ); + + Rectangle aDataArea( Point( 0, m_nColHeaderHeightPixel ), m_pDataWindow->GetOutputSizePixel() ); + + if ( m_pDataWindow->GetBackground().IsScrollable() + && abs( nPixelDelta ) < aDataArea.GetHeight() + ) + { + m_pDataWindow->Scroll( 0, (long)-nPixelDelta, aDataArea, SCROLL_CLIP | SCROLL_UPDATE | SCROLL_CHILDREN); + } + else + m_pDataWindow->Invalidate( INVALIDATE_UPDATE ); + + // update the position at the vertical scrollbar + m_pVScroll->SetThumbPos( m_nTopRow ); + } + + // The scroll bar availaility might change when we scrolled. This is because we do not hide + // the scrollbar when it is, in theory, unnecessary, but currently at a position > 0. In this case, it will + // be auto-hidden when it's scrolled back to pos 0. + if ( m_nTopRow == 0 ) + m_rAntiImpl.PostUserEvent( LINK( this, TableControl_Impl, OnUpdateScrollbars ) ); + + return (TableSize)( m_nTopRow - nOldTopRow ); + } + + //-------------------------------------------------------------------- + TableSize TableControl_Impl::impl_scrollRows( TableSize const i_rowDelta ) + { + DBG_CHECK_ME(); + return impl_ni_ScrollRows( i_rowDelta ); + } + + //-------------------------------------------------------------------- + TableSize TableControl_Impl::impl_ni_ScrollColumns( TableSize _nColumnDelta ) + { + // compute new left column + const ColPos nNewLeftColumn = + ::std::max( + ::std::min( (ColPos)( m_nLeftColumn + _nColumnDelta ), (ColPos)( m_nColumnCount - 1 ) ), + (ColPos)0 + ); + + const ColPos nOldLeftColumn = m_nLeftColumn; + m_nLeftColumn = nNewLeftColumn; + + // if updates are enabled currently, scroll the viewport + if ( m_nLeftColumn != nOldLeftColumn ) + { + DBG_SUSPEND_INV( INV_SCROLL_POSITION ); + SuppressCursor aHideCursor( *this ); + // TODO: call a onStartScroll at our listener (or better an own onStartScroll, + // which hides the cursor and then calls the listener) + // Same for onEndScroll + + // scroll the view port, if possible + const Rectangle aDataArea( Point( m_nRowHeaderWidthPixel, 0 ), m_pDataWindow->GetOutputSizePixel() ); + + long nPixelDelta = + m_aColumnWidths[ nOldLeftColumn ].getStart() + - m_aColumnWidths[ m_nLeftColumn ].getStart(); + + // update our column positions + // Do this *before* scrolling, as SCROLL_UPDATE will trigger a paint, which already needs the correct + // information in m_aColumnWidths + for ( ColumnPositions::iterator colPos = m_aColumnWidths.begin(); + colPos != m_aColumnWidths.end(); + ++colPos + ) + { + colPos->move( nPixelDelta ); + } + + // scroll the window content (if supported and possible), or invalidate the complete window + if ( m_pDataWindow->GetBackground().IsScrollable() + && abs( nPixelDelta ) < aDataArea.GetWidth() + ) + { + m_pDataWindow->Scroll( nPixelDelta, 0, aDataArea, SCROLL_CLIP | SCROLL_UPDATE ); + } + else + m_pDataWindow->Invalidate( INVALIDATE_UPDATE ); + + // update the position at the horizontal scrollbar + m_pHScroll->SetThumbPos( m_nLeftColumn ); + } + + // The scroll bar availaility might change when we scrolled. This is because we do not hide + // the scrollbar when it is, in theory, unnecessary, but currently at a position > 0. In this case, it will + // be auto-hidden when it's scrolled back to pos 0. + if ( m_nLeftColumn == 0 ) + m_rAntiImpl.PostUserEvent( LINK( this, TableControl_Impl, OnUpdateScrollbars ) ); + + return (TableSize)( m_nLeftColumn - nOldLeftColumn ); + } + + //------------------------------------------------------------------------------------------------------------------ + TableSize TableControl_Impl::impl_scrollColumns( TableSize const i_columnDelta ) + { + DBG_CHECK_ME(); + return impl_ni_ScrollColumns( i_columnDelta ); + } + + //------------------------------------------------------------------------------------------------------------------ + SelectionEngine* TableControl_Impl::getSelEngine() + { + return m_pSelEngine; + } + + //------------------------------------------------------------------------------------------------------------------ + ScrollBar* TableControl_Impl::getHorzScrollbar() + { + return m_pHScroll; + } + + //------------------------------------------------------------------------------------------------------------------ + ScrollBar* TableControl_Impl::getVertScrollbar() + { + return m_pVScroll; + } + + //------------------------------------------------------------------------------------------------------------------ + bool TableControl_Impl::isRowSelected( RowPos i_row ) const + { + return ::std::find( m_aSelectedRows.begin(), m_aSelectedRows.end(), i_row ) != m_aSelectedRows.end(); + } + + //------------------------------------------------------------------------------------------------------------------ + RowPos TableControl_Impl::getSelectedRowIndex( size_t const i_selectionIndex ) const + { + if ( i_selectionIndex < m_aSelectedRows.size() ) + return m_aSelectedRows[ i_selectionIndex ]; + return ROW_INVALID; + } + + //------------------------------------------------------------------------------------------------------------------ + int TableControl_Impl::getRowSelectedNumber(const ::std::vector<RowPos>& selectedRows, RowPos current) + { + std::vector<RowPos>::const_iterator it = ::std::find(selectedRows.begin(),selectedRows.end(),current); + if ( it != selectedRows.end() ) + { + return it - selectedRows.begin(); + } + return -1; + } + + //-------------------------------------------------------------------- + ColPos TableControl_Impl::impl_getColumnForOrdinate( long const i_ordinate ) const + { + DBG_CHECK_ME(); + + if ( ( m_aColumnWidths.empty() ) || ( i_ordinate < 0 ) ) + return COL_INVALID; + + if ( i_ordinate < m_nRowHeaderWidthPixel ) + return COL_ROW_HEADERS; + + long const ordinate = i_ordinate - m_nRowHeaderWidthPixel; + + ColumnPositions::const_iterator lowerBound = ::std::lower_bound( + m_aColumnWidths.begin(), + m_aColumnWidths.end(), + ordinate + 1, + ColumnInfoPositionLess() + ); + if ( lowerBound == m_aColumnWidths.end() ) + { + // point is *behind* the start of the last column ... + if ( ordinate < m_aColumnWidths.rbegin()->getEnd() ) + // ... but still before its end + return m_nColumnCount - 1; + return COL_INVALID; + } + return lowerBound - m_aColumnWidths.begin(); + } + + //-------------------------------------------------------------------- + RowPos TableControl_Impl::impl_getRowForAbscissa( long const i_abscissa ) const + { + DBG_CHECK_ME(); + + if ( i_abscissa < 0 ) + return ROW_INVALID; + + if ( i_abscissa < m_nColHeaderHeightPixel ) + return ROW_COL_HEADERS; + + long const abscissa = i_abscissa - m_nColHeaderHeightPixel; + long const row = m_nTopRow + abscissa / m_nRowHeightPixel; + return row < m_pModel->getRowCount() ? row : ROW_INVALID; + } + + //-------------------------------------------------------------------- + bool TableControl_Impl::markRowAsDeselected( RowPos const i_rowIndex ) + { + DBG_CHECK_ME(); + + ::std::vector< RowPos >::iterator selPos = ::std::find( m_aSelectedRows.begin(), m_aSelectedRows.end(), i_rowIndex ); + if ( selPos == m_aSelectedRows.end() ) + return false; + + m_aSelectedRows.erase( selPos ); + return true; + } + + //-------------------------------------------------------------------- + bool TableControl_Impl::markRowAsSelected( RowPos const i_rowIndex ) + { + DBG_CHECK_ME(); + + if ( isRowSelected( i_rowIndex ) ) + return false; + + SelectionMode const eSelMode = getSelEngine()->GetSelectionMode(); + switch ( eSelMode ) + { + case SINGLE_SELECTION: + if ( !m_aSelectedRows.empty() ) + { + OSL_ENSURE( m_aSelectedRows.size() == 1, "TableControl::markRowAsSelected: SingleSelection with more than one selected element?" ); + m_aSelectedRows[0] = i_rowIndex; + break; + } + // fall through + + case MULTIPLE_SELECTION: + m_aSelectedRows.push_back( i_rowIndex ); + break; + + default: + OSL_ENSURE( false, "TableControl_Impl::markRowAsSelected: unsupported selection mode!" ); + return false; + } + + return true; + } + + //-------------------------------------------------------------------- + bool TableControl_Impl::markAllRowsAsDeselected() + { + if ( m_aSelectedRows.empty() ) + return false; + + m_aSelectedRows.clear(); + return true; + } + + //-------------------------------------------------------------------- + bool TableControl_Impl::markAllRowsAsSelected() + { + DBG_CHECK_ME(); + + SelectionMode const eSelMode = getSelEngine()->GetSelectionMode(); + ENSURE_OR_RETURN_FALSE( eSelMode == MULTIPLE_SELECTION, "TableControl_Impl::markAllRowsAsSelected: unsupported selection mode!" ); + + if ( m_aSelectedRows.size() == size_t( m_pModel->getRowCount() ) ) + { + #if OSL_DEBUG_LEVEL > 0 + for ( TableSize row = 0; row < m_pModel->getRowCount(); ++row ) + { + OSL_ENSURE( isRowSelected( row ), "TableControl_Impl::markAllRowsAsSelected: inconsistency in the selected rows!" ); + } + #endif + // already all rows marked as selected + return false; + } + + m_aSelectedRows.clear(); + for ( RowPos i=0; i < m_pModel->getRowCount(); ++i ) + m_aSelectedRows.push_back(i); + + return true; + } + + //-------------------------------------------------------------------- + Rectangle TableControl_Impl::calcHeaderRect(bool bColHeader) + { + Rectangle const aRectTableWithHeaders( impl_getAllVisibleCellsArea() ); + Size const aSizeTableWithHeaders( aRectTableWithHeaders.GetSize() ); + if ( bColHeader ) + return Rectangle( aRectTableWithHeaders.TopLeft(), Size( aSizeTableWithHeaders.Width(), m_nColHeaderHeightPixel ) ); + else + return Rectangle( aRectTableWithHeaders.TopLeft(), Size( m_nRowHeaderWidthPixel, aSizeTableWithHeaders.Height() ) ); + } + + //-------------------------------------------------------------------- + Rectangle TableControl_Impl::calcTableRect() + { + return impl_getAllVisibleDataCellArea(); + } + + //-------------------------------------------------------------------- + IMPL_LINK( TableControl_Impl, OnUpdateScrollbars, void*, /**/ ) + { + DBG_CHECK_ME(); + impl_ni_updateScrollbars(); + return 1L; + } + + //-------------------------------------------------------------------- + IMPL_LINK( TableControl_Impl, OnScroll, ScrollBar*, _pScrollbar ) + { + DBG_ASSERT( ( _pScrollbar == m_pVScroll ) || ( _pScrollbar == m_pHScroll ), + "TableControl_Impl::OnScroll: where did this come from?" ); + + if ( _pScrollbar == m_pVScroll ) + impl_ni_ScrollRows( _pScrollbar->GetDelta() ); + else + impl_ni_ScrollColumns( _pScrollbar->GetDelta() ); + + return 0L; + } + + //------------------------------------------------------------------------------------------------------------------ + Reference< XAccessible > TableControl_Impl::getAccessible( Window& i_parentWindow ) + { + DBG_TESTSOLARMUTEX(); + if ( m_pAccessibleTable == NULL ) + { + Reference< XAccessible > const xAccParent = i_parentWindow.GetAccessible(); + if ( xAccParent.is() ) + { + m_pAccessibleTable = m_aFactoryAccess.getFactory().createAccessibleTableControl( + xAccParent, m_rAntiImpl + ); + } + } + + Reference< XAccessible > xAccessible; + if ( m_pAccessibleTable ) + xAccessible = m_pAccessibleTable->getMyself(); + return xAccessible; + } + + //------------------------------------------------------------------------------------------------------------------ + void TableControl_Impl::disposeAccessible() + { + if ( m_pAccessibleTable ) + m_pAccessibleTable->dispose(); + m_pAccessibleTable = NULL; + } + + //------------------------------------------------------------------------------------------------------------------ + bool TableControl_Impl::impl_isAccessibleAlive() const + { + DBG_CHECK_ME(); + return ( NULL != m_pAccessibleTable ) && m_pAccessibleTable->isAlive(); + } + + //------------------------------------------------------------------------------------------------------------------ + void TableControl_Impl::impl_commitAccessibleEvent( sal_Int16 const i_eventID, Any const & i_newValue, Any const & i_oldValue ) + { + DBG_CHECK_ME(); + if ( impl_isAccessibleAlive() ) + m_pAccessibleTable->commitEvent( i_eventID, i_newValue, i_oldValue ); + } + + //================================================================================================================== + //= TableFunctionSet + //================================================================================================================== + //------------------------------------------------------------------------------------------------------------------ + TableFunctionSet::TableFunctionSet(TableControl_Impl* _pTableControl) + :m_pTableControl( _pTableControl) + ,m_nCurrentRow( ROW_INVALID ) + { + } + //------------------------------------------------------------------------------------------------------------------ + TableFunctionSet::~TableFunctionSet() + { + } + //------------------------------------------------------------------------------------------------------------------ + void TableFunctionSet::BeginDrag() + { + } + //------------------------------------------------------------------------------------------------------------------ + void TableFunctionSet::CreateAnchor() + { + m_pTableControl->setAnchor( m_pTableControl->getCurRow() ); + } + + //------------------------------------------------------------------------------------------------------------------ + void TableFunctionSet::DestroyAnchor() + { + m_pTableControl->setAnchor( ROW_INVALID ); + } + + //------------------------------------------------------------------------------------------------------------------ + sal_Bool TableFunctionSet::SetCursorAtPoint(const Point& rPoint, sal_Bool bDontSelectAtCursor) + { + sal_Bool bHandled = sal_False; + // newRow is the row which includes the point, getCurRow() is the last selected row, before the mouse click + RowPos newRow = m_pTableControl->getRowAtPoint( rPoint ); + if ( newRow == ROW_COL_HEADERS ) + newRow = m_pTableControl->getTopRow(); + + ColPos newCol = m_pTableControl->getColAtPoint( rPoint ); + if ( newCol == COL_ROW_HEADERS ) + newCol = m_pTableControl->getLeftColumn(); + + if ( ( newRow == ROW_INVALID ) || ( newCol == COL_INVALID ) ) + return sal_False; + + if ( bDontSelectAtCursor ) + { + if ( m_pTableControl->getSelectedRowCount() > 1 ) + m_pTableControl->getSelEngine()->AddAlways(sal_True); + bHandled = sal_True; + } + else if ( m_pTableControl->getAnchor() == m_pTableControl->getCurRow() ) + { + //selecting region, + int diff = m_pTableControl->getCurRow() - newRow; + //selected region lies above the last selection + if( diff >= 0) + { + //put selected rows in vector + while ( m_pTableControl->getAnchor() >= newRow ) + { + m_pTableControl->markRowAsSelected( m_pTableControl->getAnchor() ); + m_pTableControl->setAnchor( m_pTableControl->getAnchor() - 1 ); + diff--; + } + m_pTableControl->setAnchor( m_pTableControl->getAnchor() + 1 ); + } + //selected region lies beneath the last selected row + else + { + while ( m_pTableControl->getAnchor() <= newRow ) + { + m_pTableControl->markRowAsSelected( m_pTableControl->getAnchor() ); + m_pTableControl->setAnchor( m_pTableControl->getAnchor() + 1 ); + diff++; + } + m_pTableControl->setAnchor( m_pTableControl->getAnchor() - 1 ); + } + Rectangle aCellRect; + m_pTableControl->invalidateSelectedRegion( m_pTableControl->getCurRow(), newRow, aCellRect ); + bHandled = sal_True; + } + //no region selected + else + { + if ( !m_pTableControl->hasRowSelection() ) + m_pTableControl->markRowAsSelected( newRow ); + else + { + if ( m_pTableControl->getSelEngine()->GetSelectionMode() == SINGLE_SELECTION ) + { + DeselectAll(); + m_pTableControl->markRowAsSelected( newRow ); + } + else + { + m_pTableControl->markRowAsSelected( newRow ); + } + } + if ( m_pTableControl->getSelectedRowCount() > 1 && m_pTableControl->getSelEngine()->GetSelectionMode() != SINGLE_SELECTION ) + m_pTableControl->getSelEngine()->AddAlways(sal_True); + + Rectangle aCellRect; + m_pTableControl->invalidateSelectedRegion( newRow, newRow, aCellRect ); + bHandled = sal_True; + } + m_pTableControl->goTo( newCol, newRow ); + return bHandled; + } + //------------------------------------------------------------------------------------------------------------------ + sal_Bool TableFunctionSet::IsSelectionAtPoint( const Point& rPoint ) + { + m_pTableControl->getSelEngine()->AddAlways(sal_False); + if ( !m_pTableControl->hasRowSelection() ) + return sal_False; + else + { + RowPos curRow = m_pTableControl->getRowAtPoint( rPoint ); + m_pTableControl->setAnchor( ROW_INVALID ); + bool selected = m_pTableControl->isRowSelected( curRow ); + m_nCurrentRow = curRow; + return selected; + } + } + //------------------------------------------------------------------------------------------------------------------ + void TableFunctionSet::DeselectAtPoint( const Point& rPoint ) + { + (void)rPoint; + Rectangle aCellRange; + m_pTableControl->invalidateSelectedRegion( m_nCurrentRow, m_nCurrentRow, aCellRange ); + m_pTableControl->markRowAsDeselected( m_nCurrentRow ); + } + + //------------------------------------------------------------------------------------------------------------------ + void TableFunctionSet::DeselectAll() + { + if ( m_pTableControl->hasRowSelection() ) + { + Rectangle aCellRange; + for ( size_t i=0; i<m_pTableControl->getSelectedRowCount(); ++i ) + { + RowPos const rowIndex = m_pTableControl->getSelectedRowIndex(i); + m_pTableControl->invalidateSelectedRegion( rowIndex, rowIndex, aCellRange ); + } + + m_pTableControl->markAllRowsAsDeselected(); + } + } + +//...................................................................................................................... +} } // namespace svt::table +//...................................................................................................................... + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svtools/source/table/tablecontrol_impl.hxx b/svtools/source/table/tablecontrol_impl.hxx new file mode 100644 index 000000000000..2f9cffd3089b --- /dev/null +++ b/svtools/source/table/tablecontrol_impl.hxx @@ -0,0 +1,488 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * +************************************************************************/ + +#ifndef SVTOOLS_TABLECONTROL_IMPL_HXX +#define SVTOOLS_TABLECONTROL_IMPL_HXX + +#include "svtools/table/tablemodel.hxx" +#include "svtools/table/tablecontrolinterface.hxx" + +#include "svtaccessiblefactory.hxx" + +#include <vcl/seleng.hxx> + +#include <vector> + +#include <boost/scoped_ptr.hpp> + +class ScrollBar; +class ScrollBarBox; + +//........................................................................ +namespace svt { namespace table +{ +//........................................................................ + + struct MutableColumnMetrics : protected ColumnMetrics + { + MutableColumnMetrics() + :ColumnMetrics() + { + } + + MutableColumnMetrics( long const i_startPixel, long const i_endPixel ) + :ColumnMetrics( i_startPixel, i_endPixel ) + { + } + + long getStart() const { return nStartPixel; } + long getEnd() const { return nEndPixel; } + + void setEnd( long const i_end ) { nEndPixel = i_end; } + void move( long const i_offset ) { nStartPixel += i_offset; nEndPixel += i_offset; } + + long getWidth() const { return nEndPixel - nStartPixel; } + + ColumnMetrics const & operator()() { return *this; } + }; + + struct ColumnInfoPositionLess + { + bool operator()( MutableColumnMetrics const& i_colInfo, long const i_position ) + { + return i_colInfo.getEnd() < i_position; + } + bool operator()( long const i_position, MutableColumnMetrics const& i_colInfo ) + { + return i_position < i_colInfo.getStart(); + } + }; + + typedef ::std::vector< MutableColumnMetrics > ColumnPositions; + + class TableControl; + class TableDataWindow; + class TableFunctionSet; + + //==================================================================== + //= TableControl_Impl + //==================================================================== + class TableControl_Impl :public ITableControl + ,public ITableModelListener + { + friend class TableGeometry; + friend class TableRowGeometry; + friend class TableColumnGeometry; + friend class SuspendInvariants; + + private: + /// the control whose impl-instance we implemnt + TableControl& m_rAntiImpl; + /// the model of the table control + PTableModel m_pModel; + /// the input handler to use, usually the input handler as provided by ->m_pModel + PTableInputHandler m_pInputHandler; + /// info about the widths of our columns + ColumnPositions m_aColumnWidths; + + /// the height of a single row in the table, measured in pixels + long m_nRowHeightPixel; + /// the height of the column header row in the table, measured in pixels + long m_nColHeaderHeightPixel; + /// the width of the row header column in the table, measured in pixels + long m_nRowHeaderWidthPixel; + + /// the number of columns in the table control. Cached model value. + TableSize m_nColumnCount; + + /// the number of rows in the table control. Cached model value. + TableSize m_nRowCount; + + /// denotes whether or not the columns fitted into the available width, last time we checked + long m_bColumnsFit; + + ColPos m_nCurColumn; + RowPos m_nCurRow; + ColPos m_nLeftColumn; + RowPos m_nTopRow; + + sal_Int32 m_nCursorHidden; + + /** the window to contain all data content, including header bars + + The window's upper left corner is at position (0,0), relative to the + table control, which is the direct parent of the data window. + */ + ::boost::scoped_ptr< TableDataWindow > + m_pDataWindow; + /// the vertical scrollbar, if any + ScrollBar* m_pVScroll; + /// the horizontal scrollbar, if any + ScrollBar* m_pHScroll; + ScrollBarBox* m_pScrollCorner; + //selection engine - for determining selection range, e.g. single, multiple + SelectionEngine* m_pSelEngine; + //vector which contains the selected rows + std::vector<RowPos> m_aSelectedRows; + //part of selection engine + TableFunctionSet* m_pTableFunctionSet; + //part of selection engine + RowPos m_nAnchor; + bool m_bUpdatingColWidths; + + Link m_aSelectHdl; + + AccessibleFactoryAccess m_aFactoryAccess; + IAccessibleTableControl* m_pAccessibleTable; + +#if DBG_UTIL + #define INV_SCROLL_POSITION 1 + /** represents a bitmask of invariants to check + + Actually, impl_checkInvariants checks more invariants than denoted in this + bit mask, but only those present here can be disabled temporarily. + */ + sal_Int32 m_nRequiredInvariants; +#endif + + public: + void setModel( PTableModel _pModel ); + + inline const PTableInputHandler& getInputHandler() const { return m_pInputHandler; } + + inline RowPos getCurRow() const { return m_nCurRow; } + inline void setCurRow( RowPos i_curRow ){ m_nCurRow = i_curRow; } + + RowPos getAnchor() const { return m_nAnchor; } + void setAnchor( RowPos const i_anchor ) { m_nAnchor = i_anchor; } + + inline RowPos getTopRow() const { return m_nTopRow; } + inline ColPos getLeftColumn() const { return m_nLeftColumn; } + + inline const TableControl& getAntiImpl() const { return m_rAntiImpl; } + inline TableControl& getAntiImpl() { return m_rAntiImpl; } + + public: + TableControl_Impl( TableControl& _rAntiImpl ); + ~TableControl_Impl(); + +#if DBG_UTIL + const sal_Char* impl_checkInvariants() const; +#endif + /** to be called when the anti-impl instance has been resized + */ + void onResize(); + + /** paints the table control content which intersects with the given rectangle + */ + void doPaintContent( const Rectangle& _rUpdateRect ); + + /** moves the cursor to the cell with the given coordinates + + To ease the caller's code, the coordinates must not necessarily denote a + valid position. If they don't, <FALSE/> will be returned. + */ + bool goTo( ColPos _nColumn, RowPos _nRow ); + + /** ensures that the given coordinate is visible + @param _nColumn + the column position which should be visible. Must be non-negative, and smaller + than the column count. + @param _nRow + the row position which should be visibleMust be non-negative, and smaller + than the row count. + @param _bAcceptPartialVisibility + <TRUE/> if it's okay that the given cooordinate is only partially visible + */ + void ensureVisible( ColPos _nColumn, RowPos _nRow, bool _bAcceptPartialVisibility ); + + /** retrieves the content of the given cell, converted to a string + */ + ::rtl::OUString getCellContentAsString( RowPos const i_row, ColPos const i_col ); + + /** returns the position of the current row in the selection vector */ + int getRowSelectedNumber(const ::std::vector<RowPos>& selectedRows, RowPos current); + + /** _rCellRect contains the region, which should be invalidate after some action e.g. selecting row*/ + void invalidateSelectedRegion(RowPos _nPrevRow, RowPos _nCurRow, Rectangle& _rCellRect ); + + /** invalidates the part of the data window which is covered by the given row + @param i_firstRow + the index of the first row to include in the invalidation + @param i_lastRow + the index of the last row to include in the invalidation, or ROW_INVALID if the invalidation + should happen down to the bottom of the data window. + */ + void invalidateRowRange( RowPos const i_firstRow, RowPos const i_lastRow ); + + void checkCursorPosition(); + + bool hasRowSelection() const { return !m_aSelectedRows.empty(); } + size_t getSelectedRowCount() const { return m_aSelectedRows.size(); } + RowPos getSelectedRowIndex( size_t const i_selectionIndex ) const; + + /** removes the given row index from m_aSelectedRows + + @return + <TRUE/> if and only if the row was previously marked as selected + */ + bool markRowAsDeselected( RowPos const i_rowIndex ); + + /** marks the given row as selectged, by putting it into m_aSelectedRows + @return + <TRUE/> if and only if the row was previously <em>not</em> marked as selected + */ + bool markRowAsSelected( RowPos const i_rowIndex ); + + /** marks all rows as deselected + @return + <TRUE/> if and only if the selection actually changed by this operation + */ + bool markAllRowsAsDeselected(); + + /** marks all rows as selected + @return + <FALSE/> if and only if all rows were selected already. + */ + bool markAllRowsAsSelected(); + + void setSelectHandler( Link const & i_selectHandler ) { m_aSelectHdl = i_selectHandler; } + Link const& getSelectHandler() const { return m_aSelectHdl; } + + // ITableControl + virtual void hideCursor(); + virtual void showCursor(); + virtual bool dispatchAction( TableControlAction _eAction ); + virtual SelectionEngine* getSelEngine(); + virtual PTableModel getModel() const; + virtual ColPos getCurrentColumn() const; + virtual RowPos getCurrentRow() const; + virtual bool activateCell( ColPos const i_col, RowPos const i_row ); + virtual ::Size getTableSizePixel() const; + virtual void setPointer( Pointer const & i_pointer ); + virtual void captureMouse(); + virtual void releaseMouse(); + virtual void invalidate( TableArea const i_what ); + virtual long pixelWidthToAppFont( long const i_pixels ) const; + virtual void hideTracking(); + virtual void showTracking( Rectangle const & i_location, sal_uInt16 const i_flags ); + virtual RowPos getRowAtPoint( const Point& rPoint ) const; + virtual ColPos getColAtPoint( const Point& rPoint ) const; + virtual TableCell hitTest( const Point& rPoint ) const; + virtual ColumnMetrics getColumnMetrics( ColPos const i_column ) const; + virtual bool isRowSelected( RowPos i_row ) const; + + + TableDataWindow& getDataWindow() { return *m_pDataWindow; } + const TableDataWindow& getDataWindow() const { return *m_pDataWindow; } + ScrollBar* getHorzScrollbar(); + ScrollBar* getVertScrollbar(); + + Rectangle calcHeaderRect(bool bColHeader); + Rectangle calcTableRect(); + + // A11Y + ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessible > + getAccessible( Window& i_parentWindow ); + void disposeAccessible(); + + // ITableModelListener + virtual void rowsInserted( RowPos first, RowPos last ); + virtual void rowsRemoved( RowPos first, RowPos last ); + virtual void columnInserted( ColPos const i_colIndex ); + virtual void columnRemoved( ColPos const i_colIndex ); + virtual void allColumnsRemoved(); + virtual void cellsUpdated( ColPos const i_firstCol, ColPos i_lastCol, RowPos const i_firstRow, RowPos const i_lastRow ); + virtual void columnChanged( ColPos const i_column, ColumnAttributeGroup const i_attributeGroup ); + virtual void tableMetricsChanged(); + + private: + bool impl_isAccessibleAlive() const; + void impl_commitAccessibleEvent( + sal_Int16 const i_eventID, + ::com::sun::star::uno::Any const & i_newValue, + ::com::sun::star::uno::Any const & i_oldValue + ); + + /** toggles the cursor visibility + + The method is not bound to the classes public invariants, as it's used in + situations where the they must not necessarily be fullfilled. + */ + void impl_ni_doSwitchCursor( bool _bOn ); + + /** returns the number of visible rows. + + @param _bAcceptPartialRow + specifies whether a possible only partially visible last row is + counted, too. + */ + TableSize impl_getVisibleRows( bool _bAcceptPartialRow ) const; + + /** returns the number of visible columns + + The value may change with different horizontal scroll positions, as + different columns have different widths. For instance, if your control is + 100 pixels wide, and has three columns of width 50, 50, 100, respectively, + then this method will return either "2" or "1", depending on which column + is the first visible one. + + @param _bAcceptPartialRow + specifies whether a possible only partially visible last row is + counted, too. + */ + TableSize impl_getVisibleColumns( bool _bAcceptPartialCol ) const; + + /** determines the rectangle occupied by the given cell + */ + void impl_getCellRect( ColPos _nColumn, RowPos _nRow, Rectangle& _rCellRect ) const; + + /** updates all cached model values + + The method is not bound to the classes public invariants, as it's used in + situations where the they must not necessarily be fullfilled. + */ + void impl_ni_updateCachedModelValues(); + + /** updates the cached table metrics (row height etc.) + */ + void impl_ni_updateCachedTableMetrics(); + + /** updates ->m_aColumnWidthsPixel with the current pixel widths of all model columns + + The method is not bound to the classes public invariants, as it's used in + situations where the they must not necessarily be fullfilled. + + @param i_assumeInflexibleColumnsUpToIncluding + the index of a column up to which all columns should be considered as inflexible, or + <code>COL_INVALID</code>. + */ + void impl_ni_updateColumnWidths( ColPos const i_assumeInflexibleColumnsUpToIncluding = COL_INVALID ); + + /** updates the scrollbars of the control + + The method is not bound to the classes public invariants, as it's used in + situations where the they must not necessarily be fullfilled. + + This includes both the existence of the scrollbars, and their + state. + */ + void impl_ni_updateScrollbars(); + + /** scrolls the view by the given number of rows + + The method is not bound to the classes public invariants, as it's used in + situations where the they must not necessarily be fullfilled. + + @return + the number of rows by which the viewport was scrolled. This may differ + from the given numbers to scroll in case the begin or the end of the + row range were reached. + */ + TableSize impl_ni_ScrollRows( TableSize _nRowDelta ); + + /** equivalent to impl_ni_ScrollRows, but checks the instances invariants beforehand (in a non-product build only) + */ + TableSize impl_scrollRows( TableSize const i_rowDelta ); + + /** scrolls the view by the given number of columns + + The method is not bound to the classes public invariants, as it's used in + situations where the they must not necessarily be fullfilled. + + @return + the number of columns by which the viewport was scrolled. This may differ + from the given numbers to scroll in case the begin or the end of the + column range were reached. + */ + TableSize impl_ni_ScrollColumns( TableSize _nColumnDelta ); + + /** equivalent to impl_ni_ScrollColumns, but checks the instances invariants beforehand (in a non-product build only) + */ + TableSize impl_scrollColumns( TableSize const i_columnDelta ); + + /** retrieves the area occupied by the totality of (at least partially) visible cells + + The returned area includes row and column headers. Also, it takes into + account the the fact that there might be less columns than would normally + find room in the control. + + As a result of respecting the partial visibility of rows and columns, + the returned area might be larger than the data window's output size. + */ + Rectangle impl_getAllVisibleCellsArea() const; + + /** retrieves the area occupied by all (at least partially) visible data cells. + + Effectively, the returned area is the same as returned by ->impl_getAllVisibleCellsArea, + minus the row and column header areas. + */ + Rectangle impl_getAllVisibleDataCellArea() const; + + /** retrieves the column which covers the given ordinate + */ + ColPos impl_getColumnForOrdinate( long const i_ordinate ) const; + + /** retrieves the row which covers the given abscissa + */ + RowPos impl_getRowForAbscissa( long const i_abscissa ) const; + + /// invalidates the window area occupied by the given column + void impl_invalidateColumn( ColPos const i_column ); + + DECL_LINK( OnScroll, ScrollBar* ); + DECL_LINK( OnUpdateScrollbars, void* ); + }; + + //see seleng.hxx, seleng.cxx, FunctionSet overridables, part of selection engine + class TableFunctionSet : public FunctionSet + { + private: + TableControl_Impl* m_pTableControl; + RowPos m_nCurrentRow; + + public: + TableFunctionSet(TableControl_Impl* _pTableControl); + virtual ~TableFunctionSet(); + + virtual void BeginDrag(); + virtual void CreateAnchor(); + virtual void DestroyAnchor(); + virtual sal_Bool SetCursorAtPoint(const Point& rPoint, sal_Bool bDontSelectAtCursor); + virtual sal_Bool IsSelectionAtPoint( const Point& rPoint ); + virtual void DeselectAtPoint( const Point& rPoint ); + virtual void DeselectAll(); + }; + + +//........................................................................ +} } // namespace svt::table +//........................................................................ + +#endif // SVTOOLS_TABLECONTROL_IMPL_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svtools/source/table/tabledatawindow.cxx b/svtools/source/table/tabledatawindow.cxx new file mode 100644 index 000000000000..7c546421555d --- /dev/null +++ b/svtools/source/table/tabledatawindow.cxx @@ -0,0 +1,243 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * +************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_svtools.hxx" + +#include "svtools/table/tablecontrol.hxx" + +#include "tabledatawindow.hxx" +#include "tablecontrol_impl.hxx" +#include "tablegeometry.hxx" +#include "cellvalueconversion.hxx" + +#include <vcl/help.hxx> + +//...................................................................................................................... +namespace svt { namespace table +{ +//...................................................................................................................... + + /** === begin UNO using === **/ + using ::com::sun::star::uno::Any; + /** === end UNO using === **/ + + //================================================================================================================== + //= TableDataWindow + //================================================================================================================== + //------------------------------------------------------------------------------------------------------------------ + TableDataWindow::TableDataWindow( TableControl_Impl& _rTableControl ) + :Window( &_rTableControl.getAntiImpl() ) + ,m_rTableControl( _rTableControl ) + ,m_nTipWindowHandle( 0 ) + { + // by default, use the background as determined by the style settings + const Color aWindowColor( GetSettings().GetStyleSettings().GetFieldColor() ); + SetBackground( Wallpaper( aWindowColor ) ); + SetFillColor( aWindowColor ); + } + + //------------------------------------------------------------------------------------------------------------------ + TableDataWindow::~TableDataWindow() + { + impl_hideTipWindow(); + } + + //------------------------------------------------------------------------------------------------------------------ + void TableDataWindow::Paint( const Rectangle& rUpdateRect ) + { + m_rTableControl.doPaintContent( rUpdateRect ); + } + //------------------------------------------------------------------------------------------------------------------ + void TableDataWindow::SetBackground( const Wallpaper& rColor ) + { + Window::SetBackground( rColor ); + } + //------------------------------------------------------------------------------------------------------------------ + void TableDataWindow::SetControlBackground( const Color& rColor ) + { + Window::SetControlBackground( rColor ); + } + //------------------------------------------------------------------------------------------------------------------ + void TableDataWindow::SetBackground() + { + Window::SetBackground(); + } + //------------------------------------------------------------------------------------------------------------------ + void TableDataWindow::SetControlBackground() + { + Window::SetControlBackground(); + } + + //------------------------------------------------------------------------------------------------------------------ + void TableDataWindow::RequestHelp( const HelpEvent& rHEvt ) + { + sal_uInt16 const nHelpMode = rHEvt.GetMode(); + if ( ( IsMouseCaptured() ) + || ( ( nHelpMode & HELPMODE_QUICK ) == 0 ) + ) + { + Window::RequestHelp( rHEvt ); + return; + } + + ::rtl::OUString sHelpText; + sal_uInt16 nHelpStyle = 0; + + Point const aMousePos( ScreenToOutputPixel( rHEvt.GetMousePosPixel() ) ); + RowPos const hitRow = m_rTableControl.getRowAtPoint( aMousePos ); + ColPos const hitCol = m_rTableControl.getColAtPoint( aMousePos ); + + PTableModel const pTableModel( m_rTableControl.getModel() ); + if ( ( hitCol >= 0 ) && ( hitCol < pTableModel->getColumnCount() ) ) + { + if ( hitRow == ROW_COL_HEADERS ) + { + sHelpText = pTableModel->getColumnModel( hitCol )->getHelpText(); + } + else if ( ( hitRow >= 0 ) && ( hitRow < pTableModel->getRowCount() ) ) + { + Any aCellToolTip; + pTableModel->getCellToolTip( hitCol, hitRow, aCellToolTip ); + if ( !aCellToolTip.hasValue() ) + { + // use the cell content + pTableModel->getCellContent( hitCol, hitRow, aCellToolTip ); + + // use the cell content as tool tip only if it doesn't fit into the cell. + bool const activeCell = ( hitRow == m_rTableControl.getCurrentRow() ) && ( hitCol == m_rTableControl.getCurrentColumn() ); + bool const selectedCell = m_rTableControl.isRowSelected( hitRow ); + + Rectangle const aWindowRect( Point( 0, 0 ), GetOutputSizePixel() ); + TableCellGeometry const aCell( m_rTableControl, aWindowRect, hitCol, hitRow ); + Rectangle const aCellRect( aCell.getRect() ); + + PTableRenderer const pRenderer = pTableModel->getRenderer(); + if ( pRenderer->FitsIntoCell( aCellToolTip, hitCol, hitRow, activeCell, selectedCell, *this, aCellRect ) ) + aCellToolTip.clear(); + } + + sHelpText = CellValueConversion::convertToString( aCellToolTip ); + + if ( sHelpText.indexOf( '\n' ) >= 0 ) + nHelpStyle = QUICKHELP_TIP_STYLE_BALLOON; + } + } + + if ( sHelpText.getLength() ) + { + Rectangle const aControlScreenRect( + OutputToScreenPixel( Point( 0, 0 ) ), + GetOutputSizePixel() + ); + + if ( m_nTipWindowHandle ) + Help::UpdateTip( m_nTipWindowHandle, this, aControlScreenRect, sHelpText ); + else + m_nTipWindowHandle = Help::ShowTip( this, aControlScreenRect, sHelpText, nHelpStyle ); + } + else + impl_hideTipWindow(); + } + + //------------------------------------------------------------------------------------------------------------------ + void TableDataWindow::impl_hideTipWindow() + { + if ( m_nTipWindowHandle != 0 ) + { + Help::HideTip( m_nTipWindowHandle ); + m_nTipWindowHandle = 0; + } + } + + //------------------------------------------------------------------------------------------------------------------ + void TableDataWindow::MouseMove( const MouseEvent& rMEvt ) + { + if ( rMEvt.IsLeaveWindow() ) + impl_hideTipWindow(); + + if ( !m_rTableControl.getInputHandler()->MouseMove( m_rTableControl, rMEvt ) ) + { + Window::MouseMove( rMEvt ); + } + } + //------------------------------------------------------------------------------------------------------------------ + void TableDataWindow::MouseButtonDown( const MouseEvent& rMEvt ) + { + impl_hideTipWindow(); + + Point const aPoint = rMEvt.GetPosPixel(); + RowPos const hitRow = m_rTableControl.getRowAtPoint( aPoint ); + bool const wasRowSelected = m_rTableControl.isRowSelected( hitRow ); + + if ( !m_rTableControl.getInputHandler()->MouseButtonDown( m_rTableControl, rMEvt ) ) + { + Window::MouseButtonDown( rMEvt ); + return; + } + + bool const isRowSelected = m_rTableControl.isRowSelected( hitRow ); + if ( isRowSelected != wasRowSelected ) + { + m_aSelectHdl.Call( NULL ); + } + m_aMouseButtonDownHdl.Call((MouseEvent*) &rMEvt); + } + + //------------------------------------------------------------------------------------------------------------------ + void TableDataWindow::MouseButtonUp( const MouseEvent& rMEvt ) + { + if ( !m_rTableControl.getInputHandler()->MouseButtonUp( m_rTableControl, rMEvt ) ) + Window::MouseButtonUp( rMEvt ); + + m_aMouseButtonUpHdl.Call((MouseEvent*) &rMEvt); + m_rTableControl.getAntiImpl().GrabFocus(); + } + + //------------------------------------------------------------------------------------------------------------------ + long TableDataWindow::Notify(NotifyEvent& rNEvt ) + { + long nDone = 0; + if ( rNEvt.GetType() == EVENT_COMMAND ) + { + const CommandEvent& rCEvt = *rNEvt.GetCommandEvent(); + if ( rCEvt.GetCommand() == COMMAND_WHEEL ) + { + const CommandWheelData* pData = rCEvt.GetWheelData(); + if( !pData->GetModifier() && ( pData->GetMode() == COMMAND_WHEEL_SCROLL ) ) + { + nDone = HandleScrollCommand( rCEvt, m_rTableControl.getHorzScrollbar(), m_rTableControl.getVertScrollbar() ); + } + } + } + return nDone ? nDone : Window::Notify( rNEvt ); + } +//...................................................................................................................... +} } // namespace svt::table +//...................................................................................................................... + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svtools/source/table/tabledatawindow.hxx b/svtools/source/table/tabledatawindow.hxx new file mode 100644 index 000000000000..c26cd6ccd596 --- /dev/null +++ b/svtools/source/table/tabledatawindow.hxx @@ -0,0 +1,92 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * +************************************************************************/ + +#ifndef SVTOOLS_SOURCE_TABLE_TABLEDATAWINDOW_HXX +#define SVTOOLS_SOURCE_TABLE_TABLEDATAWINDOW_HXX + +#include <vcl/window.hxx> +#include <vcl/seleng.hxx> + +//........................................................................ +namespace svt { namespace table +{ +//........................................................................ + + class TableControl_Impl; + class TableFunctionSet; + + + //==================================================================== + //= TableDataWindow + //==================================================================== + /** the window containing the content area (including headers) of + a table control + */ + class TableDataWindow : public Window + { + friend class TableFunctionSet; + private: + TableControl_Impl& m_rTableControl; + Link m_aMouseButtonDownHdl; + Link m_aMouseButtonUpHdl; + Link m_aSelectHdl; + sal_uLong m_nTipWindowHandle; + + public: + TableDataWindow( TableControl_Impl& _rTableControl ); + ~TableDataWindow(); + + inline void SetMouseButtonDownHdl( const Link& rLink ) { m_aMouseButtonDownHdl = rLink; } + inline const Link& GetMouseButtonDownHdl() const { return m_aMouseButtonDownHdl; } + inline void SetMouseButtonUpHdl( const Link& rLink ) { m_aMouseButtonUpHdl = rLink; } + inline const Link& GetMouseButtonUpHdl() const { return m_aMouseButtonUpHdl; } + inline void SetSelectHdl( const Link& rLink ) { m_aSelectHdl = rLink; } + inline const Link& GetSelectHdl() const { return m_aSelectHdl; } + + // Window overridables + virtual void Paint( const Rectangle& rRect ); + virtual void MouseMove( const MouseEvent& rMEvt); + virtual void MouseButtonDown( const MouseEvent& rMEvt); + virtual void MouseButtonUp( const MouseEvent& rMEvt); + virtual long Notify(NotifyEvent& rNEvt); + virtual void SetControlBackground(const Color& rColor); + virtual void SetControlBackground(); + virtual void RequestHelp( const HelpEvent& rHEvt ); + + void SetBackground(const Wallpaper& rColor); + void SetBackground(); + + private: + void impl_hideTipWindow(); + }; +//........................................................................ +} } // namespace svt::table +//........................................................................ + +#endif // SVTOOLS_SOURCE_TABLE_TABLEDATAWINDOW_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svtools/source/table/tablegeometry.cxx b/svtools/source/table/tablegeometry.cxx new file mode 100644 index 000000000000..e00e21c28911 --- /dev/null +++ b/svtools/source/table/tablegeometry.cxx @@ -0,0 +1,168 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * +************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_svtools.hxx" + +#include "tablegeometry.hxx" +#include "tablecontrol_impl.hxx" + +#include <tools/debug.hxx> + +//...................................................................................................................... +namespace svt { namespace table +{ +//...................................................................................................................... + + //================================================================================================================== + //= TableRowGeometry + //================================================================================================================== + //------------------------------------------------------------------------------------------------------------------ + TableRowGeometry::TableRowGeometry( TableControl_Impl const & _rControl, Rectangle const & _rBoundaries, + RowPos const _nRow, bool const i_allowVirtualRows ) + :TableGeometry( _rControl, _rBoundaries ) + ,m_nRowPos( _nRow ) + ,m_bAllowVirtualRows( i_allowVirtualRows ) + { + if ( m_nRowPos == ROW_COL_HEADERS ) + { + m_aRect.Top() = 0; + m_aRect.Bottom() = m_rControl.m_nColHeaderHeightPixel - 1; + } + else + { + impl_initRect(); + } + } + + //------------------------------------------------------------------------------------------------------------------ + void TableRowGeometry::impl_initRect() + { + if ( ( m_nRowPos >= m_rControl.m_nTopRow ) && impl_isValidRow( m_nRowPos ) ) + { + m_aRect.Top() = m_rControl.m_nColHeaderHeightPixel + ( m_nRowPos - m_rControl.m_nTopRow ) * m_rControl.m_nRowHeightPixel; + m_aRect.Bottom() = m_aRect.Top() + m_rControl.m_nRowHeightPixel - 1; + } + else + m_aRect.SetEmpty(); + } + + //------------------------------------------------------------------------------------------------------------------ + bool TableRowGeometry::impl_isValidRow( RowPos const i_row ) const + { + return m_bAllowVirtualRows || ( i_row < m_rControl.m_pModel->getRowCount() ); + } + + //------------------------------------------------------------------------------------------------------------------ + bool TableRowGeometry::moveDown() + { + if ( m_nRowPos == ROW_COL_HEADERS ) + { + m_nRowPos = m_rControl.m_nTopRow; + impl_initRect(); + } + else + { + if ( impl_isValidRow( ++m_nRowPos ) ) + m_aRect.Move( 0, m_rControl.m_nRowHeightPixel ); + else + m_aRect.SetEmpty(); + } + return isValid(); + } + + //================================================================================================================== + //= TableColumnGeometry + //================================================================================================================== + //------------------------------------------------------------------------------------------------------------------ + TableColumnGeometry::TableColumnGeometry( TableControl_Impl const & _rControl, Rectangle const & _rBoundaries, + ColPos const _nCol, bool const i_allowVirtualColumns ) + :TableGeometry( _rControl, _rBoundaries ) + ,m_nColPos( _nCol ) + ,m_bAllowVirtualColumns( i_allowVirtualColumns ) + { + if ( m_nColPos == COL_ROW_HEADERS ) + { + m_aRect.Left() = 0; + m_aRect.Right() = m_rControl.m_nRowHeaderWidthPixel - 1; + } + else + { + impl_initRect(); + } + } + + //------------------------------------------------------------------------------------------------------------------ + void TableColumnGeometry::impl_initRect() + { + ColPos nLeftColumn = m_rControl.m_nLeftColumn; + if ( ( m_nColPos >= nLeftColumn ) && impl_isValidColumn( m_nColPos ) ) + { + m_aRect.Left() = m_rControl.m_nRowHeaderWidthPixel; + // TODO: take into account any possibly frozen columns + + for ( ColPos col = nLeftColumn; col < m_nColPos; ++col ) + m_aRect.Left() += m_rControl.m_aColumnWidths[ col ].getWidth(); + m_aRect.Right() = m_aRect.Left() + m_rControl.m_aColumnWidths[ m_nColPos ].getWidth() - 1; + } + else + m_aRect.SetEmpty(); + } + + //------------------------------------------------------------------------------------------------------------------ + bool TableColumnGeometry::impl_isValidColumn( ColPos const i_column ) const + { + return m_bAllowVirtualColumns || ( i_column < ColPos( m_rControl.m_aColumnWidths.size() ) ); + } + + //------------------------------------------------------------------------------------------------------------------ + bool TableColumnGeometry::moveRight() + { + if ( m_nColPos == COL_ROW_HEADERS ) + { + m_nColPos = m_rControl.m_nLeftColumn; + impl_initRect(); + } + else + { + if ( impl_isValidColumn( ++m_nColPos ) ) + { + m_aRect.Left() = m_aRect.Right() + 1; + m_aRect.Right() += m_rControl.m_aColumnWidths[ m_nColPos ].getWidth(); + } + else + m_aRect.SetEmpty(); + } + + return isValid(); + } + +//...................................................................................................................... +} } // namespace svt::table +//...................................................................................................................... + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svtools/source/table/tablegeometry.hxx b/svtools/source/table/tablegeometry.hxx new file mode 100644 index 000000000000..9520e4992411 --- /dev/null +++ b/svtools/source/table/tablegeometry.hxx @@ -0,0 +1,175 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * +************************************************************************/ + +#ifndef SVTOOLS_TABLEGEOMETRY_HXX +#define SVTOOLS_TABLEGEOMETRY_HXX + +#include "svtools/table/tabletypes.hxx" + +#include <tools/gen.hxx> + +//........................................................................ +namespace svt { namespace table +{ +//........................................................................ + + class TableControl_Impl; + + //==================================================================== + //= TableGeometry + //==================================================================== + class TableGeometry + { + protected: + const TableControl_Impl& m_rControl; + const Rectangle& m_rBoundaries; + Rectangle m_aRect; + + protected: + TableGeometry( + const TableControl_Impl& _rControl, + const Rectangle& _rBoundaries + ) + :m_rControl( _rControl ) + ,m_rBoundaries( _rBoundaries ) + ,m_aRect( _rBoundaries ) + { + } + + public: + // attribute access + const TableControl_Impl& getControl() const { return m_rControl; } + + // status + const Rectangle& getRect() const { return m_aRect; } + bool isValid() const { return !m_aRect.GetIntersection( m_rBoundaries ).IsEmpty(); } + }; + + //==================================================================== + //= TableRowGeometry + //==================================================================== + class TableRowGeometry : public TableGeometry + { + protected: + RowPos m_nRowPos; + bool m_bAllowVirtualRows; + + public: + TableRowGeometry( + TableControl_Impl const & _rControl, + Rectangle const & _rBoundaries, + RowPos const _nRow, + bool const i_allowVirtualRows = false + // allow rows >= getRowCount()? + ); + + // status + RowPos getRow() const { return m_nRowPos; } + // operations + bool moveDown(); + + private: + void impl_initRect(); + bool impl_isValidRow( RowPos const i_row ) const; + }; + + //==================================================================== + //= TableColumnGeometry + //==================================================================== + class TableColumnGeometry : public TableGeometry + { + protected: + ColPos m_nColPos; + bool m_bAllowVirtualColumns; + + public: + TableColumnGeometry( + TableControl_Impl const & _rControl, + Rectangle const & _rBoundaries, + ColPos const _nCol, + bool const i_allowVirtualColumns = false + ); + + // status + ColPos getCol() const { return m_nColPos; } + // operations + bool moveRight(); + + private: + void impl_initRect(); + bool impl_isValidColumn( ColPos const i_column ) const; + }; + + //==================================================================== + //= TableCellGeometry + //==================================================================== + /** a helper representing geometry information of a cell + */ + class TableCellGeometry + { + private: + TableRowGeometry m_aRow; + TableColumnGeometry m_aCol; + + public: + TableCellGeometry( + TableControl_Impl const & _rControl, + Rectangle const & _rBoundaries, + ColPos const _nCol, + RowPos const _nRow, + bool const i_alllowVirtualCells = false + ) + :m_aRow( _rControl, _rBoundaries, _nRow, i_alllowVirtualCells ) + ,m_aCol( _rControl, _rBoundaries, _nCol, i_alllowVirtualCells ) + { + } + + TableCellGeometry( + const TableRowGeometry& _rRow, + ColPos _nCol + ) + :m_aRow( _rRow ) + ,m_aCol( _rRow.getControl(), _rRow.getRect(), _nCol ) + { + } + + inline Rectangle getRect() const { return m_aRow.getRect().GetIntersection( m_aCol.getRect() ); } + inline RowPos getRow() const { return m_aRow.getRow(); } + inline ColPos getColumn() const { return m_aCol.getCol(); } + inline bool isValid() const { return !getRect().IsEmpty(); } + + inline bool moveDown() {return m_aRow.moveDown(); } + inline bool moveRight() {return m_aCol.moveRight(); } + }; + +//........................................................................ +} } // namespace svt::table +//........................................................................ + +#endif // SVTOOLS_TABLEGEOMETRY_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |