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