/************************************************************************* * 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 * * 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 /** === end UNO includes === **/ #include #include #include #include //...................................................................................................................... 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::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 === **/ struct GridTableRenderer_Impl { ITableModel& rModel; RowPos nCurrentRow; bool bUseGridLines; GridTableRenderer_Impl( ITableModel& _rModel ) :rModel( _rModel ) ,nCurrentRow( ROW_INVALID ) ,bUseGridLines( true ) { } }; 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 ULONG lcl_getAlignmentTextDrawFlags( GridTableRenderer_Impl const & i_impl, ColPos const i_columnPos ) { 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; } 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 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 ) ) ); ULONG const nDrawTextFlags = lcl_getAlignmentTextDrawFlags( *m_pImpl, _nCol ) | TEXT_DRAW_CLIP; _rDevice.DrawText( aTextRect, sHeaderText, nDrawTextFlags ); ::boost::optional< ::Color > 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() ); _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() ); ::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 ) ) ); 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, m_pImpl->rModel.getRowHeader( m_pImpl->nCurrentRow ), 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 ); const Reference< XGraphic > xGraphic( aCellContent, UNO_QUERY ); if ( xGraphic.is() ) { 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 ) ); 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; 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; ::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 ); return true; } //...................................................................................................................... } } // namespace svt::table //......................................................................................................................