1 /************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2010 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 11 * OpenOffice.org is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU Lesser General Public License version 3 13 * only, as published by the Free Software Foundation. 14 * 15 * OpenOffice.org is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License version 3 for more details 19 * (a copy is included in the LICENSE file that accompanied this code). 20 * 21 * You should have received a copy of the GNU Lesser General Public License 22 * version 3 along with OpenOffice.org. If not, see 23 * <http://www.openoffice.org/license.html> 24 * for a copy of the LGPLv3 License. 25 * 26 ************************************************************************/ 27 28 // MARKER(update_precomp.py): autogen include statement, do not remove 29 #include "precompiled_sc.hxx" 30 31 #include "fieldwnd.hxx" 32 33 #include <tools/debug.hxx> 34 #include <vcl/decoview.hxx> 35 #include <vcl/help.hxx> 36 #include <vcl/svapp.hxx> 37 #include <vcl/virdev.hxx> 38 39 #include "pvlaydlg.hxx" 40 #include "AccessibleDataPilotControl.hxx" 41 #include "scresid.hxx" 42 #include "sc.hrc" 43 44 // ============================================================================ 45 46 using namespace ::com::sun::star; 47 using ::rtl::OUString; 48 49 // ============================================================================ 50 51 namespace { 52 53 /** Line width for insertion cursor in pixels. */ 54 const long CURSOR_WIDTH = 3; 55 56 /** Number of tracking events before auto scrolling starts. */ 57 const size_t INITIAL_TRACKING_DELAY = 20; 58 59 } // namespace 60 61 // ============================================================================ 62 63 ScPivotFieldWindow::ScPivotWindowField::ScPivotWindowField( const ScDPLabelData& rLabelData ) : 64 maFuncData( rLabelData.mnCol, rLabelData.mnFuncMask ), 65 maFieldName( rLabelData.getDisplayName() ) 66 { 67 } 68 69 ScPivotFieldWindow::ScPivotWindowField::ScPivotWindowField( ScPivotLayoutDlg& rDialog, const ScPivotField& rField, bool bDataWindow ) : 70 maFuncData( rField.nCol, rField.nFuncMask, rField.maFieldRef ) 71 { 72 InitFieldName( rDialog, bDataWindow ); 73 } 74 75 ScPivotFieldWindow::ScPivotWindowField::ScPivotWindowField( ScPivotLayoutDlg& rDialog, const ScPivotFuncData& rFuncData, bool bDataWindow ) : 76 maFuncData( rFuncData ) 77 { 78 InitFieldName( rDialog, bDataWindow ); 79 } 80 81 void ScPivotFieldWindow::ScPivotWindowField::InitFieldName( ScPivotLayoutDlg& rDialog, bool bDataWindow ) 82 { 83 if( maFuncData.mnCol != PIVOT_DATA_FIELD ) 84 { 85 ScDPLabelData* pLabelData = rDialog.GetLabelData( maFuncData.mnCol ); 86 DBG_ASSERT( pLabelData, "ScPivotWindowField::InitFieldName - no label data found" ); 87 if( pLabelData ) 88 { 89 if( bDataWindow ) 90 { 91 // write original nFuncMask to label data 92 pLabelData->mnFuncMask = maFuncData.mnFuncMask; 93 // GetFuncString() modifies nFuncMask (e.g. auto to sum or count) 94 maFieldName = rDialog.GetFuncString( maFuncData.mnFuncMask, pLabelData->mbIsValue ); 95 } 96 else 97 maFieldName = OUString(); // #i118111# don't append to previous string 98 maFieldName += pLabelData->getDisplayName(); 99 } 100 } 101 } 102 103 // ============================================================================ 104 105 ScPivotFieldWindow::ScPivotFieldWindow( ScPivotLayoutDlg* pDialog, const ResId& rResId, 106 ScrollBar& rScrollBar, FixedText* pFtCaption, const OUString& rName, 107 ScPivotFieldType eFieldType, const sal_Char* pcHelpId, PointerStyle eDropPointer, 108 size_t nColCount, size_t nRowCount, long nFieldWidthFactor, long nSpaceSize ) : 109 Control( pDialog, rResId ), 110 mpDialog( pDialog ), 111 mpAccessible( 0 ), 112 mrScrollBar( rScrollBar ), 113 mpFtCaption( pFtCaption ), 114 maName( rName ), 115 meFieldType( eFieldType ), 116 meDropPointer( eDropPointer ), 117 mnColCount( nColCount ), 118 mnRowCount( nRowCount ), 119 mnFirstVisIndex( 0 ), 120 mnSelectIndex( 0 ), 121 mnInsCursorIndex( PIVOTFIELD_INVALID ), 122 mnOldFirstVisIndex( 0 ), 123 mnAutoScrollDelay( 0 ), 124 mbVertical( eFieldType == PIVOTFIELDTYPE_SELECT ), 125 mbIsTrackingSource( false ) 126 { 127 SetHelpId( pcHelpId ); 128 129 mnLineSize = mbVertical ? mnRowCount : mnColCount; 130 mnPageSize = mnColCount * mnRowCount; 131 132 // a single field is 36x12 appfont units 133 maFieldSize = LogicToPixel( Size( 36, 12 ), MapMode( MAP_APPFONT ) ); 134 maFieldSize.Width() *= nFieldWidthFactor; 135 maSpaceSize = LogicToPixel( Size( nSpaceSize, nSpaceSize ), MapMode( MAP_APPFONT ) ); 136 137 // set window size 138 long nWinWidth = static_cast< long >( mnColCount * maFieldSize.Width() + (mnColCount - 1) * maSpaceSize.Width() ); 139 long nWinHeight = static_cast< long >( mnRowCount * maFieldSize.Height() + (mnRowCount - 1) * maSpaceSize.Height() ); 140 SetSizePixel( Size( nWinWidth, nWinHeight ) ); 141 142 // scroll bar 143 Point aScrollBarPos = GetPosPixel(); 144 Size aScrollBarSize( nWinWidth, nWinHeight ); 145 if( mbVertical ) 146 { 147 aScrollBarPos.Y() += nWinHeight + maSpaceSize.Height(); 148 aScrollBarSize.Height() = GetSettings().GetStyleSettings().GetScrollBarSize(); 149 } 150 else 151 { 152 aScrollBarPos.X() += nWinWidth + maSpaceSize.Width(); 153 aScrollBarSize.Width() = GetSettings().GetStyleSettings().GetScrollBarSize(); 154 } 155 mrScrollBar.SetPosSizePixel( aScrollBarPos, aScrollBarSize ); 156 mrScrollBar.SetLineSize( 1 ); 157 mrScrollBar.SetPageSize( static_cast< long >( mbVertical ? mnColCount : mnRowCount ) ); 158 mrScrollBar.SetVisibleSize( static_cast< long >( mbVertical ? mnColCount : mnRowCount ) ); 159 mrScrollBar.SetScrollHdl( LINK( this, ScPivotFieldWindow, ScrollHdl ) ); 160 mrScrollBar.SetEndScrollHdl( LINK( this, ScPivotFieldWindow, ScrollHdl ) ); 161 } 162 163 ScPivotFieldWindow::~ScPivotFieldWindow() 164 { 165 ::rtl::Reference< ScAccessibleDataPilotControl > xAcc = GetAccessibleControl(); 166 if( xAcc.is() ) 167 xAcc->dispose(); 168 } 169 170 void ScPivotFieldWindow::ReadDataLabels( const ScDPLabelDataVector& rLabels ) 171 { 172 maFields.clear(); 173 maFields.reserve( rLabels.size() ); 174 for( ScDPLabelDataVector::const_iterator aIt = rLabels.begin(), aEnd = rLabels.end(); aIt != aEnd; ++aIt ) 175 { 176 ScPivotWindowField aField( *aIt ); 177 if( aField.maFieldName.getLength() > 0 ) 178 maFields.push_back( aField ); 179 } 180 Invalidate(); 181 } 182 183 void ScPivotFieldWindow::ReadPivotFields( const ScPivotFieldVector& rPivotFields ) 184 { 185 maFields.clear(); 186 maFields.reserve( rPivotFields.size() ); 187 for( ScPivotFieldVector::const_iterator aIt = rPivotFields.begin(), aEnd = rPivotFields.end(); aIt != aEnd; ++aIt ) 188 { 189 ScPivotWindowField aField( *mpDialog, *aIt, meFieldType == PIVOTFIELDTYPE_DATA ); 190 if( aField.maFieldName.getLength() > 0 ) 191 maFields.push_back( aField ); 192 } 193 Invalidate(); 194 } 195 196 void ScPivotFieldWindow::WriteFieldNames( ScDPNameVec& rFieldNames ) const 197 { 198 rFieldNames.clear(); 199 rFieldNames.reserve( maFields.size() ); 200 // do not use the names stored in maFields, but generate plain display names from label data 201 for( ScPivotWindowFieldVector::const_iterator aIt = maFields.begin(), aEnd = maFields.end(); aIt != aEnd; ++aIt ) 202 { 203 if( ScDPLabelData* pLabelData = mpDialog->GetLabelData( aIt->maFuncData.mnCol ) ) 204 { 205 OUString aDisplayName = pLabelData->getDisplayName(); 206 if( aDisplayName.getLength() > 0 ) 207 rFieldNames.push_back( aDisplayName ); 208 } 209 } 210 } 211 212 void ScPivotFieldWindow::WritePivotFields( ScPivotFieldVector& rPivotFields ) const 213 { 214 rPivotFields.resize( maFields.size() ); 215 ScPivotFieldVector::iterator aOutIt = rPivotFields.begin(); 216 for( ScPivotWindowFieldVector::const_iterator aIt = maFields.begin(), aEnd = maFields.end(); aIt != aEnd; ++aIt, ++aOutIt ) 217 { 218 aOutIt->nCol = aIt->maFuncData.mnCol; 219 aOutIt->nFuncMask = aIt->maFuncData.mnFuncMask; 220 aOutIt->maFieldRef = aIt->maFuncData.maFieldRef; 221 } 222 } 223 224 OUString ScPivotFieldWindow::GetDescription() const 225 { 226 switch( meFieldType ) 227 { 228 case PIVOTFIELDTYPE_COL: return String( ScResId( STR_ACC_DATAPILOT_COL_DESCR ) ); 229 case PIVOTFIELDTYPE_ROW: return String( ScResId( STR_ACC_DATAPILOT_ROW_DESCR ) ); 230 case PIVOTFIELDTYPE_DATA: return String( ScResId( STR_ACC_DATAPILOT_DATA_DESCR ) ); 231 case PIVOTFIELDTYPE_SELECT: return String( ScResId( STR_ACC_DATAPILOT_SEL_DESCR ) ); 232 default:; 233 } 234 return OUString(); 235 } 236 237 OUString ScPivotFieldWindow::GetFieldText( size_t nFieldIndex ) const 238 { 239 return (nFieldIndex < maFields.size()) ? maFields[ nFieldIndex ].maFieldName : OUString(); 240 } 241 242 ScPivotFuncDataEntry ScPivotFieldWindow::FindFuncDataByCol( SCCOL nCol ) const 243 { 244 for( ScPivotWindowFieldVector::const_iterator aIt = maFields.begin(), aEnd = maFields.end(); aIt != aEnd; ++aIt ) 245 if( aIt->maFuncData.mnCol == nCol ) 246 return ScPivotFuncDataEntry( &aIt->maFuncData, aIt - maFields.begin() ); 247 return ScPivotFuncDataEntry( 0, PIVOTFIELD_INVALID ); 248 } 249 250 Point ScPivotFieldWindow::GetFieldPosition( size_t nFieldIndex ) const 251 { 252 long nRelIndex = static_cast< long >( nFieldIndex ) - mnFirstVisIndex; 253 long nCol = static_cast< long >( mbVertical ? (nRelIndex / mnRowCount) : (nRelIndex % mnColCount) ); 254 long nRow = static_cast< long >( mbVertical ? (nRelIndex % mnRowCount) : (nRelIndex / mnColCount) ); 255 return Point( nCol * (maFieldSize.Width() + maSpaceSize.Width()), nRow * (maFieldSize.Height() + maSpaceSize.Height()) ); 256 } 257 258 size_t ScPivotFieldWindow::GetFieldIndex( const Point& rWindowPos ) const 259 { 260 if( (rWindowPos.X() >= 0) && (rWindowPos.Y() >= 0) ) 261 { 262 long nGridWidth = maFieldSize.Width() + maSpaceSize.Width(); 263 long nGridHeight = maFieldSize.Height() + maSpaceSize.Height(); 264 size_t nCol = static_cast< size_t >( rWindowPos.X() / nGridWidth ); 265 size_t nRow = static_cast< size_t >( rWindowPos.Y() / nGridHeight ); 266 if( (nCol < mnColCount) && (nRow < mnRowCount) ) 267 { 268 long nColOffset = rWindowPos.X() % nGridWidth; 269 long nRowOffset = rWindowPos.Y() % nGridHeight; 270 // check that passed position is not in the space between the fields 271 if( (nColOffset < maFieldSize.Width()) && (nRowOffset < maFieldSize.Height()) ) 272 { 273 size_t nFieldIndex = mnFirstVisIndex + (mbVertical ? (nCol * mnRowCount + nRow) : (nRow * mnColCount + nCol)); 274 return (nFieldIndex < maFields.size()) ? nFieldIndex : PIVOTFIELD_INVALID; 275 } 276 } 277 } 278 return PIVOTFIELD_INVALID; 279 } 280 281 size_t ScPivotFieldWindow::GetDropIndex( const Point& rWindowPos ) const 282 { 283 if( (rWindowPos.X() >= 0) && (rWindowPos.Y() >= 0) ) 284 { 285 long nGridWidth = maFieldSize.Width() + maSpaceSize.Width(); 286 long nGridHeight = maFieldSize.Height() + maSpaceSize.Height(); 287 size_t nCol = static_cast< size_t >( rWindowPos.X() / nGridWidth ); 288 size_t nRow = static_cast< size_t >( rWindowPos.Y() / nGridHeight ); 289 if( (nCol < mnColCount) && (nRow < mnRowCount) ) 290 { 291 size_t nFieldIndex = mnFirstVisIndex + (mbVertical ? (nCol * mnRowCount + nRow) : (nRow * mnColCount + nCol)); 292 long nColOffset = rWindowPos.X() % nGridWidth; 293 long nRowOffset = rWindowPos.Y() % nGridHeight; 294 // take next field, if position is in right/lower third 295 if( (mnColCount == 1) ? (nRowOffset * 3 > nGridHeight * 2) : (nColOffset * 3 > nGridWidth * 2) ) 296 ++nFieldIndex; 297 return ::std::min( nFieldIndex, maFields.size() ); 298 } 299 } 300 return maFields.size(); 301 } 302 303 void ScPivotFieldWindow::GrabFocusAndSelect( size_t nSelectIndex ) 304 { 305 if( !HasFocus() ) GrabFocus(); 306 MoveSelection( nSelectIndex ); 307 } 308 309 void ScPivotFieldWindow::SelectNextField() 310 { 311 MoveSelection( NEXT_FIELD ); 312 } 313 314 void ScPivotFieldWindow::InsertField( size_t nInsertIndex, const ScPivotFuncData& rFuncData ) 315 { 316 if( (meFieldType != PIVOTFIELDTYPE_SELECT) && (nInsertIndex <= maFields.size()) ) 317 { 318 size_t nFieldIndex = FindFuncDataByCol( rFuncData.mnCol ).second; 319 if( nFieldIndex < maFields.size() ) 320 { 321 // field exists already in this window, move it to the specified position 322 MoveField( nFieldIndex, nInsertIndex ); 323 } 324 else 325 { 326 // insert the field into the vector and notify accessibility object 327 ScPivotWindowField aField( *mpDialog, rFuncData, meFieldType == PIVOTFIELDTYPE_DATA ); 328 if( aField.maFieldName.getLength() > 0 ) 329 { 330 InsertFieldUnchecked( nInsertIndex, aField ); 331 // adjust selection and scroll position 332 MoveSelection( nInsertIndex ); 333 Invalidate(); 334 } 335 } 336 } 337 } 338 339 bool ScPivotFieldWindow::RemoveField( size_t nRemoveIndex ) 340 { 341 if( (meFieldType != PIVOTFIELDTYPE_SELECT) && (nRemoveIndex < maFields.size()) ) 342 { 343 // remove the field from the vector and notify accessibility object 344 RemoveFieldUnchecked( nRemoveIndex ); 345 // adjust selection and scroll position, if last field is removed 346 if( !maFields.empty() ) 347 MoveSelection( (mnSelectIndex < maFields.size()) ? mnSelectIndex : (maFields.size() - 1) ); 348 Invalidate(); 349 return true; 350 } 351 return false; 352 } 353 354 bool ScPivotFieldWindow::MoveField( size_t nFieldIndex, size_t nInsertIndex ) 355 { 356 /* If field is moved behind current position, insertion index needs to be 357 adjusted, because the field is first removed from the vector. This is 358 done before nFieldIndex and nInsertIndex are checked for equality, to 359 catch the cases "move before ourselves" and "move bedind ourselves" 360 which are both no-ops. */ 361 if( nFieldIndex < nInsertIndex ) 362 --nInsertIndex; 363 364 if( (meFieldType != PIVOTFIELDTYPE_SELECT) && (nFieldIndex != nInsertIndex) && (nFieldIndex < maFields.size()) && (nInsertIndex < maFields.size()) ) 365 { 366 // move the field in the vector and notify accessibility object 367 ScPivotWindowField aField = maFields[ nFieldIndex ]; 368 RemoveFieldUnchecked( nFieldIndex ); 369 InsertFieldUnchecked( nInsertIndex, aField ); 370 // adjust selection and scroll position 371 MoveSelection( nInsertIndex ); 372 Invalidate(); 373 return true; 374 } 375 return false; 376 } 377 378 const ScPivotFuncData* ScPivotFieldWindow::GetSelectedFuncData() const 379 { 380 return (mnSelectIndex < maFields.size()) ? &maFields[ mnSelectIndex ].maFuncData : 0; 381 } 382 383 void ScPivotFieldWindow::ModifySelectedField( const ScPivotFuncData& rFuncData ) 384 { 385 if( mnSelectIndex < maFields.size() ) 386 { 387 maFields[ mnSelectIndex ].maFuncData = rFuncData; 388 maFields[ mnSelectIndex ].InitFieldName( *mpDialog, meFieldType == PIVOTFIELDTYPE_DATA ); 389 Invalidate(); 390 } 391 } 392 393 bool ScPivotFieldWindow::RemoveSelectedField() 394 { 395 return RemoveField( mnSelectIndex ); 396 } 397 398 bool ScPivotFieldWindow::MoveSelectedField( size_t nInsertIndex ) 399 { 400 return MoveField( mnSelectIndex, nInsertIndex ); 401 } 402 403 void ScPivotFieldWindow::NotifyStartTracking() 404 { 405 // rescue old scrolling index, to be able to restore it when tracking is cancelled 406 mnOldFirstVisIndex = mnFirstVisIndex; 407 } 408 409 void ScPivotFieldWindow::NotifyTracking( const Point& rWindowPos ) 410 { 411 size_t nFieldIndex = GetDropIndex( rWindowPos ); 412 413 // insertion index changed: draw new cursor and exit 414 if( nFieldIndex != mnInsCursorIndex ) 415 { 416 mnInsCursorIndex = nFieldIndex; 417 mnAutoScrollDelay = INITIAL_TRACKING_DELAY; 418 Invalidate(); 419 return; 420 } 421 422 // insertion index unchanged: countdown for auto scrolling 423 if( mnAutoScrollDelay > 0 ) 424 { 425 --mnAutoScrollDelay; 426 return; 427 } 428 429 // check if tracking happens on first or last field 430 long nScrollDelta = 0; 431 if( (mnInsCursorIndex > 0) && (mnInsCursorIndex == mnFirstVisIndex) ) 432 nScrollDelta = -static_cast< long >( mnLineSize ); 433 else if( (mnInsCursorIndex < maFields.size()) && (mnInsCursorIndex == mnFirstVisIndex + mnPageSize) ) 434 nScrollDelta = static_cast< long >( mnLineSize ); 435 if( nScrollDelta != 0 ) 436 { 437 // update mnInsCursorIndex, so it will be drawn at the same position after scrolling 438 mnInsCursorIndex += nScrollDelta; 439 mnFirstVisIndex += nScrollDelta; 440 // delay auto scroll by line size, to slow down scrolling in column/page windows 441 mnAutoScrollDelay = mnLineSize - 1; 442 Invalidate(); 443 } 444 } 445 446 void ScPivotFieldWindow::NotifyEndTracking( ScPivotFieldEndTracking eEndType ) 447 { 448 if( eEndType != ENDTRACKING_DROP ) 449 mnFirstVisIndex = mnOldFirstVisIndex; 450 if( eEndType != ENDTRACKING_SUSPEND ) 451 { 452 mnOldFirstVisIndex = PIVOTFIELD_INVALID; 453 mbIsTrackingSource = false; 454 } 455 mnInsCursorIndex = PIVOTFIELD_INVALID; 456 Invalidate(); 457 } 458 459 // protected ------------------------------------------------------------------ 460 461 void ScPivotFieldWindow::Paint( const Rectangle& /*rRect*/ ) 462 { 463 // prepare a virtual device for buffered painting 464 VirtualDevice aVirDev; 465 // #i97623# VirtualDevice is always LTR on construction while other windows derive direction from parent 466 aVirDev.EnableRTL( IsRTLEnabled() ); 467 aVirDev.SetMapMode( MAP_PIXEL ); 468 aVirDev.SetOutputSizePixel( GetSizePixel() ); 469 Font aFont = GetFont(); 470 aFont.SetTransparent( true ); 471 aVirDev.SetFont( aFont ); 472 473 // draw the background and all fields 474 DrawBackground( aVirDev ); 475 for( size_t nFieldIndex = mnFirstVisIndex, nEndIndex = mnFirstVisIndex + mnPageSize; nFieldIndex < nEndIndex; ++nFieldIndex ) 476 DrawField( aVirDev, nFieldIndex ); 477 DrawInsertionCursor( aVirDev ); 478 DrawBitmap( Point( 0, 0 ), aVirDev.GetBitmap( Point( 0, 0 ), GetSizePixel() ) ); 479 480 // draw field text focus 481 if( HasFocus() && (mnSelectIndex < maFields.size()) && (mnFirstVisIndex <= mnSelectIndex) && (mnSelectIndex < mnFirstVisIndex + mnPageSize) ) 482 { 483 long nFieldWidth = maFieldSize.Width(); 484 long nSelectionWidth = Min( GetTextWidth( maFields[ mnSelectIndex ].maFieldName ) + 4, nFieldWidth - 6 ); 485 Rectangle aSelection( 486 GetFieldPosition( mnSelectIndex ) + Point( (nFieldWidth - nSelectionWidth) / 2, 3 ), 487 Size( nSelectionWidth, maFieldSize.Height() - 6 ) ); 488 InvertTracking( aSelection, SHOWTRACK_SMALL | SHOWTRACK_WINDOW ); 489 } 490 491 // update scrollbar 492 size_t nFieldCount = maFields.size(); 493 /* Already show the scrollbar if window is full but no fields are hidden 494 (yet). This gives the user the hint that it is now possible to add more 495 fields to the window. */ 496 mrScrollBar.Show( nFieldCount >= mnPageSize ); 497 mrScrollBar.Enable( nFieldCount > mnPageSize ); 498 if( mrScrollBar.IsVisible() ) 499 { 500 mrScrollBar.SetRange( Range( 0, static_cast< long >( (nFieldCount - 1) / mnLineSize + 1 ) ) ); 501 mrScrollBar.SetThumbPos( static_cast< long >( mnFirstVisIndex / mnLineSize ) ); 502 } 503 504 /* Exclude empty fields from tab chain, but do not disable them. They need 505 to be enabled because they still act as target for field movement via 506 keyboard shortcuts. */ 507 WinBits nMask = ~(WB_TABSTOP | WB_NOTABSTOP); 508 SetStyle( (GetStyle() & nMask) | (IsEmpty() ? WB_NOTABSTOP : WB_TABSTOP) ); 509 } 510 511 void ScPivotFieldWindow::StateChanged( StateChangedType nStateChange ) 512 { 513 Control::StateChanged( nStateChange ); 514 515 if( nStateChange == STATE_CHANGE_INITSHOW ) 516 { 517 /* After the fixed text associated to this control has received its 518 unique mnemonic from VCL dialog initialization code, put this text 519 into the field windows. 520 #124828# Hiding the FixedTexts and clearing the tab stop style bits 521 has to be done after assigning the mnemonics, but Paint() is too 522 late, because the test tool may send key events to the dialog when 523 it isn't visible. Mnemonics are assigned in Dialog::StateChanged() 524 for STATE_CHANGE_INITSHOW, so this can be done immediately 525 afterwards. */ 526 if( mpFtCaption ) 527 { 528 SetText( mpFtCaption->GetText() ); 529 mpFtCaption->Hide(); 530 } 531 } 532 } 533 534 void ScPivotFieldWindow::DataChanged( const DataChangedEvent& rDCEvt ) 535 { 536 Control::DataChanged( rDCEvt ); 537 if( (rDCEvt.GetType() == DATACHANGED_SETTINGS) && (rDCEvt.GetFlags() & SETTINGS_STYLE) ) 538 Invalidate(); 539 } 540 541 void ScPivotFieldWindow::KeyInput( const KeyEvent& rKEvt ) 542 { 543 bool bKeyEvaluated = false; 544 545 if( !maFields.empty() ) 546 { 547 const KeyCode& rKeyCode = rKEvt.GetKeyCode(); 548 sal_uInt16 nCode = rKeyCode.GetCode(); 549 550 // do not move fields in selection window 551 if( rKeyCode.IsMod1() && (meFieldType != PIVOTFIELDTYPE_SELECT) ) 552 { 553 bKeyEvaluated = true; 554 switch( nCode ) 555 { 556 case KEY_UP: MoveSelectedField( mbVertical ? PREV_FIELD : PREV_LINE ); break; 557 case KEY_DOWN: MoveSelectedField( mbVertical ? NEXT_FIELD : NEXT_LINE ); break; 558 case KEY_LEFT: MoveSelectedField( mbVertical ? PREV_LINE : PREV_FIELD ); break; 559 case KEY_RIGHT: MoveSelectedField( mbVertical ? NEXT_LINE : NEXT_FIELD ); break; 560 case KEY_HOME: MoveSelectedField( FIRST_FIELD ); break; 561 case KEY_END: MoveSelectedField( LAST_FIELD ); break; 562 default: bKeyEvaluated = false; 563 } 564 } 565 else 566 { 567 bKeyEvaluated = true; 568 switch( nCode ) 569 { 570 case KEY_UP: MoveSelection( mbVertical ? PREV_FIELD : PREV_LINE ); break; 571 case KEY_DOWN: MoveSelection( mbVertical ? NEXT_FIELD : NEXT_LINE ); break; 572 case KEY_LEFT: MoveSelection( mbVertical ? PREV_LINE : PREV_FIELD ); break; 573 case KEY_RIGHT: MoveSelection( mbVertical ? NEXT_LINE : NEXT_FIELD ); break; 574 case KEY_PAGEUP: MoveSelection( PREV_PAGE ); break; 575 case KEY_PAGEDOWN: MoveSelection( NEXT_PAGE ); break; 576 case KEY_HOME: MoveSelection( FIRST_FIELD ); break; 577 case KEY_END: MoveSelection( LAST_FIELD ); break; 578 // delete field per DEL key - dialog needs to change focus if window becomes empty 579 case KEY_DELETE: RemoveSelectedField(); mpDialog->NotifyFieldRemoved( *this ); break; 580 default: bKeyEvaluated = false; 581 } 582 } 583 } 584 585 if( !bKeyEvaluated ) 586 Control::KeyInput( rKEvt ); 587 } 588 589 void ScPivotFieldWindow::MouseButtonDown( const MouseEvent& rMEvt ) 590 { 591 if( rMEvt.IsLeft() ) 592 { 593 size_t nNewSelectIndex = GetFieldIndex( rMEvt.GetPosPixel() ); 594 if( nNewSelectIndex < maFields.size() ) 595 { 596 // grabbing after GetFieldIndex() will prevent to focus empty window 597 GrabFocusAndSelect( nNewSelectIndex ); 598 if( rMEvt.GetClicks() == 1 ) 599 { 600 // one click: start tracking 601 mbIsTrackingSource = true; 602 mnOldFirstVisIndex = mnFirstVisIndex; 603 mpDialog->NotifyStartTracking( *this ); 604 } 605 else 606 { 607 // two clicks: open field options dialog 608 mpDialog->NotifyDoubleClick( *this ); 609 } 610 } 611 } 612 } 613 614 void ScPivotFieldWindow::RequestHelp( const HelpEvent& rHEvt ) 615 { 616 if( (rHEvt.GetMode() & HELPMODE_QUICK) != 0 ) 617 { 618 // show a tooltip with full field name, if field text is clipped 619 size_t nFieldIndex = GetFieldIndex( rHEvt.GetMousePosPixel() - GetPosPixel() ); 620 if( (nFieldIndex < maFields.size()) && maFields[ nFieldIndex ].mbClipped ) 621 { 622 Rectangle aRect( rHEvt.GetMousePosPixel(), GetSizePixel() ); 623 Help::ShowQuickHelp( this, aRect, maFields[ nFieldIndex ].maFieldName ); 624 return; 625 } 626 } 627 Control::RequestHelp( rHEvt ); 628 } 629 630 void ScPivotFieldWindow::GetFocus() 631 { 632 Control::GetFocus(); 633 Invalidate(); 634 ::rtl::Reference< ScAccessibleDataPilotControl > xAcc = GetAccessibleControl(); 635 if( xAcc.is() ) 636 xAcc->GotFocus(); 637 } 638 639 void ScPivotFieldWindow::LoseFocus() 640 { 641 Control::LoseFocus(); 642 Invalidate(); 643 ::rtl::Reference< ScAccessibleDataPilotControl > xAcc = GetAccessibleControl(); 644 if( xAcc.is() ) 645 xAcc->LostFocus(); 646 } 647 648 uno::Reference< accessibility::XAccessible > ScPivotFieldWindow::CreateAccessible() 649 { 650 mpAccessible = new ScAccessibleDataPilotControl( GetAccessibleParentWindow()->GetAccessible(), this ); 651 uno::Reference< accessibility::XAccessible > xReturn( mpAccessible ); 652 mpAccessible->Init(); 653 mxAccessible = xReturn; 654 return xReturn; 655 } 656 657 // private -------------------------------------------------------------------- 658 659 size_t ScPivotFieldWindow::RecalcVisibleIndex( size_t nSelectIndex ) const 660 { 661 // calculate a scrolling offset that shows the selected field 662 size_t nNewFirstVisIndex = mnFirstVisIndex; 663 if( nSelectIndex < nNewFirstVisIndex ) 664 nNewFirstVisIndex = static_cast< size_t >( (nSelectIndex / mnLineSize) * mnLineSize ); 665 else if( nSelectIndex >= nNewFirstVisIndex + mnPageSize ) 666 nNewFirstVisIndex = static_cast< size_t >( (nSelectIndex / mnLineSize + 1) * mnLineSize ) - mnPageSize; 667 // check if there are complete empty lines in the bottom/right 668 size_t nMaxFirstVisIndex = (maFields.size() <= mnPageSize) ? 0 : (((maFields.size() - 1) / mnLineSize + 1) * mnLineSize - mnPageSize); 669 return ::std::min( nNewFirstVisIndex, nMaxFirstVisIndex ); 670 } 671 672 void ScPivotFieldWindow::SetSelectionUnchecked( size_t nSelectIndex, size_t nFirstVisIndex ) 673 { 674 if( !maFields.empty() && (nSelectIndex < maFields.size()) ) 675 { 676 bool bScrollPosChanged = mnFirstVisIndex != nFirstVisIndex; 677 bool bSelectionChanged = mnSelectIndex != nSelectIndex; 678 679 sal_Int32 nOldSelected = static_cast< sal_Int32 >( mnSelectIndex ); 680 mnFirstVisIndex = nFirstVisIndex; 681 mnSelectIndex = nSelectIndex; 682 683 if( bScrollPosChanged || bSelectionChanged ) 684 Invalidate(); 685 686 // TODO: accessibility action for changed scrolling position? 687 688 // notify accessibility object about changed selection 689 if( bSelectionChanged && HasFocus() ) 690 { 691 ::rtl::Reference< ScAccessibleDataPilotControl > xAcc = GetAccessibleControl(); 692 if( xAcc.is() ) 693 xAcc->FieldFocusChange( nOldSelected, static_cast< sal_Int32 >( mnSelectIndex ) ); 694 } 695 } 696 } 697 698 void ScPivotFieldWindow::MoveSelection( size_t nSelectIndex ) 699 { 700 if( nSelectIndex < maFields.size() ) 701 SetSelectionUnchecked( nSelectIndex, RecalcVisibleIndex( nSelectIndex ) ); 702 } 703 704 void ScPivotFieldWindow::MoveSelection( MoveType eMoveType ) 705 { 706 if( maFields.empty() ) 707 return; 708 709 size_t nLastIndex = maFields.size() - 1; 710 size_t nNewSelectIndex = mnSelectIndex; 711 switch( eMoveType ) 712 { 713 case PREV_FIELD: 714 nNewSelectIndex = (nNewSelectIndex > 0) ? (nNewSelectIndex - 1) : 0; 715 break; 716 case NEXT_FIELD: 717 nNewSelectIndex = (nNewSelectIndex < nLastIndex) ? (nNewSelectIndex + 1) : nLastIndex; 718 break; 719 case PREV_LINE: 720 nNewSelectIndex = (nNewSelectIndex > mnLineSize) ? (nNewSelectIndex - mnLineSize) : 0; 721 break; 722 case NEXT_LINE: 723 nNewSelectIndex = (nNewSelectIndex + mnLineSize < nLastIndex) ? (nNewSelectIndex + mnLineSize) : nLastIndex; 724 break; 725 case PREV_PAGE: 726 nNewSelectIndex = (nNewSelectIndex > mnPageSize) ? (nNewSelectIndex - mnPageSize) : 0; 727 break; 728 case NEXT_PAGE: 729 nNewSelectIndex = (nNewSelectIndex + mnPageSize < nLastIndex) ? (nNewSelectIndex + mnPageSize) : nLastIndex; 730 break; 731 case FIRST_FIELD: 732 nNewSelectIndex = 0; 733 break; 734 case LAST_FIELD: 735 nNewSelectIndex = nLastIndex; 736 break; 737 } 738 739 // SetSelectionUnchecked() redraws the control and updates the scrollbar 740 SetSelectionUnchecked( nNewSelectIndex, RecalcVisibleIndex( nNewSelectIndex ) ); 741 } 742 743 void ScPivotFieldWindow::MoveSelectedField( MoveType eMoveType ) 744 { 745 if( mnSelectIndex < maFields.size() ) 746 { 747 // find position to insert the field by changing the selection first 748 size_t nOldSelectIndex = mnSelectIndex; 749 MoveSelection( eMoveType ); 750 MoveField( nOldSelectIndex, (nOldSelectIndex < mnSelectIndex) ? (mnSelectIndex + 1) : mnSelectIndex ); 751 } 752 } 753 754 void ScPivotFieldWindow::InsertFieldUnchecked( size_t nInsertIndex, const ScPivotWindowField& rField ) 755 { 756 maFields.insert( maFields.begin() + nInsertIndex, rField ); 757 ::rtl::Reference< ScAccessibleDataPilotControl > xAcc = GetAccessibleControl(); 758 if( xAcc.is() ) 759 xAcc->AddField( static_cast< sal_Int32 >( nInsertIndex ) ); 760 } 761 762 void ScPivotFieldWindow::RemoveFieldUnchecked( size_t nRemoveIndex ) 763 { 764 ::rtl::Reference< ScAccessibleDataPilotControl > xAcc = GetAccessibleControl(); 765 if( xAcc.is() ) 766 xAcc->RemoveField( static_cast< sal_Int32 >( nRemoveIndex ) ); 767 maFields.erase( maFields.begin() + nRemoveIndex ); 768 } 769 770 void ScPivotFieldWindow::DrawBackground( OutputDevice& rDev ) 771 { 772 Size aDevSize = rDev.GetOutputSizePixel(); 773 const StyleSettings& rStyleSett = GetSettings().GetStyleSettings(); 774 775 if( meFieldType == PIVOTFIELDTYPE_SELECT ) 776 { 777 rDev.SetLineColor(); 778 rDev.SetFillColor( rStyleSett.GetFaceColor() ); 779 rDev.DrawRect( Rectangle( Point( 0, 0 ), aDevSize ) ); 780 } 781 else 782 { 783 rDev.SetLineColor( rStyleSett.GetWindowTextColor() ); 784 rDev.SetFillColor( rStyleSett.GetWindowColor() ); 785 rDev.DrawRect( Rectangle( Point( 0, 0 ), aDevSize ) ); 786 787 /* Draw the caption text. This needs some special handling, because we 788 support hard line breaks here. This part will draw each line of the 789 text for itself. */ 790 rDev.SetTextColor( rStyleSett.GetWindowTextColor() ); 791 xub_StrLen nTokenCnt = GetText().GetTokenCount( '\n' ); 792 long nY = (aDevSize.Height() - nTokenCnt * rDev.GetTextHeight()) / 2; 793 for( xub_StrLen nToken = 0, nStringIx = 0; nToken < nTokenCnt; ++nToken ) 794 { 795 String aLine = GetText().GetToken( 0, '\n', nStringIx ); 796 Point aLinePos( (aDevSize.Width() - rDev.GetCtrlTextWidth( aLine )) / 2, nY ); 797 rDev.DrawCtrlText( aLinePos, aLine ); 798 nY += rDev.GetTextHeight(); 799 } 800 } 801 } 802 803 void ScPivotFieldWindow::DrawField( OutputDevice& rDev, size_t nFieldIndex ) 804 { 805 if( (nFieldIndex < maFields.size()) && (mnFirstVisIndex <= nFieldIndex) && (nFieldIndex < mnFirstVisIndex + mnPageSize) ) 806 { 807 // draw the button 808 Point aFieldPos = GetFieldPosition( nFieldIndex ); 809 bool bFocus = HasFocus() && (nFieldIndex == mnSelectIndex); 810 DecorationView aDecoView( &rDev ); 811 aDecoView.DrawButton( Rectangle( aFieldPos, maFieldSize ), bFocus ? BUTTON_DRAW_DEFAULT : 0 ); 812 813 // #i31600# if text is too long, cut and add ellipsis 814 const OUString& rFullText = maFields[ nFieldIndex ].maFieldName; 815 OUString aClippedText = rFullText; 816 long nLabelWidth = rDev.GetTextWidth( rFullText ); 817 if( (maFields[ nFieldIndex ].mbClipped = nLabelWidth + 6 > maFieldSize.Width()) == true ) 818 { 819 sal_Int32 nMinLen = 0; 820 sal_Int32 nMaxLen = rFullText.getLength(); 821 bool bFits = false; 822 do 823 { 824 sal_Int32 nCurrLen = (nMinLen + nMaxLen) / 2; 825 aClippedText = rFullText.copy( 0, nCurrLen ) + OUString( RTL_CONSTASCII_USTRINGPARAM( "..." ) ); 826 nLabelWidth = rDev.GetTextWidth( aClippedText ); 827 bFits = nLabelWidth + 6 <= maFieldSize.Width(); 828 (bFits ? nMinLen : nMaxLen) = nCurrLen; 829 } 830 while( !bFits || (nMinLen + 1 < nMaxLen) ); 831 } 832 833 // draw the button text 834 Point aLabelOffset( (maFieldSize.Width() - nLabelWidth) / 2, ::std::max< long >( (maFieldSize.Height() - rDev.GetTextHeight()) / 2, 3 ) ); 835 rDev.SetTextColor( GetSettings().GetStyleSettings().GetButtonTextColor() ); 836 rDev.DrawText( aFieldPos + aLabelOffset, aClippedText ); 837 } 838 } 839 840 void ScPivotFieldWindow::DrawInsertionCursor( OutputDevice& rDev ) 841 { 842 if( (mnInsCursorIndex <= maFields.size()) && (mnFirstVisIndex <= mnInsCursorIndex) && (mnInsCursorIndex <= mnFirstVisIndex + mnPageSize) && 843 (!mbIsTrackingSource || (mnInsCursorIndex < mnSelectIndex) || (mnInsCursorIndex > mnSelectIndex + 1)) ) 844 { 845 Color aTextColor = GetSettings().GetStyleSettings().GetButtonTextColor(); 846 rDev.SetLineColor( aTextColor ); 847 rDev.SetFillColor( aTextColor ); 848 849 bool bVerticalCursor = mnColCount > 1; 850 long nCursorLength = bVerticalCursor ? maFieldSize.Height() : maFieldSize.Width(); 851 852 bool bEndOfLastField = mnInsCursorIndex == mnFirstVisIndex + mnPageSize; 853 Point aMainLinePos = GetFieldPosition( bEndOfLastField ? (mnInsCursorIndex - 1) : mnInsCursorIndex ); 854 if( bEndOfLastField ) 855 (bVerticalCursor ? aMainLinePos.X() : aMainLinePos.Y()) += ((bVerticalCursor ? maFieldSize.Width() : maFieldSize.Height()) - CURSOR_WIDTH); 856 else if( (bVerticalCursor ? aMainLinePos.X() : aMainLinePos.Y()) > 0 ) 857 (bVerticalCursor ? aMainLinePos.X() : aMainLinePos.Y()) -= ((CURSOR_WIDTH + 1) / 2); 858 Size aMainLineSize( bVerticalCursor ? CURSOR_WIDTH : nCursorLength, bVerticalCursor ? nCursorLength : CURSOR_WIDTH ); 859 rDev.DrawRect( Rectangle( aMainLinePos, aMainLineSize ) ); 860 861 Point aSubLinePos = aMainLinePos; 862 (bVerticalCursor ? aSubLinePos.X() : aSubLinePos.Y()) -= CURSOR_WIDTH; 863 Size aSubLineSize( bVerticalCursor ? (3 * CURSOR_WIDTH) : CURSOR_WIDTH, bVerticalCursor ? CURSOR_WIDTH : (3 * CURSOR_WIDTH) ); 864 rDev.DrawRect( Rectangle( aSubLinePos, aSubLineSize ) ); 865 866 (bVerticalCursor ? aSubLinePos.Y() : aSubLinePos.X()) += (nCursorLength - CURSOR_WIDTH); 867 rDev.DrawRect( Rectangle( aSubLinePos, aSubLineSize ) ); 868 } 869 } 870 871 ::rtl::Reference< ScAccessibleDataPilotControl > ScPivotFieldWindow::GetAccessibleControl() 872 { 873 ::rtl::Reference< ScAccessibleDataPilotControl > xAccImpl; 874 if( mpAccessible ) 875 { 876 // try to resolve the weak reference mxAccessible 877 uno::Reference< accessibility::XAccessible > xAcc = mxAccessible; 878 if( xAcc.is() ) 879 xAccImpl.set( mpAccessible ); // the rtl reference keeps the object alive 880 else 881 mpAccessible = 0; // object is dead, forget the pointer 882 } 883 return xAccImpl; 884 } 885 886 // handlers ------------------------------------------------------------------- 887 888 IMPL_LINK( ScPivotFieldWindow, ScrollHdl, ScrollBar*, pScrollBar ) 889 { 890 // scrollbar may return negative values, if it is too small 891 long nThumbPos = pScrollBar->GetThumbPos(); 892 if( nThumbPos >= 0 ) 893 { 894 size_t nNewFirstVisIndex = static_cast< size_t >( nThumbPos * mnLineSize ); 895 // keep the selection index on same relative position inside row/column 896 size_t nSelectLineOffset = mnSelectIndex % mnLineSize; 897 size_t nNewSelectIndex = mnSelectIndex; 898 if( nNewSelectIndex < nNewFirstVisIndex ) 899 nNewSelectIndex = nNewFirstVisIndex + nSelectLineOffset; 900 else if( nNewSelectIndex >= nNewFirstVisIndex + mnPageSize ) 901 nNewSelectIndex = nNewFirstVisIndex + mnPageSize - mnLineSize + nSelectLineOffset; 902 nNewSelectIndex = ::std::min( nNewSelectIndex, maFields.size() - 1 ); 903 SetSelectionUnchecked( nNewSelectIndex, nNewFirstVisIndex ); 904 } 905 GrabFocus(); 906 return 0; 907 } 908 909 // ============================================================================ 910