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
ScPivotWindowField(const ScDPLabelData & rLabelData)59 ScPivotFieldWindow::ScPivotWindowField::ScPivotWindowField( const ScDPLabelData& rLabelData ) :
60 maFuncData( rLabelData.mnCol, rLabelData.mnFuncMask ),
61 maFieldName( rLabelData.getDisplayName() )
62 {
63 }
64
ScPivotWindowField(ScPivotLayoutDlg & rDialog,const ScPivotField & rField,bool bDataWindow)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
ScPivotWindowField(ScPivotLayoutDlg & rDialog,const ScPivotFuncData & rFuncData,bool bDataWindow)71 ScPivotFieldWindow::ScPivotWindowField::ScPivotWindowField( ScPivotLayoutDlg& rDialog, const ScPivotFuncData& rFuncData, bool bDataWindow ) :
72 maFuncData( rFuncData )
73 {
74 InitFieldName( rDialog, bDataWindow );
75 }
76
InitFieldName(ScPivotLayoutDlg & rDialog,bool bDataWindow)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
ScPivotFieldWindow(ScPivotLayoutDlg * pDialog,const ResId & rResId,ScrollBar & rScrollBar,FixedText * pFtCaption,const OUString & rName,ScPivotFieldType eFieldType,const sal_Char * pcHelpId,PointerStyle eDropPointer,size_t nColCount,size_t nRowCount,long nFieldWidthFactor,long nSpaceSize)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
~ScPivotFieldWindow()159 ScPivotFieldWindow::~ScPivotFieldWindow()
160 {
161 ::rtl::Reference< ScAccessibleDataPilotControl > xAcc = GetAccessibleControl();
162 if( xAcc.is() )
163 xAcc->dispose();
164 }
165
ReadDataLabels(const ScDPLabelDataVector & rLabels)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
ReadPivotFields(const ScPivotFieldVector & rPivotFields)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
WriteFieldNames(ScDPNameVec & rFieldNames) const192 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
WritePivotFields(ScPivotFieldVector & rPivotFields) const208 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
GetDescription() const220 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
GetFieldText(size_t nFieldIndex) const233 OUString ScPivotFieldWindow::GetFieldText( size_t nFieldIndex ) const
234 {
235 return (nFieldIndex < maFields.size()) ? maFields[ nFieldIndex ].maFieldName : OUString();
236 }
237
FindFuncDataByCol(SCCOL nCol) const238 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
GetFieldPosition(size_t nFieldIndex) const246 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
GetFieldIndex(const Point & rWindowPos) const254 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
GetDropIndex(const Point & rWindowPos) const277 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
GrabFocusAndSelect(size_t nSelectIndex)299 void ScPivotFieldWindow::GrabFocusAndSelect( size_t nSelectIndex )
300 {
301 if( !HasFocus() ) GrabFocus();
302 MoveSelection( nSelectIndex );
303 }
304
SelectNextField()305 void ScPivotFieldWindow::SelectNextField()
306 {
307 MoveSelection( NEXT_FIELD );
308 }
309
InsertField(size_t nInsertIndex,const ScPivotFuncData & rFuncData)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
RemoveField(size_t nRemoveIndex)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
MoveField(size_t nFieldIndex,size_t nInsertIndex)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
GetSelectedFuncData() const374 const ScPivotFuncData* ScPivotFieldWindow::GetSelectedFuncData() const
375 {
376 return (mnSelectIndex < maFields.size()) ? &maFields[ mnSelectIndex ].maFuncData : 0;
377 }
378
ModifySelectedField(const ScPivotFuncData & rFuncData)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
RemoveSelectedField()389 bool ScPivotFieldWindow::RemoveSelectedField()
390 {
391 return RemoveField( mnSelectIndex );
392 }
393
MoveSelectedField(size_t nInsertIndex)394 bool ScPivotFieldWindow::MoveSelectedField( size_t nInsertIndex )
395 {
396 return MoveField( mnSelectIndex, nInsertIndex );
397 }
398
NotifyStartTracking()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
NotifyTracking(const Point & rWindowPos)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
NotifyEndTracking(ScPivotFieldEndTracking eEndType)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
Paint(const Rectangle &)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
StateChanged(StateChangedType nStateChange)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
DataChanged(const DataChangedEvent & rDCEvt)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
KeyInput(const KeyEvent & rKEvt)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
MouseButtonDown(const MouseEvent & rMEvt)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
RequestHelp(const HelpEvent & rHEvt)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
GetFocus()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
LoseFocus()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
CreateAccessible()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
RecalcVisibleIndex(size_t nSelectIndex) const655 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
SetSelectionUnchecked(size_t nSelectIndex,size_t nFirstVisIndex)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
MoveSelection(size_t nSelectIndex)694 void ScPivotFieldWindow::MoveSelection( size_t nSelectIndex )
695 {
696 if( nSelectIndex < maFields.size() )
697 SetSelectionUnchecked( nSelectIndex, RecalcVisibleIndex( nSelectIndex ) );
698 }
699
MoveSelection(MoveType eMoveType)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
MoveSelectedField(MoveType eMoveType)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
InsertFieldUnchecked(size_t nInsertIndex,const ScPivotWindowField & rField)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
RemoveFieldUnchecked(size_t nRemoveIndex)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
DrawBackground(OutputDevice & rDev)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
DrawField(OutputDevice & rDev,size_t nFieldIndex)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
DrawInsertionCursor(OutputDevice & rDev)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
GetAccessibleControl()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
IMPL_LINK(ScPivotFieldWindow,ScrollHdl,ScrollBar *,pScrollBar)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