diff options
Diffstat (limited to 'sc/source/ui/dbgui/fieldwnd.cxx')
-rw-r--r-- | sc/source/ui/dbgui/fieldwnd.cxx | 1193 |
1 files changed, 662 insertions, 531 deletions
diff --git a/sc/source/ui/dbgui/fieldwnd.cxx b/sc/source/ui/dbgui/fieldwnd.cxx index 91a214d4e..3fc8f59d2 100644 --- a/sc/source/ui/dbgui/fieldwnd.cxx +++ b/sc/source/ui/dbgui/fieldwnd.cxx @@ -1,7 +1,7 @@ /************************************************************************* * * 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 @@ -28,749 +28,880 @@ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_sc.hxx" -#include <vcl/virdev.hxx> +#include "fieldwnd.hxx" + +#include <tools/debug.hxx> #include <vcl/decoview.hxx> -#include <vcl/svapp.hxx> -#include <vcl/mnemonic.hxx> #include <vcl/help.hxx> -#include <tools/debug.hxx> +#include <vcl/svapp.hxx> +#include <vcl/virdev.hxx> -#include "fieldwnd.hxx" #include "pvlaydlg.hxx" -#include "pvglob.hxx" #include "AccessibleDataPilotControl.hxx" #include "scresid.hxx" #include "sc.hrc" -const size_t INVALID_INDEX = static_cast< size_t >( -1 ); +// ============================================================================ -//=================================================================== +using namespace ::com::sun::star; +using ::rtl::OUString; -ScDPFieldWindow::ScDPFieldWindow( - ScDPLayoutDlg* pDialog, - const ResId& rResId, - ScDPFieldType eFieldType, - FixedText* pFtFieldCaption ) : - Control( pDialog, rResId ), - pDlg( pDialog ), - pFtCaption( pFtFieldCaption ), - eType( eFieldType ), - nFieldSelected( 0 ), - pAccessible( NULL ) -{ - Init(); - if (eType != TYPE_SELECT && pFtCaption) - aName = MnemonicGenerator::EraseAllMnemonicChars( pFtCaption->GetText() ); -} - -ScDPFieldWindow::ScDPFieldWindow( - ScDPLayoutDlg* pDialog, - const ResId& rResId, - ScDPFieldType eFieldType, - const String& rName ) : - Control( pDialog, rResId ), - aName(rName), - pDlg( pDialog ), - pFtCaption( NULL ), - eType( eFieldType ), - nFieldSelected( 0 ), - pAccessible( NULL ) -{ - Init(); -} +// ============================================================================ -void ScDPFieldWindow::Init() -{ - aWndRect = Rectangle( GetPosPixel(), GetSizePixel() ); - nFieldSize = (eType == TYPE_SELECT) ? PAGE_SIZE : ((eType == TYPE_PAGE) ? MAX_PAGEFIELDS : MAX_FIELDS); +namespace { - if( pFtCaption ) - { - Size aWinSize( aWndRect.GetSize() ); - Size aTextSize( GetTextWidth( pFtCaption->GetText() ), GetTextHeight() ); - aTextPos.X() = (aWinSize.Width() - aTextSize.Width()) / 2; - aTextPos.Y() = (aWinSize.Height() - aTextSize.Height()) / 2; - } +/** Line width for insertion cursor in pixels. */ +const long CURSOR_WIDTH = 3; - GetStyleSettings(); -} - -__EXPORT ScDPFieldWindow::~ScDPFieldWindow() -{ - if (pAccessible) - { - com::sun::star::uno::Reference < com::sun::star::accessibility::XAccessible > xTempAcc = xAccessible; - if (xTempAcc.is()) - pAccessible->dispose(); - } -} +/** Number of tracking events before auto scrolling starts. */ +const size_t INITIAL_TRACKING_DELAY = 20; -//------------------------------------------------------------------- - -void ScDPFieldWindow::GetStyleSettings() -{ - const StyleSettings& rStyleSet = GetSettings().GetStyleSettings(); - aFaceColor = rStyleSet.GetFaceColor(); - aWinColor = rStyleSet.GetWindowColor(); - aTextColor = rStyleSet.GetButtonTextColor(); - aWinTextColor = rStyleSet.GetWindowTextColor(); -} +} // namespace -//------------------------------------------------------------------- +// ============================================================================ -Point ScDPFieldWindow::GetFieldPosition( size_t nIndex ) const +ScPivotFieldWindow::ScPivotWindowField::ScPivotWindowField( const ScDPLabelData& rLabelData ) : + maFuncData( rLabelData.mnCol, rLabelData.mnFuncMask ), + maFieldName( rLabelData.getDisplayName() ) { - Point aPos; - switch( eType ) - { - case TYPE_PAGE: - aPos.X() = OWIDTH * (nIndex % (MAX_PAGEFIELDS / 2)); - aPos.Y() = OHEIGHT * (nIndex / (MAX_PAGEFIELDS / 2)); - break; - case TYPE_COL: - aPos.X() = OWIDTH * (nIndex % (MAX_FIELDS / 2)); - aPos.Y() = OHEIGHT * (nIndex / (MAX_FIELDS / 2)); - break; - case TYPE_ROW: - case TYPE_DATA: - aPos.X() = 0; - aPos.Y() = OHEIGHT * nIndex; - break; - case TYPE_SELECT: - aPos.X() = (OWIDTH + SSPACE) * (nIndex / LINE_SIZE); - aPos.Y() = (OHEIGHT + SSPACE) * (nIndex % LINE_SIZE); - break; - } - return aPos; } -Size ScDPFieldWindow::GetFieldSize() const +ScPivotFieldWindow::ScPivotWindowField::ScPivotWindowField( ScPivotLayoutDlg& rDialog, const ScPivotField& rField, bool bDataWindow ) : + maFuncData( rField.nCol, rField.nFuncMask, rField.maFieldRef ) { - return Size( (eType == TYPE_DATA) ? GetSizePixel().Width() : OWIDTH, OHEIGHT ); + InitFieldName( rDialog, bDataWindow ); } -Point ScDPFieldWindow::GetLastPosition() const +ScPivotFieldWindow::ScPivotWindowField::ScPivotWindowField( ScPivotLayoutDlg& rDialog, const ScPivotFuncData& rFuncData, bool bDataWindow ) : + maFuncData( rFuncData ) { - return OutputToScreenPixel( GetFieldPosition( nFieldSize - 1 ) ); + InitFieldName( rDialog, bDataWindow ); } -bool ScDPFieldWindow::GetFieldIndex( const Point& rPos, size_t& rnIndex ) const +void ScPivotFieldWindow::ScPivotWindowField::InitFieldName( ScPivotLayoutDlg& rDialog, bool bDataWindow ) { - rnIndex = INVALID_INDEX; - if( (rPos.X() >= 0) && (rPos.Y() >= 0) ) + if( maFuncData.mnCol != PIVOT_DATA_FIELD ) { - switch( eType ) + ScDPLabelData* pLabelData = rDialog.GetLabelData( maFuncData.mnCol ); + DBG_ASSERT( pLabelData, "ScPivotWindowField::InitFieldName - no label data found" ); + if( pLabelData ) { - case TYPE_ROW: - case TYPE_DATA: - rnIndex = rPos.Y() / OHEIGHT; - break; - case TYPE_PAGE: + if( bDataWindow ) { - size_t nRow = rPos.Y() / OHEIGHT; - size_t nCol = rPos.X() / OWIDTH; - rnIndex = nRow * MAX_PAGEFIELDS / 2 + nCol; + // write original nFuncMask to label data + pLabelData->mnFuncMask = maFuncData.mnFuncMask; + // GetFuncString() modifies nFuncMask (e.g. auto to sum or count) + maFieldName = rDialog.GetFuncString( maFuncData.mnFuncMask, pLabelData->mbIsValue ); } - break; - case TYPE_COL: - { - size_t nRow = rPos.Y() / OHEIGHT; - size_t nCol = rPos.X() / OWIDTH; - rnIndex = nRow * MAX_FIELDS / 2 + nCol; - } - break; - case TYPE_SELECT: - { - size_t nRow = rPos.Y() / (OHEIGHT + SSPACE); - size_t nCol = rPos.X() / (OWIDTH + SSPACE); - // is not between controls? - if( (rPos.Y() % (OHEIGHT + SSPACE) < OHEIGHT) && (rPos.X() % (OWIDTH + SSPACE) < OWIDTH) ) - rnIndex = nCol * LINE_SIZE + nRow; - } - break; + maFieldName += pLabelData->getDisplayName(); } } - return IsValidIndex( rnIndex ); } -//------------------------------------------------------------------- - -void ScDPFieldWindow::DrawBackground( OutputDevice& rDev ) -{ - Point aPos0; - Size aSize( GetSizePixel() ); +// ============================================================================ - if ( eType == TYPE_SELECT ) - { - rDev.SetLineColor(); - rDev.SetFillColor( aFaceColor ); - rDev.DrawRect( Rectangle( aPos0, aSize ) ); +ScPivotFieldWindow::ScPivotFieldWindow( ScPivotLayoutDlg* pDialog, const ResId& rResId, + ScrollBar& rScrollBar, FixedText* pFtCaption, const OUString& rName, + ScPivotFieldType eFieldType, const sal_Char* pcHelpId, PointerStyle eDropPointer, + size_t nColCount, size_t nRowCount, long nFieldWidthFactor, long nSpaceSize ) : + Control( pDialog, rResId ), + mpDialog( pDialog ), + mpAccessible( 0 ), + mrScrollBar( rScrollBar ), + mpFtCaption( pFtCaption ), + maName( rName ), + meFieldType( eFieldType ), + meDropPointer( eDropPointer ), + mnColCount( nColCount ), + mnRowCount( nRowCount ), + mnFirstVisIndex( 0 ), + mnSelectIndex( 0 ), + mnInsCursorIndex( PIVOTFIELD_INVALID ), + mnOldFirstVisIndex( 0 ), + mnAutoScrollDelay( 0 ), + mbVertical( eFieldType == PIVOTFIELDTYPE_SELECT ), + mbIsTrackingSource( false ) +{ + SetHelpId( pcHelpId ); + + mnLineSize = mbVertical ? mnRowCount : mnColCount; + mnPageSize = mnColCount * mnRowCount; + + // a single field is 36x12 appfont units + maFieldSize = LogicToPixel( Size( 36, 12 ), MapMode( MAP_APPFONT ) ); + maFieldSize.Width() *= nFieldWidthFactor; + maSpaceSize = LogicToPixel( Size( nSpaceSize, nSpaceSize ), MapMode( MAP_APPFONT ) ); + + // set window size + long nWinWidth = static_cast< long >( mnColCount * maFieldSize.Width() + (mnColCount - 1) * maSpaceSize.Width() ); + long nWinHeight = static_cast< long >( mnRowCount * maFieldSize.Height() + (mnRowCount - 1) * maSpaceSize.Height() ); + SetSizePixel( Size( nWinWidth, nWinHeight ) ); + + // scroll bar + Point aScrollBarPos = GetPosPixel(); + Size aScrollBarSize( nWinWidth, nWinHeight ); + if( mbVertical ) + { + aScrollBarPos.Y() += nWinHeight + maSpaceSize.Height(); + aScrollBarSize.Height() = GetSettings().GetStyleSettings().GetScrollBarSize(); } else { - rDev.SetLineColor( aWinTextColor ); - rDev.SetFillColor( aWinColor ); - rDev.DrawRect( Rectangle( aPos0, aSize ) ); - - rDev.SetTextColor( aWinTextColor ); - - /* Draw the caption text. This needs some special handling, because we - support hard line breaks here. This part will draw each line of the - text for itself. */ - - xub_StrLen nTokenCnt = GetText().GetTokenCount( '\n' ); - long nY = (aSize.Height() - nTokenCnt * rDev.GetTextHeight()) / 2; - for( xub_StrLen nToken = 0, nStringIx = 0; nToken < nTokenCnt; ++nToken ) - { - String aLine( GetText().GetToken( 0, '\n', nStringIx ) ); - Point aLinePos( (aSize.Width() - rDev.GetCtrlTextWidth( aLine )) / 2, nY ); - rDev.DrawCtrlText( aLinePos, aLine ); - nY += rDev.GetTextHeight(); - } + aScrollBarPos.X() += nWinWidth + maSpaceSize.Width(); + aScrollBarSize.Width() = GetSettings().GetStyleSettings().GetScrollBarSize(); } + mrScrollBar.SetPosSizePixel( aScrollBarPos, aScrollBarSize ); + mrScrollBar.SetLineSize( 1 ); + mrScrollBar.SetPageSize( static_cast< long >( mbVertical ? mnColCount : mnRowCount ) ); + mrScrollBar.SetVisibleSize( static_cast< long >( mbVertical ? mnColCount : mnRowCount ) ); + mrScrollBar.SetScrollHdl( LINK( this, ScPivotFieldWindow, ScrollHdl ) ); + mrScrollBar.SetEndScrollHdl( LINK( this, ScPivotFieldWindow, ScrollHdl ) ); } -void ScDPFieldWindow::DrawField( - OutputDevice& rDev, const Rectangle& rRect, FieldString& rText, bool bFocus ) +ScPivotFieldWindow::~ScPivotFieldWindow() { - VirtualDevice aVirDev( rDev ); - // #i97623# VirtualDevice is always LTR while other windows derive direction from parent - aVirDev.EnableRTL( IsRTLEnabled() ); + ::rtl::Reference< ScAccessibleDataPilotControl > xAcc = GetAccessibleControl(); + if( xAcc.is() ) + xAcc->dispose(); +} - String aText = rText.first; - Size aDevSize( rRect.GetSize() ); - long nWidth = aDevSize.Width(); - long nHeight = aDevSize.Height(); - long nLabelWidth = rDev.GetTextWidth( aText ); - long nLabelHeight = rDev.GetTextHeight(); - - // #i31600# if text is too long, cut and add ellipsis - rText.second = nLabelWidth + 6 <= nWidth; - if( !rText.second ) - { - xub_StrLen nMinLen = 0; - xub_StrLen nMaxLen = aText.Len(); - bool bFits = false; - do - { - xub_StrLen nCurrLen = (nMinLen + nMaxLen) / 2; - aText = String( rText.first, 0, nCurrLen ).AppendAscii( "..." ); - nLabelWidth = rDev.GetTextWidth( aText ); - bFits = nLabelWidth + 6 <= nWidth; - (bFits ? nMinLen : nMaxLen) = nCurrLen; - } - while( !bFits || (nMinLen + 1 < nMaxLen) ); +void ScPivotFieldWindow::ReadDataLabels( const ScDPLabelDataVector& rLabels ) +{ + maFields.clear(); + maFields.reserve( rLabels.size() ); + for( ScDPLabelDataVector::const_iterator aIt = rLabels.begin(), aEnd = rLabels.end(); aIt != aEnd; ++aIt ) + { + ScPivotWindowField aField( *aIt ); + if( aField.maFieldName.getLength() > 0 ) + maFields.push_back( aField ); } - Point aLabelPos( (nWidth - nLabelWidth) / 2, ::std::max< long >( (nHeight - nLabelHeight) / 2, 3 ) ); - - aVirDev.SetOutputSizePixel( aDevSize ); - aVirDev.SetFont( rDev.GetFont() ); - DecorationView aDecoView( &aVirDev ); - aDecoView.DrawButton( Rectangle( Point( 0, 0 ), aDevSize ), bFocus ? BUTTON_DRAW_DEFAULT : 0 ); - aVirDev.SetTextColor( aTextColor ); - aVirDev.DrawText( aLabelPos, aText ); - rDev.DrawBitmap( rRect.TopLeft(), aVirDev.GetBitmap( Point( 0, 0 ), aDevSize ) ); + Invalidate(); } -void ScDPFieldWindow::Redraw() +void ScPivotFieldWindow::ReadPivotFields( const ScPivotFieldVector& rPivotFields ) { - VirtualDevice aVirDev; - // #i97623# VirtualDevice is always LTR while other windows derive direction from parent - aVirDev.EnableRTL( IsRTLEnabled() ); - aVirDev.SetMapMode( MAP_PIXEL ); - - Point aPos0; - Size aSize( GetSizePixel() ); - Font aFont( GetFont() ); // Font vom Window - aFont.SetTransparent( sal_True ); - aVirDev.SetFont( aFont ); - aVirDev.SetOutputSizePixel( aSize ); - - DrawBackground( aVirDev ); - - if( !aFieldArr.empty() && (nFieldSelected >= aFieldArr.size()) ) - nFieldSelected = aFieldArr.size() - 1; - Rectangle aFieldRect( aPos0, GetFieldSize() ); - for( size_t nIx = 0; nIx < aFieldArr.size(); ++nIx ) + maFields.clear(); + maFields.reserve( rPivotFields.size() ); + for( ScPivotFieldVector::const_iterator aIt = rPivotFields.begin(), aEnd = rPivotFields.end(); aIt != aEnd; ++aIt ) { - aFieldRect.SetPos( GetFieldPosition( nIx ) ); - bool bFocus = HasFocus() && (nIx == nFieldSelected); - DrawField( aVirDev, aFieldRect, aFieldArr[ nIx ], bFocus ); + ScPivotWindowField aField( *mpDialog, *aIt, meFieldType == PIVOTFIELDTYPE_DATA ); + if( aField.maFieldName.getLength() > 0 ) + maFields.push_back( aField ); } - DrawBitmap( aPos0, aVirDev.GetBitmap( aPos0, aSize ) ); + Invalidate(); +} - if( HasFocus() && (nFieldSelected < aFieldArr.size()) ) +void ScPivotFieldWindow::WriteFieldNames( ScDPNameVec& rFieldNames ) const +{ + rFieldNames.clear(); + rFieldNames.reserve( maFields.size() ); + // do not use the names stored in maFields, but generate plain display names from label data + for( ScPivotWindowFieldVector::const_iterator aIt = maFields.begin(), aEnd = maFields.end(); aIt != aEnd; ++aIt ) { - long nFieldWidth = aFieldRect.GetWidth(); - long nSelectionWidth = Min( GetTextWidth( aFieldArr[ nFieldSelected ].first ) + 4, nFieldWidth - 6 ); - Rectangle aSelection( - GetFieldPosition( nFieldSelected ) + Point( (nFieldWidth - nSelectionWidth) / 2, 3 ), - Size( nSelectionWidth, aFieldRect.GetHeight() - 6 ) ); - InvertTracking( aSelection, SHOWTRACK_SMALL | SHOWTRACK_WINDOW ); + if( ScDPLabelData* pLabelData = mpDialog->GetLabelData( aIt->maFuncData.mnCol ) ) + { + OUString aDisplayName = pLabelData->getDisplayName(); + if( aDisplayName.getLength() > 0 ) + rFieldNames.push_back( aDisplayName ); + } } - - UpdateStyle(); } -void ScDPFieldWindow::UpdateStyle() +void ScPivotFieldWindow::WritePivotFields( ScPivotFieldVector& rPivotFields ) const { - WinBits nMask = ~(WB_TABSTOP | WB_NOTABSTOP); - SetStyle( (GetStyle() & nMask) | (IsEmpty() ? WB_NOTABSTOP : WB_TABSTOP) ); + rPivotFields.resize( maFields.size() ); + ScPivotFieldVector::iterator aOutIt = rPivotFields.begin(); + for( ScPivotWindowFieldVector::const_iterator aIt = maFields.begin(), aEnd = maFields.end(); aIt != aEnd; ++aIt, ++aOutIt ) + { + aOutIt->nCol = aIt->maFuncData.mnCol; + aOutIt->nFuncMask = aIt->maFuncData.mnFuncMask; + aOutIt->maFieldRef = aIt->maFuncData.maFieldRef; + } } -//------------------------------------------------------------------- - -bool ScDPFieldWindow::IsValidIndex( size_t nIndex ) const +OUString ScPivotFieldWindow::GetDescription() const { - return nIndex < nFieldSize; + switch( meFieldType ) + { + case PIVOTFIELDTYPE_COL: return String( ScResId( STR_ACC_DATAPILOT_COL_DESCR ) ); + case PIVOTFIELDTYPE_ROW: return String( ScResId( STR_ACC_DATAPILOT_ROW_DESCR ) ); + case PIVOTFIELDTYPE_DATA: return String( ScResId( STR_ACC_DATAPILOT_DATA_DESCR ) ); + case PIVOTFIELDTYPE_SELECT: return String( ScResId( STR_ACC_DATAPILOT_SEL_DESCR ) ); + default:; + } + return OUString(); } -bool ScDPFieldWindow::IsExistingIndex( size_t nIndex ) const +OUString ScPivotFieldWindow::GetFieldText( size_t nFieldIndex ) const { - return nIndex < aFieldArr.size(); + return (nFieldIndex < maFields.size()) ? maFields[ nFieldIndex ].maFieldName : OUString(); } -bool ScDPFieldWindow::IsShortenedText( size_t nIndex ) const +ScPivotFuncDataEntry ScPivotFieldWindow::FindFuncDataByCol( SCCOL nCol ) const { - return (nIndex < aFieldArr.size()) && !aFieldArr[ nIndex ].second; + for( ScPivotWindowFieldVector::const_iterator aIt = maFields.begin(), aEnd = maFields.end(); aIt != aEnd; ++aIt ) + if( aIt->maFuncData.mnCol == nCol ) + return ScPivotFuncDataEntry( &aIt->maFuncData, aIt - maFields.begin() ); + return ScPivotFuncDataEntry( 0, PIVOTFIELD_INVALID ); } -size_t ScDPFieldWindow::CalcNewFieldIndex( SCsCOL nDX, SCsROW nDY ) const +Point ScPivotFieldWindow::GetFieldPosition( size_t nFieldIndex ) const { - size_t nNewField = nFieldSelected; - switch( eType ) - { - case TYPE_PAGE: - nNewField += static_cast<SCsCOLROW>(nDX) + nDY * MAX_PAGEFIELDS / 2; - break; - case TYPE_COL: - nNewField += static_cast<SCsCOLROW>(nDX) + nDY * MAX_FIELDS / 2; - break; - case TYPE_ROW: - case TYPE_DATA: - nNewField += nDY; - break; - case TYPE_SELECT: - nNewField += static_cast<SCsCOLROW>(nDX) * LINE_SIZE + nDY; - break; - } - - return IsExistingIndex( nNewField ) ? nNewField : nFieldSelected; + long nRelIndex = static_cast< long >( nFieldIndex ) - mnFirstVisIndex; + long nCol = static_cast< long >( mbVertical ? (nRelIndex / mnRowCount) : (nRelIndex % mnColCount) ); + long nRow = static_cast< long >( mbVertical ? (nRelIndex % mnRowCount) : (nRelIndex / mnColCount) ); + return Point( nCol * (maFieldSize.Width() + maSpaceSize.Width()), nRow * (maFieldSize.Height() + maSpaceSize.Height()) ); } -void ScDPFieldWindow::SetSelection( size_t nIndex ) +size_t ScPivotFieldWindow::GetFieldIndex( const Point& rWindowPos ) const { - if( !aFieldArr.empty() ) + if( (rWindowPos.X() >= 0) && (rWindowPos.Y() >= 0) ) { - if( nFieldSelected >= aFieldArr.size() ) - nFieldSelected = aFieldArr.size() - 1; - if( nFieldSelected != nIndex ) + long nGridWidth = maFieldSize.Width() + maSpaceSize.Width(); + long nGridHeight = maFieldSize.Height() + maSpaceSize.Height(); + size_t nCol = static_cast< size_t >( rWindowPos.X() / nGridWidth ); + size_t nRow = static_cast< size_t >( rWindowPos.Y() / nGridHeight ); + if( (nCol < mnColCount) && (nRow < mnRowCount) ) { - sal_Int32 nOldSelected(nFieldSelected); - nFieldSelected = nIndex; - Redraw(); - - if (pAccessible && HasFocus()) + long nColOffset = rWindowPos.X() % nGridWidth; + long nRowOffset = rWindowPos.Y() % nGridHeight; + // check that passed position is not in the space between the fields + if( (nColOffset < maFieldSize.Width()) && (nRowOffset < maFieldSize.Height()) ) { - com::sun::star::uno::Reference < com::sun::star::accessibility::XAccessible > xTempAcc = xAccessible; - if (xTempAcc.is()) - pAccessible->FieldFocusChange(nOldSelected, nFieldSelected); - else - pAccessible = NULL; + size_t nFieldIndex = mnFirstVisIndex + (mbVertical ? (nCol * mnRowCount + nRow) : (nRow * mnColCount + nCol)); + return (nFieldIndex < maFields.size()) ? nFieldIndex : PIVOTFIELD_INVALID; } } } + return PIVOTFIELD_INVALID; } -void ScDPFieldWindow::SetSelectionHome() +size_t ScPivotFieldWindow::GetDropIndex( const Point& rWindowPos ) const { - if( !aFieldArr.empty() ) + if( (rWindowPos.X() >= 0) && (rWindowPos.Y() >= 0) ) { - if( eType == TYPE_SELECT ) - pDlg->NotifyMoveSlider( KEY_HOME ); - SetSelection( 0 ); + long nGridWidth = maFieldSize.Width() + maSpaceSize.Width(); + long nGridHeight = maFieldSize.Height() + maSpaceSize.Height(); + size_t nCol = static_cast< size_t >( rWindowPos.X() / nGridWidth ); + size_t nRow = static_cast< size_t >( rWindowPos.Y() / nGridHeight ); + if( (nCol < mnColCount) && (nRow < mnRowCount) ) + { + size_t nFieldIndex = mnFirstVisIndex + (mbVertical ? (nCol * mnRowCount + nRow) : (nRow * mnColCount + nCol)); + long nColOffset = rWindowPos.X() % nGridWidth; + long nRowOffset = rWindowPos.Y() % nGridHeight; + // take next field, if position is in right/lower third + if( (mnColCount == 1) ? (nRowOffset * 3 > nGridHeight * 2) : (nColOffset * 3 > nGridWidth * 2) ) + ++nFieldIndex; + return ::std::min( nFieldIndex, maFields.size() ); + } } + return maFields.size(); } -void ScDPFieldWindow::SetSelectionEnd() +void ScPivotFieldWindow::GrabFocusAndSelect( size_t nSelectIndex ) { - if( !aFieldArr.empty() ) - { - if( eType == TYPE_SELECT ) - pDlg->NotifyMoveSlider( KEY_END ); - SetSelection( aFieldArr.size() - 1 ); - } + if( !HasFocus() ) GrabFocus(); + MoveSelection( nSelectIndex ); } -void ScDPFieldWindow::MoveSelection( sal_uInt16 nKeyCode, SCsCOL nDX, SCsROW nDY ) +void ScPivotFieldWindow::SelectNextField() { - size_t nNewIndex = CalcNewFieldIndex( nDX, nDY ); - if( (eType == TYPE_SELECT) && (nNewIndex == nFieldSelected) ) + MoveSelection( NEXT_FIELD ); +} + +void ScPivotFieldWindow::InsertField( size_t nInsertIndex, const ScPivotFuncData& rFuncData ) +{ + if( (meFieldType != PIVOTFIELDTYPE_SELECT) && (nInsertIndex <= maFields.size()) ) { - if( pDlg->NotifyMoveSlider( nKeyCode ) ) + size_t nFieldIndex = FindFuncDataByCol( rFuncData.mnCol ).second; + if( nFieldIndex < maFields.size() ) + { + // field exists already in this window, move it to the specified position + MoveField( nFieldIndex, nInsertIndex ); + } + else { - switch( nKeyCode ) + // insert the field into the vector and notify accessibility object + ScPivotWindowField aField( *mpDialog, rFuncData, meFieldType == PIVOTFIELDTYPE_DATA ); + if( aField.maFieldName.getLength() > 0 ) { - case KEY_UP: nNewIndex += (LINE_SIZE - 1); break; - case KEY_DOWN: nNewIndex -= (LINE_SIZE - 1); break; + InsertFieldUnchecked( nInsertIndex, aField ); + // adjust selection and scroll position + MoveSelection( nInsertIndex ); + Invalidate(); } } } - SetSelection( nNewIndex ); } -void ScDPFieldWindow::ModifySelectionOffset( long nOffsetDiff ) +bool ScPivotFieldWindow::RemoveField( size_t nRemoveIndex ) { - nFieldSelected -= nOffsetDiff; - Redraw(); + if( (meFieldType != PIVOTFIELDTYPE_SELECT) && (nRemoveIndex < maFields.size()) ) + { + // remove the field from the vector and notify accessibility object + RemoveFieldUnchecked( nRemoveIndex ); + // adjust selection and scroll position, if last field is removed + if( !maFields.empty() ) + MoveSelection( (mnSelectIndex < maFields.size()) ? mnSelectIndex : (maFields.size() - 1) ); + Invalidate(); + return true; + } + return false; } -void ScDPFieldWindow::SelectNext() +bool ScPivotFieldWindow::MoveField( size_t nFieldIndex, size_t nInsertIndex ) { - if( eType == TYPE_SELECT ) - MoveSelection( KEY_DOWN, 0, 1 ); + /* If field is moved behind current position, insertion index needs to be + adjusted, because the field is first removed from the vector. This is + done before nFieldIndex and nInsertIndex are checked for equality, to + catch the cases "move before ourselves" and "move bedind ourselves" + which are both no-ops. */ + if( nFieldIndex < nInsertIndex ) + --nInsertIndex; + + if( (meFieldType != PIVOTFIELDTYPE_SELECT) && (nFieldIndex != nInsertIndex) && (nFieldIndex < maFields.size()) && (nInsertIndex < maFields.size()) ) + { + // move the field in the vector and notify accessibility object + ScPivotWindowField aField = maFields[ nFieldIndex ]; + RemoveFieldUnchecked( nFieldIndex ); + InsertFieldUnchecked( nInsertIndex, aField ); + // adjust selection and scroll position + MoveSelection( nInsertIndex ); + Invalidate(); + return true; + } + return false; } -void ScDPFieldWindow::GrabFocusWithSel( size_t nIndex ) +const ScPivotFuncData* ScPivotFieldWindow::GetSelectedFuncData() const { - SetSelection( nIndex ); - if( !HasFocus() ) - GrabFocus(); + return (mnSelectIndex < maFields.size()) ? &maFields[ mnSelectIndex ].maFuncData : 0; } -void ScDPFieldWindow::MoveField( size_t nDestIndex ) +void ScPivotFieldWindow::ModifySelectedField( const ScPivotFuncData& rFuncData ) { - if( nDestIndex != nFieldSelected ) + if( mnSelectIndex < maFields.size() ) { - // "recycle" existing functionality - pDlg->NotifyMouseButtonDown( eType, nFieldSelected ); - pDlg->NotifyMouseButtonUp( OutputToScreenPixel( GetFieldPosition( nDestIndex ) ) ); + maFields[ mnSelectIndex ].maFuncData = rFuncData; + maFields[ mnSelectIndex ].InitFieldName( *mpDialog, meFieldType == PIVOTFIELDTYPE_DATA ); + Invalidate(); } } -void ScDPFieldWindow::MoveFieldRel( SCsCOL nDX, SCsROW nDY ) +bool ScPivotFieldWindow::RemoveSelectedField() { - MoveField( CalcNewFieldIndex( nDX, nDY ) ); + return RemoveField( mnSelectIndex ); } -//------------------------------------------------------------------- - -void __EXPORT ScDPFieldWindow::Paint( const Rectangle& /* rRect */ ) +bool ScPivotFieldWindow::MoveSelectedField( size_t nInsertIndex ) { - // #124828# hiding the caption is now done from StateChanged - Redraw(); + return MoveField( mnSelectIndex, nInsertIndex ); } -void ScDPFieldWindow::UseMnemonic() +void ScPivotFieldWindow::NotifyStartTracking() { - // Now the FixedText has its mnemonic char. Grab the text and hide the - // FixedText to be able to handle tabstop and mnemonics separately. - if( pFtCaption ) - { - SetText( pFtCaption->GetText() ); - pFtCaption->Hide(); - } - - // after reading the mnemonics, tab stop style bits can be updated - UpdateStyle(); + // rescue old scrolling index, to be able to restore it when tracking is cancelled + mnOldFirstVisIndex = mnFirstVisIndex; } -void __EXPORT ScDPFieldWindow::DataChanged( const DataChangedEvent& rDCEvt ) +void ScPivotFieldWindow::NotifyTracking( const Point& rWindowPos ) { - if( (rDCEvt.GetType() == DATACHANGED_SETTINGS) && (rDCEvt.GetFlags() & SETTINGS_STYLE) ) + size_t nFieldIndex = GetDropIndex( rWindowPos ); + + // insertion index changed: draw new cursor and exit + if( nFieldIndex != mnInsCursorIndex ) { - GetStyleSettings(); - Redraw(); + mnInsCursorIndex = nFieldIndex; + mnAutoScrollDelay = INITIAL_TRACKING_DELAY; + Invalidate(); + return; } - Control::DataChanged( rDCEvt ); -} -void __EXPORT ScDPFieldWindow::MouseButtonDown( const MouseEvent& rMEvt ) -{ - if( rMEvt.IsLeft() ) + // insertion index unchanged: countdown for auto scrolling + if( mnAutoScrollDelay > 0 ) { - size_t nIndex = 0; - if( GetFieldIndex( rMEvt.GetPosPixel(), nIndex ) && IsExistingIndex( nIndex ) ) - { - GrabFocusWithSel( nIndex ); + --mnAutoScrollDelay; + return; + } - if( rMEvt.GetClicks() == 1 ) - { - PointerStyle ePtr = pDlg->NotifyMouseButtonDown( eType, nIndex ); - CaptureMouse(); - SetPointer( Pointer( ePtr ) ); - } - else - pDlg->NotifyDoubleClick( eType, nIndex ); - } + // check if tracking happens on first or last field + long nScrollDelta = 0; + if( (mnInsCursorIndex > 0) && (mnInsCursorIndex == mnFirstVisIndex) ) + nScrollDelta = -static_cast< long >( mnLineSize ); + else if( (mnInsCursorIndex < maFields.size()) && (mnInsCursorIndex == mnFirstVisIndex + mnPageSize) ) + nScrollDelta = static_cast< long >( mnLineSize ); + if( nScrollDelta != 0 ) + { + // update mnInsCursorIndex, so it will be drawn at the same position after scrolling + mnInsCursorIndex += nScrollDelta; + mnFirstVisIndex += nScrollDelta; + // delay auto scroll by line size, to slow down scrolling in column/page windows + mnAutoScrollDelay = mnLineSize - 1; + Invalidate(); } } -void __EXPORT ScDPFieldWindow::MouseButtonUp( const MouseEvent& rMEvt ) +void ScPivotFieldWindow::NotifyEndTracking( ScPivotFieldEndTracking eEndType ) { - if( rMEvt.IsLeft() ) + if( eEndType != ENDTRACKING_DROP ) + mnFirstVisIndex = mnOldFirstVisIndex; + if( eEndType != ENDTRACKING_SUSPEND ) { - if( rMEvt.GetClicks() == 1 ) - { - pDlg->NotifyMouseButtonUp( OutputToScreenPixel( rMEvt.GetPosPixel() ) ); - SetPointer( Pointer( POINTER_ARROW ) ); - } - - if( IsMouseCaptured() ) - ReleaseMouse(); + mnOldFirstVisIndex = PIVOTFIELD_INVALID; + mbIsTrackingSource = false; } + mnInsCursorIndex = PIVOTFIELD_INVALID; + Invalidate(); } -void __EXPORT ScDPFieldWindow::MouseMove( const MouseEvent& rMEvt ) +// protected ------------------------------------------------------------------ + +void ScPivotFieldWindow::Paint( const Rectangle& /*rRect*/ ) { - if( IsMouseCaptured() ) + // prepare a virtual device for buffered painting + VirtualDevice aVirDev; + // #i97623# VirtualDevice is always LTR on construction while other windows derive direction from parent + aVirDev.EnableRTL( IsRTLEnabled() ); + aVirDev.SetMapMode( MAP_PIXEL ); + aVirDev.SetOutputSizePixel( GetSizePixel() ); + Font aFont = GetFont(); + aFont.SetTransparent( true ); + aVirDev.SetFont( aFont ); + + // draw the background and all fields + DrawBackground( aVirDev ); + for( size_t nFieldIndex = mnFirstVisIndex, nEndIndex = mnFirstVisIndex + mnPageSize; nFieldIndex < nEndIndex; ++nFieldIndex ) + DrawField( aVirDev, nFieldIndex ); + DrawInsertionCursor( aVirDev ); + DrawBitmap( Point( 0, 0 ), aVirDev.GetBitmap( Point( 0, 0 ), GetSizePixel() ) ); + + // draw field text focus + if( HasFocus() && (mnSelectIndex < maFields.size()) && (mnFirstVisIndex <= mnSelectIndex) && (mnSelectIndex < mnFirstVisIndex + mnPageSize) ) { - PointerStyle ePtr = pDlg->NotifyMouseMove( OutputToScreenPixel( rMEvt.GetPosPixel() ) ); - SetPointer( Pointer( ePtr ) ); + long nFieldWidth = maFieldSize.Width(); + long nSelectionWidth = Min( GetTextWidth( maFields[ mnSelectIndex ].maFieldName ) + 4, nFieldWidth - 6 ); + Rectangle aSelection( + GetFieldPosition( mnSelectIndex ) + Point( (nFieldWidth - nSelectionWidth) / 2, 3 ), + Size( nSelectionWidth, maFieldSize.Height() - 6 ) ); + InvertTracking( aSelection, SHOWTRACK_SMALL | SHOWTRACK_WINDOW ); } - size_t nIndex = 0; - if( GetFieldIndex( rMEvt.GetPosPixel(), nIndex ) && IsShortenedText( nIndex ) ) + + // update scrollbar + size_t nFieldCount = maFields.size(); + /* Already show the scrollbar if window is full but no fields are hidden + (yet). This gives the user the hint that it is now possible to add more + fields to the window. */ + mrScrollBar.Show( nFieldCount >= mnPageSize ); + mrScrollBar.Enable( nFieldCount > mnPageSize ); + if( mrScrollBar.IsVisible() ) { - Point aPos = OutputToScreenPixel( rMEvt.GetPosPixel() ); - Rectangle aRect( aPos, GetSizePixel() ); - String aHelpText = GetFieldText(nIndex); - Help::ShowQuickHelp( this, aRect, aHelpText ); + mrScrollBar.SetRange( Range( 0, static_cast< long >( (nFieldCount - 1) / mnLineSize + 1 ) ) ); + mrScrollBar.SetThumbPos( static_cast< long >( mnFirstVisIndex / mnLineSize ) ); } + + /* Exclude empty fields from tab chain, but do not disable them. They need + to be enabled because they still act as target for field movement via + keyboard shortcuts. */ + WinBits nMask = ~(WB_TABSTOP | WB_NOTABSTOP); + SetStyle( (GetStyle() & nMask) | (IsEmpty() ? WB_NOTABSTOP : WB_TABSTOP) ); } -void __EXPORT ScDPFieldWindow::KeyInput( const KeyEvent& rKEvt ) +void ScPivotFieldWindow::StateChanged( StateChangedType nStateChange ) { - const KeyCode& rKeyCode = rKEvt.GetKeyCode(); - sal_uInt16 nCode = rKeyCode.GetCode(); - sal_Bool bKeyEvaluated = sal_False; + Control::StateChanged( nStateChange ); - if( rKeyCode.IsMod1() && (eType != TYPE_SELECT) ) - { - bKeyEvaluated = sal_True; - switch( nCode ) - { - case KEY_UP: MoveFieldRel( 0, -1 ); break; - case KEY_DOWN: MoveFieldRel( 0, 1 ); break; - case KEY_LEFT: MoveFieldRel( -1, 0 ); break; - case KEY_RIGHT: MoveFieldRel( 1, 0 ); break; - case KEY_HOME: MoveField( 0 ); break; - case KEY_END: MoveField( aFieldArr.size() - 1 ); break; - default: bKeyEvaluated = sal_False; - } - } - else + if( nStateChange == STATE_CHANGE_INITSHOW ) { - bKeyEvaluated = sal_True; - switch( nCode ) + /* After the fixed text associated to this control has received its + unique mnemonic from VCL dialog initialization code, put this text + into the field windows. + #124828# Hiding the FixedTexts and clearing the tab stop style bits + has to be done after assigning the mnemonics, but Paint() is too + late, because the test tool may send key events to the dialog when + it isn't visible. Mnemonics are assigned in Dialog::StateChanged() + for STATE_CHANGE_INITSHOW, so this can be done immediately + afterwards. */ + if( mpFtCaption ) { - case KEY_UP: MoveSelection( nCode, 0, -1 ); break; - case KEY_DOWN: MoveSelection( nCode, 0, 1 ); break; - case KEY_LEFT: MoveSelection( nCode, -1, 0 ); break; - case KEY_RIGHT: MoveSelection( nCode, 1, 0 ); break; - case KEY_HOME: SetSelectionHome(); break; - case KEY_END: SetSelectionEnd(); break; - case KEY_DELETE: - pDlg->NotifyRemoveField( eType, nFieldSelected ); break; - default: bKeyEvaluated = sal_False; + SetText( mpFtCaption->GetText() ); + mpFtCaption->Hide(); } } - - if( !bKeyEvaluated ) - Control::KeyInput( rKEvt ); } -void __EXPORT ScDPFieldWindow::GetFocus() +void ScPivotFieldWindow::DataChanged( const DataChangedEvent& rDCEvt ) { - Control::GetFocus(); - Redraw(); - if( GetGetFocusFlags() & GETFOCUS_MNEMONIC ) // move field on shortcut - pDlg->NotifyMoveField( eType ); - else // else change focus - pDlg->NotifyFieldFocus( eType, sal_True ); - - if (pAccessible) - { - com::sun::star::uno::Reference < com::sun::star::accessibility::XAccessible > xTempAcc = xAccessible; - if (xTempAcc.is()) - pAccessible->GotFocus(); - else - pAccessible = NULL; - } + Control::DataChanged( rDCEvt ); + if( (rDCEvt.GetType() == DATACHANGED_SETTINGS) && (rDCEvt.GetFlags() & SETTINGS_STYLE) ) + Invalidate(); } -void __EXPORT ScDPFieldWindow::LoseFocus() +void ScPivotFieldWindow::KeyInput( const KeyEvent& rKEvt ) { - Control::LoseFocus(); - Redraw(); - pDlg->NotifyFieldFocus( eType, sal_False ); + bool bKeyEvaluated = false; - if (pAccessible) + if( !maFields.empty() ) { - com::sun::star::uno::Reference < com::sun::star::accessibility::XAccessible > xTempAcc = xAccessible; - if (xTempAcc.is()) - pAccessible->LostFocus(); + const KeyCode& rKeyCode = rKEvt.GetKeyCode(); + sal_uInt16 nCode = rKeyCode.GetCode(); + + // do not move fields in selection window + if( rKeyCode.IsMod1() && (meFieldType != PIVOTFIELDTYPE_SELECT) ) + { + bKeyEvaluated = true; + switch( nCode ) + { + case KEY_UP: MoveSelectedField( mbVertical ? PREV_FIELD : PREV_LINE ); break; + case KEY_DOWN: MoveSelectedField( mbVertical ? NEXT_FIELD : NEXT_LINE ); break; + case KEY_LEFT: MoveSelectedField( mbVertical ? PREV_LINE : PREV_FIELD ); break; + case KEY_RIGHT: MoveSelectedField( mbVertical ? NEXT_LINE : NEXT_FIELD ); break; + case KEY_HOME: MoveSelectedField( FIRST_FIELD ); break; + case KEY_END: MoveSelectedField( LAST_FIELD ); break; + default: bKeyEvaluated = false; + } + } else - pAccessible = NULL; + { + bKeyEvaluated = true; + switch( nCode ) + { + case KEY_UP: MoveSelection( mbVertical ? PREV_FIELD : PREV_LINE ); break; + case KEY_DOWN: MoveSelection( mbVertical ? NEXT_FIELD : NEXT_LINE ); break; + case KEY_LEFT: MoveSelection( mbVertical ? PREV_LINE : PREV_FIELD ); break; + case KEY_RIGHT: MoveSelection( mbVertical ? NEXT_LINE : NEXT_FIELD ); break; + case KEY_PAGEUP: MoveSelection( PREV_PAGE ); break; + case KEY_PAGEDOWN: MoveSelection( NEXT_PAGE ); break; + case KEY_HOME: MoveSelection( FIRST_FIELD ); break; + case KEY_END: MoveSelection( LAST_FIELD ); break; + // delete field per DEL key - dialog needs to change focus if window becomes empty + case KEY_DELETE: RemoveSelectedField(); mpDialog->NotifyFieldRemoved( *this ); break; + default: bKeyEvaluated = false; + } + } } -} -//------------------------------------------------------------------- + if( !bKeyEvaluated ) + Control::KeyInput( rKEvt ); +} -void ScDPFieldWindow::AddField( const String& rText, size_t nNewIndex ) +void ScPivotFieldWindow::MouseButtonDown( const MouseEvent& rMEvt ) { - DBG_ASSERT( nNewIndex == aFieldArr.size(), "ScDPFieldWindow::AddField - invalid index" ); - if( IsValidIndex( nNewIndex ) ) + if( rMEvt.IsLeft() ) { - aFieldArr.push_back( FieldString( rText, true ) ); - if (pAccessible) + size_t nNewSelectIndex = GetFieldIndex( rMEvt.GetPosPixel() ); + if( nNewSelectIndex < maFields.size() ) { - com::sun::star::uno::Reference < com::sun::star::accessibility::XAccessible > xTempAcc = xAccessible; - if (xTempAcc.is()) - pAccessible->AddField(nNewIndex); + // grabbing after GetFieldIndex() will prevent to focus empty window + GrabFocusAndSelect( nNewSelectIndex ); + if( rMEvt.GetClicks() == 1 ) + { + // one click: start tracking + mbIsTrackingSource = true; + mnOldFirstVisIndex = mnFirstVisIndex; + mpDialog->NotifyStartTracking( *this ); + } else - pAccessible = NULL; + { + // two clicks: open field options dialog + mpDialog->NotifyDoubleClick( *this ); + } } } } -void ScDPFieldWindow::DelField( size_t nDelIndex ) +void ScPivotFieldWindow::RequestHelp( const HelpEvent& rHEvt ) { - if( IsExistingIndex( nDelIndex ) ) + if( (rHEvt.GetMode() & HELPMODE_QUICK) != 0 ) { - if (pAccessible) // before decrement fieldcount + // show a tooltip with full field name, if field text is clipped + size_t nFieldIndex = GetFieldIndex( rHEvt.GetMousePosPixel() - GetPosPixel() ); + if( (nFieldIndex < maFields.size()) && maFields[ nFieldIndex ].mbClipped ) { - com::sun::star::uno::Reference < com::sun::star::accessibility::XAccessible > xTempAcc = xAccessible; - if (xTempAcc.is()) - pAccessible->RemoveField(nDelIndex); - else - pAccessible = NULL; + Rectangle aRect( rHEvt.GetMousePosPixel(), GetSizePixel() ); + Help::ShowQuickHelp( this, aRect, maFields[ nFieldIndex ].maFieldName ); + return; } - aFieldArr.erase( aFieldArr.begin() + nDelIndex ); - Redraw(); } + Control::RequestHelp( rHEvt ); } -void ScDPFieldWindow::ClearFields() +void ScPivotFieldWindow::GetFocus() { - if( eType == TYPE_SELECT || eType == TYPE_PAGE || eType == TYPE_COL || eType == TYPE_ROW || eType == TYPE_DATA) - { - com::sun::star::uno::Reference < com::sun::star::accessibility::XAccessible > xTempAcc = xAccessible; - if (!xTempAcc.is() && pAccessible) - pAccessible = NULL; - if (pAccessible) - for( size_t nIdx = aFieldArr.size(); nIdx > 0; --nIdx ) - pAccessible->RemoveField( nIdx - 1 ); + Control::GetFocus(); + Invalidate(); + ::rtl::Reference< ScAccessibleDataPilotControl > xAcc = GetAccessibleControl(); + if( xAcc.is() ) + xAcc->GotFocus(); +} - aFieldArr.clear(); - } +void ScPivotFieldWindow::LoseFocus() +{ + Control::LoseFocus(); + Invalidate(); + ::rtl::Reference< ScAccessibleDataPilotControl > xAcc = GetAccessibleControl(); + if( xAcc.is() ) + xAcc->LostFocus(); +} + +uno::Reference< accessibility::XAccessible > ScPivotFieldWindow::CreateAccessible() +{ + mpAccessible = new ScAccessibleDataPilotControl( GetAccessibleParentWindow()->GetAccessible(), this ); + uno::Reference< accessibility::XAccessible > xReturn( mpAccessible ); + mpAccessible->Init(); + mxAccessible = xReturn; + return xReturn; +} + +// private -------------------------------------------------------------------- + +size_t ScPivotFieldWindow::RecalcVisibleIndex( size_t nSelectIndex ) const +{ + // calculate a scrolling offset that shows the selected field + size_t nNewFirstVisIndex = mnFirstVisIndex; + if( nSelectIndex < nNewFirstVisIndex ) + nNewFirstVisIndex = static_cast< size_t >( (nSelectIndex / mnLineSize) * mnLineSize ); + else if( nSelectIndex >= nNewFirstVisIndex + mnPageSize ) + nNewFirstVisIndex = static_cast< size_t >( (nSelectIndex / mnLineSize + 1) * mnLineSize ) - mnPageSize; + // check if there are complete empty lines in the bottom/right + size_t nMaxFirstVisIndex = (maFields.size() <= mnPageSize) ? 0 : (((maFields.size() - 1) / mnLineSize + 1) * mnLineSize - mnPageSize); + return ::std::min( nNewFirstVisIndex, nMaxFirstVisIndex ); } -void ScDPFieldWindow::SetFieldText( const String& rText, size_t nIndex ) +void ScPivotFieldWindow::SetSelectionUnchecked( size_t nSelectIndex, size_t nFirstVisIndex ) { - if( IsExistingIndex( nIndex ) ) + if( !maFields.empty() && (nSelectIndex < maFields.size()) ) { - aFieldArr[ nIndex ] = FieldString( rText, true ); - Redraw(); + bool bScrollPosChanged = mnFirstVisIndex != nFirstVisIndex; + bool bSelectionChanged = mnSelectIndex != nSelectIndex; - if (pAccessible) + sal_Int32 nOldSelected = static_cast< sal_Int32 >( mnSelectIndex ); + mnFirstVisIndex = nFirstVisIndex; + mnSelectIndex = nSelectIndex; + + if( bScrollPosChanged || bSelectionChanged ) + Invalidate(); + + // TODO: accessibility action for changed scrolling position? + + // notify accessibility object about changed selection + if( bSelectionChanged && HasFocus() ) { - com::sun::star::uno::Reference < com::sun::star::accessibility::XAccessible > xTempAcc = xAccessible; - if (xTempAcc.is()) - pAccessible->FieldNameChange(nIndex); - else - pAccessible = NULL; + ::rtl::Reference< ScAccessibleDataPilotControl > xAcc = GetAccessibleControl(); + if( xAcc.is() ) + xAcc->FieldFocusChange( nOldSelected, static_cast< sal_Int32 >( mnSelectIndex ) ); } } } -const String& ScDPFieldWindow::GetFieldText( size_t nIndex ) const +void ScPivotFieldWindow::MoveSelection( size_t nSelectIndex ) { - if( IsExistingIndex( nIndex ) ) - return aFieldArr[ nIndex ].first; - return EMPTY_STRING; + if( nSelectIndex < maFields.size() ) + SetSelectionUnchecked( nSelectIndex, RecalcVisibleIndex( nSelectIndex ) ); } -//------------------------------------------------------------------- - -bool ScDPFieldWindow::AddField( const String& rText, const Point& rPos, size_t& rnIndex ) +void ScPivotFieldWindow::MoveSelection( MoveType eMoveType ) { - if ( aFieldArr.size() == nFieldSize ) - return sal_False; + if( maFields.empty() ) + return; - size_t nNewIndex = 0; - if( GetFieldIndex( rPos, nNewIndex ) ) + size_t nLastIndex = maFields.size() - 1; + size_t nNewSelectIndex = mnSelectIndex; + switch( eMoveType ) { - if( nNewIndex > aFieldArr.size() ) - nNewIndex = aFieldArr.size(); - - aFieldArr.insert( aFieldArr.begin() + nNewIndex, FieldString( rText, true ) ); - nFieldSelected = nNewIndex; - Redraw(); - rnIndex = nNewIndex; + case PREV_FIELD: + nNewSelectIndex = (nNewSelectIndex > 0) ? (nNewSelectIndex - 1) : 0; + break; + case NEXT_FIELD: + nNewSelectIndex = (nNewSelectIndex < nLastIndex) ? (nNewSelectIndex + 1) : nLastIndex; + break; + case PREV_LINE: + nNewSelectIndex = (nNewSelectIndex > mnLineSize) ? (nNewSelectIndex - mnLineSize) : 0; + break; + case NEXT_LINE: + nNewSelectIndex = (nNewSelectIndex + mnLineSize < nLastIndex) ? (nNewSelectIndex + mnLineSize) : nLastIndex; + break; + case PREV_PAGE: + nNewSelectIndex = (nNewSelectIndex > mnPageSize) ? (nNewSelectIndex - mnPageSize) : 0; + break; + case NEXT_PAGE: + nNewSelectIndex = (nNewSelectIndex + mnPageSize < nLastIndex) ? (nNewSelectIndex + mnPageSize) : nLastIndex; + break; + case FIRST_FIELD: + nNewSelectIndex = 0; + break; + case LAST_FIELD: + nNewSelectIndex = nLastIndex; + break; + } - if (pAccessible) - { - com::sun::star::uno::Reference < com::sun::star::accessibility::XAccessible > xTempAcc = xAccessible; - if (xTempAcc.is()) - pAccessible->AddField(nNewIndex); - else - pAccessible = NULL; - } + // SetSelectionUnchecked() redraws the control and updates the scrollbar + SetSelectionUnchecked( nNewSelectIndex, RecalcVisibleIndex( nNewSelectIndex ) ); +} - return true; +void ScPivotFieldWindow::MoveSelectedField( MoveType eMoveType ) +{ + if( mnSelectIndex < maFields.size() ) + { + // find position to insert the field by changing the selection first + size_t nOldSelectIndex = mnSelectIndex; + MoveSelection( eMoveType ); + MoveField( nOldSelectIndex, (nOldSelectIndex < mnSelectIndex) ? (mnSelectIndex + 1) : mnSelectIndex ); } +} - return false; +void ScPivotFieldWindow::InsertFieldUnchecked( size_t nInsertIndex, const ScPivotWindowField& rField ) +{ + maFields.insert( maFields.begin() + nInsertIndex, rField ); + ::rtl::Reference< ScAccessibleDataPilotControl > xAcc = GetAccessibleControl(); + if( xAcc.is() ) + xAcc->AddField( static_cast< sal_Int32 >( nInsertIndex ) ); +} + +void ScPivotFieldWindow::RemoveFieldUnchecked( size_t nRemoveIndex ) +{ + ::rtl::Reference< ScAccessibleDataPilotControl > xAcc = GetAccessibleControl(); + if( xAcc.is() ) + xAcc->RemoveField( static_cast< sal_Int32 >( nRemoveIndex ) ); + maFields.erase( maFields.begin() + nRemoveIndex ); } -void ScDPFieldWindow::GetExistingIndex( const Point& rPos, size_t& rnIndex ) +void ScPivotFieldWindow::DrawBackground( OutputDevice& rDev ) { - if( !aFieldArr.empty() && (eType != TYPE_SELECT) && GetFieldIndex( rPos, rnIndex ) ) + Size aDevSize = rDev.GetOutputSizePixel(); + const StyleSettings& rStyleSett = GetSettings().GetStyleSettings(); + + if( meFieldType == PIVOTFIELDTYPE_SELECT ) { - if( rnIndex >= aFieldArr.size() ) - rnIndex = aFieldArr.size() - 1; + rDev.SetLineColor(); + rDev.SetFillColor( rStyleSett.GetFaceColor() ); + rDev.DrawRect( Rectangle( Point( 0, 0 ), aDevSize ) ); } else - rnIndex = 0; + { + rDev.SetLineColor( rStyleSett.GetWindowTextColor() ); + rDev.SetFillColor( rStyleSett.GetWindowColor() ); + rDev.DrawRect( Rectangle( Point( 0, 0 ), aDevSize ) ); + + /* Draw the caption text. This needs some special handling, because we + support hard line breaks here. This part will draw each line of the + text for itself. */ + rDev.SetTextColor( rStyleSett.GetWindowTextColor() ); + xub_StrLen nTokenCnt = GetText().GetTokenCount( '\n' ); + long nY = (aDevSize.Height() - nTokenCnt * rDev.GetTextHeight()) / 2; + for( xub_StrLen nToken = 0, nStringIx = 0; nToken < nTokenCnt; ++nToken ) + { + String aLine = GetText().GetToken( 0, '\n', nStringIx ); + Point aLinePos( (aDevSize.Width() - rDev.GetCtrlTextWidth( aLine )) / 2, nY ); + rDev.DrawCtrlText( aLinePos, aLine ); + nY += rDev.GetTextHeight(); + } + } } -String ScDPFieldWindow::GetDescription() const +void ScPivotFieldWindow::DrawField( OutputDevice& rDev, size_t nFieldIndex ) { - String sDescription; - switch( eType ) + if( (nFieldIndex < maFields.size()) && (mnFirstVisIndex <= nFieldIndex) && (nFieldIndex < mnFirstVisIndex + mnPageSize) ) { - case TYPE_COL: - sDescription = ScResId(STR_ACC_DATAPILOT_COL_DESCR); - break; - case TYPE_ROW: - sDescription = ScResId(STR_ACC_DATAPILOT_ROW_DESCR); - break; - case TYPE_DATA: - sDescription = ScResId(STR_ACC_DATAPILOT_DATA_DESCR); - break; - case TYPE_SELECT: - sDescription = ScResId(STR_ACC_DATAPILOT_SEL_DESCR); - break; - default: + // draw the button + Point aFieldPos = GetFieldPosition( nFieldIndex ); + bool bFocus = HasFocus() && (nFieldIndex == mnSelectIndex); + DecorationView aDecoView( &rDev ); + aDecoView.DrawButton( Rectangle( aFieldPos, maFieldSize ), bFocus ? BUTTON_DRAW_DEFAULT : 0 ); + + // #i31600# if text is too long, cut and add ellipsis + const OUString& rFullText = maFields[ nFieldIndex ].maFieldName; + OUString aClippedText = rFullText; + long nLabelWidth = rDev.GetTextWidth( rFullText ); + if( (maFields[ nFieldIndex ].mbClipped = nLabelWidth + 6 > maFieldSize.Width()) == true ) { - // added to avoid warnings + sal_Int32 nMinLen = 0; + sal_Int32 nMaxLen = rFullText.getLength(); + bool bFits = false; + do + { + sal_Int32 nCurrLen = (nMinLen + nMaxLen) / 2; + aClippedText = rFullText.copy( 0, nCurrLen ) + OUString( RTL_CONSTASCII_USTRINGPARAM( "..." ) ); + nLabelWidth = rDev.GetTextWidth( aClippedText ); + bFits = nLabelWidth + 6 <= maFieldSize.Width(); + (bFits ? nMinLen : nMaxLen) = nCurrLen; + } + while( !bFits || (nMinLen + 1 < nMaxLen) ); } + + // draw the button text + Point aLabelOffset( (maFieldSize.Width() - nLabelWidth) / 2, ::std::max< long >( (maFieldSize.Height() - rDev.GetTextHeight()) / 2, 3 ) ); + rDev.SetTextColor( GetSettings().GetStyleSettings().GetButtonTextColor() ); + rDev.DrawText( aFieldPos + aLabelOffset, aClippedText ); } - return sDescription; } -::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessible > ScDPFieldWindow::CreateAccessible() +void ScPivotFieldWindow::DrawInsertionCursor( OutputDevice& rDev ) { - pAccessible = - new ScAccessibleDataPilotControl(GetAccessibleParentWindow()->GetAccessible(), this); + if( (mnInsCursorIndex <= maFields.size()) && (mnFirstVisIndex <= mnInsCursorIndex) && (mnInsCursorIndex <= mnFirstVisIndex + mnPageSize) && + (!mbIsTrackingSource || (mnInsCursorIndex < mnSelectIndex) || (mnInsCursorIndex > mnSelectIndex + 1)) ) + { + Color aTextColor = GetSettings().GetStyleSettings().GetButtonTextColor(); + rDev.SetLineColor( aTextColor ); + rDev.SetFillColor( aTextColor ); - com::sun::star::uno::Reference < ::com::sun::star::accessibility::XAccessible > xReturn = pAccessible; + bool bVerticalCursor = mnColCount > 1; + long nCursorLength = bVerticalCursor ? maFieldSize.Height() : maFieldSize.Width(); - pAccessible->Init(); - xAccessible = xReturn; + bool bEndOfLastField = mnInsCursorIndex == mnFirstVisIndex + mnPageSize; + Point aMainLinePos = GetFieldPosition( bEndOfLastField ? (mnInsCursorIndex - 1) : mnInsCursorIndex ); + if( bEndOfLastField ) + (bVerticalCursor ? aMainLinePos.X() : aMainLinePos.Y()) += ((bVerticalCursor ? maFieldSize.Width() : maFieldSize.Height()) - CURSOR_WIDTH); + else if( (bVerticalCursor ? aMainLinePos.X() : aMainLinePos.Y()) > 0 ) + (bVerticalCursor ? aMainLinePos.X() : aMainLinePos.Y()) -= ((CURSOR_WIDTH + 1) / 2); + Size aMainLineSize( bVerticalCursor ? CURSOR_WIDTH : nCursorLength, bVerticalCursor ? nCursorLength : CURSOR_WIDTH ); + rDev.DrawRect( Rectangle( aMainLinePos, aMainLineSize ) ); - return xReturn; + Point aSubLinePos = aMainLinePos; + (bVerticalCursor ? aSubLinePos.X() : aSubLinePos.Y()) -= CURSOR_WIDTH; + Size aSubLineSize( bVerticalCursor ? (3 * CURSOR_WIDTH) : CURSOR_WIDTH, bVerticalCursor ? CURSOR_WIDTH : (3 * CURSOR_WIDTH) ); + rDev.DrawRect( Rectangle( aSubLinePos, aSubLineSize ) ); + + (bVerticalCursor ? aSubLinePos.Y() : aSubLinePos.X()) += (nCursorLength - CURSOR_WIDTH); + rDev.DrawRect( Rectangle( aSubLinePos, aSubLineSize ) ); + } } -//=================================================================== +::rtl::Reference< ScAccessibleDataPilotControl > ScPivotFieldWindow::GetAccessibleControl() +{ + ::rtl::Reference< ScAccessibleDataPilotControl > xAccImpl; + if( mpAccessible ) + { + // try to resolve the weak reference mxAccessible + uno::Reference< accessibility::XAccessible > xAcc = mxAccessible; + if( xAcc.is() ) + xAccImpl.set( mpAccessible ); // the rtl reference keeps the object alive + else + mpAccessible = 0; // object is dead, forget the pointer + } + return xAccImpl; + } + +// handlers ------------------------------------------------------------------- + +IMPL_LINK( ScPivotFieldWindow, ScrollHdl, ScrollBar*, pScrollBar ) +{ + // scrollbar may return negative values, if it is too small + long nThumbPos = pScrollBar->GetThumbPos(); + if( nThumbPos >= 0 ) + { + size_t nNewFirstVisIndex = static_cast< size_t >( nThumbPos * mnLineSize ); + // keep the selection index on same relative position inside row/column + size_t nSelectLineOffset = mnSelectIndex % mnLineSize; + size_t nNewSelectIndex = mnSelectIndex; + if( nNewSelectIndex < nNewFirstVisIndex ) + nNewSelectIndex = nNewFirstVisIndex + nSelectLineOffset; + else if( nNewSelectIndex >= nNewFirstVisIndex + mnPageSize ) + nNewSelectIndex = nNewFirstVisIndex + mnPageSize - mnLineSize + nSelectLineOffset; + nNewSelectIndex = ::std::min( nNewSelectIndex, maFields.size() - 1 ); + SetSelectionUnchecked( nNewSelectIndex, nNewFirstVisIndex ); + } + GrabFocus(); + return 0; +} +// ============================================================================ |