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