Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 :
21 : #include "svtools/table/tablecontrol.hxx"
22 : #include "svtools/table/defaultinputhandler.hxx"
23 : #include "svtools/table/tablemodel.hxx"
24 :
25 : #include "tabledatawindow.hxx"
26 : #include "tablecontrol_impl.hxx"
27 : #include "tablegeometry.hxx"
28 :
29 : #include <com/sun/star/accessibility/XAccessible.hpp>
30 : #include <com/sun/star/accessibility/AccessibleTableModelChange.hpp>
31 : #include <com/sun/star/accessibility/AccessibleEventId.hpp>
32 : #include <com/sun/star/accessibility/AccessibleTableModelChangeType.hpp>
33 :
34 : #include <comphelper/flagguard.hxx>
35 : #include <vcl/scrbar.hxx>
36 : #include <vcl/seleng.hxx>
37 : #include <rtl/ref.hxx>
38 : #include <vcl/image.hxx>
39 : #include <tools/diagnose_ex.h>
40 :
41 : #include <functional>
42 : #include <numeric>
43 :
44 : #define MIN_COLUMN_WIDTH_PIXEL 4
45 :
46 : //......................................................................................................................
47 : namespace svt { namespace table
48 : {
49 : //......................................................................................................................
50 :
51 : /** === begin UNO using === **/
52 : using ::com::sun::star::accessibility::AccessibleTableModelChange;
53 : using ::com::sun::star::uno::makeAny;
54 : using ::com::sun::star::uno::Any;
55 : using ::com::sun::star::accessibility::XAccessible;
56 : using ::com::sun::star::uno::Reference;
57 : /** === end UNO using === **/
58 : namespace AccessibleEventId = ::com::sun::star::accessibility::AccessibleEventId;
59 : namespace AccessibleTableModelChangeType = ::com::sun::star::accessibility::AccessibleTableModelChangeType;
60 :
61 : //==================================================================================================================
62 : //= SuppressCursor
63 : //==================================================================================================================
64 : class SuppressCursor
65 : {
66 : private:
67 : ITableControl& m_rTable;
68 :
69 : public:
70 0 : SuppressCursor( ITableControl& _rTable )
71 0 : :m_rTable( _rTable )
72 : {
73 0 : m_rTable.hideCursor();
74 0 : }
75 0 : ~SuppressCursor()
76 : {
77 0 : m_rTable.showCursor();
78 0 : }
79 : };
80 :
81 : //====================================================================
82 : //= EmptyTableModel
83 : //====================================================================
84 : /** default implementation of an ->ITableModel, used as fallback when no
85 : real model is present
86 :
87 : Instances of this class are static in any way, and provide the least
88 : necessary default functionality for a table model.
89 : */
90 0 : class EmptyTableModel : public ITableModel
91 : {
92 : public:
93 0 : EmptyTableModel()
94 0 : {
95 0 : }
96 :
97 : // ITableModel overridables
98 0 : virtual TableSize getColumnCount() const
99 : {
100 0 : return 0;
101 : }
102 0 : virtual TableSize getRowCount() const
103 : {
104 0 : return 0;
105 : }
106 0 : virtual bool hasColumnHeaders() const
107 : {
108 0 : return false;
109 : }
110 0 : virtual bool hasRowHeaders() const
111 : {
112 0 : return false;
113 : }
114 0 : virtual bool isCellEditable( ColPos col, RowPos row ) const
115 : {
116 : (void)col;
117 : (void)row;
118 0 : return false;
119 : }
120 0 : virtual PColumnModel getColumnModel( ColPos column )
121 : {
122 : OSL_FAIL( "EmptyTableModel::getColumnModel: invalid call!" );
123 : (void)column;
124 0 : return PColumnModel();
125 : }
126 0 : virtual PTableRenderer getRenderer() const
127 : {
128 0 : return PTableRenderer();
129 : }
130 0 : virtual PTableInputHandler getInputHandler() const
131 : {
132 0 : return PTableInputHandler();
133 : }
134 0 : virtual TableMetrics getRowHeight() const
135 : {
136 0 : return 5 * 100;
137 : }
138 0 : virtual void setRowHeight(TableMetrics _nRowHeight)
139 : {
140 : (void)_nRowHeight;
141 0 : }
142 0 : virtual TableMetrics getColumnHeaderHeight() const
143 : {
144 0 : return 0;
145 : }
146 0 : virtual TableMetrics getRowHeaderWidth() const
147 : {
148 0 : return 0;
149 : }
150 0 : virtual ScrollbarVisibility getVerticalScrollbarVisibility() const
151 : {
152 0 : return ScrollbarShowNever;
153 : }
154 0 : virtual ScrollbarVisibility getHorizontalScrollbarVisibility() const
155 : {
156 0 : return ScrollbarShowNever;
157 : }
158 0 : virtual void addTableModelListener( const PTableModelListener& i_listener )
159 : {
160 : (void)i_listener;
161 0 : }
162 0 : virtual void removeTableModelListener( const PTableModelListener& i_listener )
163 : {
164 : (void)i_listener;
165 0 : }
166 0 : virtual ::boost::optional< ::Color > getLineColor() const
167 : {
168 0 : return ::boost::optional< ::Color >();
169 : }
170 0 : virtual ::boost::optional< ::Color > getHeaderBackgroundColor() const
171 : {
172 0 : return ::boost::optional< ::Color >();
173 : }
174 0 : virtual ::boost::optional< ::Color > getHeaderTextColor() const
175 : {
176 0 : return ::boost::optional< ::Color >();
177 : }
178 0 : virtual ::boost::optional< ::Color > getActiveSelectionBackColor() const
179 : {
180 0 : return ::boost::optional< ::Color >();
181 : }
182 0 : virtual ::boost::optional< ::Color > getInactiveSelectionBackColor() const
183 : {
184 0 : return ::boost::optional< ::Color >();
185 : }
186 0 : virtual ::boost::optional< ::Color > getActiveSelectionTextColor() const
187 : {
188 0 : return ::boost::optional< ::Color >();
189 : }
190 0 : virtual ::boost::optional< ::Color > getInactiveSelectionTextColor() const
191 : {
192 0 : return ::boost::optional< ::Color >();
193 : }
194 0 : virtual ::boost::optional< ::Color > getTextColor() const
195 : {
196 0 : return ::boost::optional< ::Color >();
197 : }
198 0 : virtual ::boost::optional< ::Color > getTextLineColor() const
199 : {
200 0 : return ::boost::optional< ::Color >();
201 : }
202 0 : virtual ::boost::optional< ::std::vector< ::Color > > getRowBackgroundColors() const
203 : {
204 0 : return ::boost::optional< ::std::vector< ::Color > >();
205 : }
206 0 : virtual ::com::sun::star::style::VerticalAlignment getVerticalAlign() const
207 : {
208 0 : return com::sun::star::style::VerticalAlignment(0);
209 : }
210 0 : virtual ITableDataSort* getSortAdapter()
211 : {
212 0 : return NULL;
213 : }
214 0 : virtual void getCellContent( ColPos const i_col, RowPos const i_row, ::com::sun::star::uno::Any& o_cellContent )
215 : {
216 : (void)i_row;
217 : (void)i_col;
218 0 : o_cellContent.clear();
219 0 : }
220 0 : virtual void getCellToolTip( ColPos const, RowPos const, ::com::sun::star::uno::Any& )
221 : {
222 0 : }
223 0 : virtual Any getRowHeading( RowPos const i_rowPos ) const
224 : {
225 : (void)i_rowPos;
226 0 : return Any();
227 : }
228 : };
229 :
230 :
231 : //====================================================================
232 : //= TableControl_Impl
233 : //====================================================================
234 : DBG_NAME( TableControl_Impl )
235 :
236 : #if DBG_UTIL
237 : //====================================================================
238 : //= SuspendInvariants
239 : //====================================================================
240 : class SuspendInvariants
241 : {
242 : private:
243 : const TableControl_Impl& m_rTable;
244 : sal_Int32 m_nSuspendFlags;
245 :
246 : public:
247 : SuspendInvariants( const TableControl_Impl& _rTable, sal_Int32 _nSuspendFlags )
248 : :m_rTable( _rTable )
249 : ,m_nSuspendFlags( _nSuspendFlags )
250 : {
251 : //DBG_ASSERT( ( m_rTable.m_nRequiredInvariants & m_nSuspendFlags ) == m_nSuspendFlags,
252 : // "SuspendInvariants: cannot suspend what is already suspended!" );
253 : const_cast< TableControl_Impl& >( m_rTable ).m_nRequiredInvariants &= ~m_nSuspendFlags;
254 : }
255 : ~SuspendInvariants()
256 : {
257 : const_cast< TableControl_Impl& >( m_rTable ).m_nRequiredInvariants |= m_nSuspendFlags;
258 : }
259 : };
260 : #define DBG_SUSPEND_INV( flags ) \
261 : SuspendInvariants aSuspendInv( *this, flags );
262 : #else
263 : #define DBG_SUSPEND_INV( flags )
264 : #endif
265 :
266 : #if DBG_UTIL
267 : //====================================================================
268 : const char* TableControl_Impl_checkInvariants( const void* _pInstance )
269 : {
270 : return static_cast< const TableControl_Impl* >( _pInstance )->impl_checkInvariants();
271 : }
272 :
273 : namespace
274 : {
275 : template< typename SCALAR_TYPE >
276 : bool lcl_checkLimitsExclusive( SCALAR_TYPE _nValue, SCALAR_TYPE _nMin, SCALAR_TYPE _nMax )
277 : {
278 : return ( _nValue > _nMin ) && ( _nValue < _nMax );
279 : }
280 :
281 : template< typename SCALAR_TYPE >
282 : bool lcl_checkLimitsExclusive_OrDefault_OrFallback( SCALAR_TYPE _nValue, SCALAR_TYPE _nMin, SCALAR_TYPE _nMax,
283 : PTableModel _pModel, SCALAR_TYPE _nDefaultOrFallback )
284 : {
285 : if ( !_pModel )
286 : return _nValue == _nDefaultOrFallback;
287 : if ( _nMax <= _nMin )
288 : return _nDefaultOrFallback == _nValue;
289 : return lcl_checkLimitsExclusive( _nValue, _nMin, _nMax );
290 : }
291 : }
292 :
293 : //------------------------------------------------------------------------------------------------------------------
294 : const sal_Char* TableControl_Impl::impl_checkInvariants() const
295 : {
296 : if ( !m_pModel )
297 : return "no model, not even an EmptyTableModel";
298 :
299 : if ( !m_pDataWindow )
300 : return "invalid data window!";
301 :
302 : if ( m_pModel->getColumnCount() != m_nColumnCount )
303 : return "column counts are inconsistent!";
304 :
305 : if ( m_pModel->getRowCount() != m_nRowCount )
306 : return "row counts are inconsistent!";
307 :
308 : if ( ( ( m_nCurColumn != COL_INVALID ) && !m_aColumnWidths.empty() && ( m_nCurColumn < 0 ) ) || ( m_nCurColumn >= (ColPos)m_aColumnWidths.size() ) )
309 : return "current column is invalid!";
310 :
311 : if ( !lcl_checkLimitsExclusive_OrDefault_OrFallback( m_nTopRow, (RowPos)-1, m_nRowCount, getModel(), (RowPos)0 ) )
312 : return "invalid top row value!";
313 :
314 : if ( !lcl_checkLimitsExclusive_OrDefault_OrFallback( m_nCurRow, (RowPos)-1, m_nRowCount, getModel(), ROW_INVALID ) )
315 : return "invalid current row value!";
316 :
317 : if ( !lcl_checkLimitsExclusive_OrDefault_OrFallback( m_nLeftColumn, (ColPos)-1, m_nColumnCount, getModel(), (ColPos)0 ) )
318 : return "invalid current column value!";
319 :
320 : if ( !lcl_checkLimitsExclusive_OrDefault_OrFallback( m_nCurColumn, (ColPos)-1, m_nColumnCount, getModel(), COL_INVALID ) )
321 : return "invalid current column value!";
322 :
323 : if ( m_pInputHandler != m_pModel->getInputHandler() )
324 : return "input handler is not the model-provided one!";
325 :
326 : // m_aSelectedRows should have reasonable content
327 : {
328 : if ( m_aSelectedRows.size() > size_t( m_pModel->getRowCount() ) )
329 : return "there are more rows selected than actually exist";
330 : for ( ::std::vector< RowPos >::const_iterator selRow = m_aSelectedRows.begin();
331 : selRow != m_aSelectedRows.end();
332 : ++selRow
333 : )
334 : {
335 : if ( ( *selRow < 0 ) || ( *selRow >= m_pModel->getRowCount() ) )
336 : return "a non-existent row is selected";
337 : }
338 : }
339 :
340 : // m_nColHeaderHeightPixel consistent with the model's value?
341 : {
342 : TableMetrics nHeaderHeight = m_pModel->hasColumnHeaders() ? m_pModel->getColumnHeaderHeight() : 0;
343 : nHeaderHeight = m_rAntiImpl.LogicToPixel( Size( 0, nHeaderHeight ), MAP_APPFONT ).Height();
344 : if ( nHeaderHeight != m_nColHeaderHeightPixel )
345 : return "column header heights are inconsistent!";
346 : }
347 :
348 : bool isDummyModel = dynamic_cast< const EmptyTableModel* >( m_pModel.get() ) != NULL;
349 : if ( !isDummyModel )
350 : {
351 : TableMetrics nRowHeight = m_pModel->getRowHeight();
352 : nRowHeight = m_rAntiImpl.LogicToPixel( Size( 0, nRowHeight ), MAP_APPFONT).Height();
353 : if ( nRowHeight != m_nRowHeightPixel )
354 : return "row heights are inconsistent!";
355 : }
356 :
357 : // m_nRowHeaderWidthPixel consistent with the model's value?
358 : {
359 : TableMetrics nHeaderWidth = m_pModel->hasRowHeaders() ? m_pModel->getRowHeaderWidth() : 0;
360 : nHeaderWidth = m_rAntiImpl.LogicToPixel( Size( nHeaderWidth, 0 ), MAP_APPFONT ).Width();
361 : if ( nHeaderWidth != m_nRowHeaderWidthPixel )
362 : return "row header widths are inconsistent!";
363 : }
364 :
365 : // m_aColumnWidths consistency
366 : if ( size_t( m_nColumnCount ) != m_aColumnWidths.size() )
367 : return "wrong number of cached column widths";
368 :
369 : for ( ColumnPositions::const_iterator col = m_aColumnWidths.begin();
370 : col != m_aColumnWidths.end();
371 : )
372 : {
373 : if ( col->getEnd() < col->getStart() )
374 : return "column widths: 'end' is expected to not be smaller than start";
375 :
376 : ColumnPositions::const_iterator nextCol = col + 1;
377 : if ( nextCol != m_aColumnWidths.end() )
378 : if ( col->getEnd() != nextCol->getStart() )
379 : return "column widths: one column's end should be the next column's start";
380 : col = nextCol;
381 : }
382 :
383 : if ( m_nLeftColumn < m_nColumnCount )
384 : if ( m_aColumnWidths[ m_nLeftColumn ].getStart() != m_nRowHeaderWidthPixel )
385 : return "the left-most column should start immediately after the row header";
386 :
387 : if ( m_nCursorHidden < 0 )
388 : return "invalid hidden count for the cursor!";
389 :
390 : if ( ( m_nRequiredInvariants & INV_SCROLL_POSITION ) && m_pVScroll )
391 : {
392 : DBG_SUSPEND_INV( INV_SCROLL_POSITION );
393 : // prevent infinite recursion
394 :
395 : if ( m_nLeftColumn < 0 )
396 : return "invalid left-most column index";
397 : if ( m_pVScroll->GetThumbPos() != m_nTopRow )
398 : return "vertical scroll bar |position| is incorrect!";
399 : if ( m_pVScroll->GetRange().Max() != m_nRowCount )
400 : return "vertical scroll bar |range| is incorrect!";
401 : if ( m_pVScroll->GetVisibleSize() != impl_getVisibleRows( false ) )
402 : return "vertical scroll bar |visible size| is incorrect!";
403 : }
404 :
405 : if ( ( m_nRequiredInvariants & INV_SCROLL_POSITION ) && m_pHScroll )
406 : {
407 : DBG_SUSPEND_INV( INV_SCROLL_POSITION );
408 : // prevent infinite recursion
409 :
410 : if ( m_pHScroll->GetThumbPos() != m_nLeftColumn )
411 : return "horizontal scroll bar |position| is incorrect!";
412 : if ( m_pHScroll->GetRange().Max() != m_nColumnCount )
413 : return "horizontal scroll bar |range| is incorrect!";
414 : if ( m_pHScroll->GetVisibleSize() != impl_getVisibleColumns( false ) )
415 : return "horizontal scroll bar |visible size| is incorrect!";
416 : }
417 :
418 : return NULL;
419 : }
420 : #endif
421 :
422 : #define DBG_CHECK_ME() \
423 : DBG_CHKTHIS( TableControl_Impl, TableControl_Impl_checkInvariants )
424 :
425 : //------------------------------------------------------------------------------------------------------------------
426 0 : TableControl_Impl::TableControl_Impl( TableControl& _rAntiImpl )
427 : :m_rAntiImpl ( _rAntiImpl )
428 0 : ,m_pModel ( new EmptyTableModel )
429 : ,m_pInputHandler ( )
430 : ,m_nRowHeightPixel ( 15 )
431 : ,m_nColHeaderHeightPixel( 0 )
432 : ,m_nRowHeaderWidthPixel ( 0 )
433 : ,m_nColumnCount ( 0 )
434 : ,m_nRowCount ( 0 )
435 : ,m_bColumnsFit ( true )
436 : ,m_nCurColumn ( COL_INVALID )
437 : ,m_nCurRow ( ROW_INVALID )
438 : ,m_nLeftColumn ( 0 )
439 : ,m_nTopRow ( 0 )
440 : ,m_nCursorHidden ( 1 )
441 0 : ,m_pDataWindow ( new TableDataWindow( *this ) )
442 : ,m_pVScroll ( NULL )
443 : ,m_pHScroll ( NULL )
444 : ,m_pScrollCorner ( NULL )
445 : ,m_pSelEngine ( )
446 : ,m_aSelectedRows ( )
447 0 : ,m_pTableFunctionSet ( new TableFunctionSet( this ) )
448 : ,m_nAnchor ( -1 )
449 : ,m_bUpdatingColWidths ( false )
450 0 : ,m_pAccessibleTable ( NULL )
451 : #if DBG_UTIL
452 : ,m_nRequiredInvariants ( INV_SCROLL_POSITION )
453 : #endif
454 : {
455 : DBG_CTOR( TableControl_Impl, TableControl_Impl_checkInvariants );
456 0 : m_pSelEngine = new SelectionEngine( m_pDataWindow.get(), m_pTableFunctionSet );
457 0 : m_pSelEngine->SetSelectionMode(SINGLE_SELECTION);
458 0 : m_pDataWindow->SetPosPixel( Point( 0, 0 ) );
459 0 : m_pDataWindow->Show();
460 0 : }
461 :
462 : //------------------------------------------------------------------------------------------------------------------
463 0 : TableControl_Impl::~TableControl_Impl()
464 : {
465 : DBG_DTOR( TableControl_Impl, TableControl_Impl_checkInvariants );
466 :
467 0 : DELETEZ( m_pVScroll );
468 0 : DELETEZ( m_pHScroll );
469 0 : DELETEZ( m_pScrollCorner );
470 0 : DELETEZ( m_pTableFunctionSet );
471 0 : DELETEZ( m_pSelEngine );
472 0 : }
473 :
474 : //------------------------------------------------------------------------------------------------------------------
475 0 : void TableControl_Impl::setModel( PTableModel _pModel )
476 : {
477 : DBG_CHECK_ME();
478 :
479 0 : SuppressCursor aHideCursor( *this );
480 :
481 0 : if ( !!m_pModel )
482 0 : m_pModel->removeTableModelListener( shared_from_this() );
483 :
484 0 : m_pModel = _pModel;
485 0 : if ( !m_pModel)
486 0 : m_pModel.reset( new EmptyTableModel );
487 :
488 0 : m_pModel->addTableModelListener( shared_from_this() );
489 :
490 0 : m_nCurRow = ROW_INVALID;
491 0 : m_nCurColumn = COL_INVALID;
492 :
493 : // recalc some model-dependent cached info
494 0 : impl_ni_updateCachedModelValues();
495 0 : impl_ni_relayout();
496 :
497 : // completely invalidate
498 0 : m_rAntiImpl.Invalidate();
499 :
500 : // reset cursor to (0,0)
501 0 : if ( m_nRowCount ) m_nCurRow = 0;
502 0 : if ( m_nColumnCount ) m_nCurColumn = 0;
503 0 : }
504 :
505 : //------------------------------------------------------------------------------------------------------------------
506 : namespace
507 : {
508 0 : bool lcl_adjustSelectedRows( ::std::vector< RowPos >& io_selectionIndexes, RowPos const i_firstAffectedRowIndex, TableSize const i_offset )
509 : {
510 0 : bool didChanges = false;
511 0 : for ( ::std::vector< RowPos >::iterator selPos = io_selectionIndexes.begin();
512 0 : selPos != io_selectionIndexes.end();
513 : ++selPos
514 : )
515 : {
516 0 : if ( *selPos < i_firstAffectedRowIndex )
517 0 : continue;
518 0 : *selPos += i_offset;
519 0 : didChanges = true;
520 : }
521 0 : return didChanges;
522 : }
523 : }
524 :
525 : //------------------------------------------------------------------------------------------------------------------
526 0 : void TableControl_Impl::rowsInserted( RowPos i_first, RowPos i_last )
527 : {
528 : DBG_CHECK_ME();
529 : OSL_PRECOND( i_last >= i_first, "TableControl_Impl::rowsInserted: invalid row indexes!" );
530 :
531 0 : TableSize const insertedRows = i_last - i_first + 1;
532 :
533 : // adjust selection, if necessary
534 0 : bool const selectionChanged = lcl_adjustSelectedRows( m_aSelectedRows, i_first, insertedRows );
535 :
536 : // adjust our cached row count
537 0 : m_nRowCount = m_pModel->getRowCount();
538 :
539 : // if the rows have been inserted before the current row, adjust this
540 0 : if ( i_first <= m_nCurRow )
541 0 : goTo( m_nCurColumn, m_nCurRow + insertedRows );
542 :
543 : // relayout, since the scrollbar need might have changed
544 0 : impl_ni_relayout();
545 :
546 : // notify A1YY events
547 0 : if ( impl_isAccessibleAlive() )
548 : {
549 : impl_commitAccessibleEvent( AccessibleEventId::TABLE_MODEL_CHANGED,
550 0 : makeAny( AccessibleTableModelChange( AccessibleTableModelChangeType::INSERT, i_first, i_last, 0, m_pModel->getColumnCount() ) ),
551 : Any()
552 0 : );
553 : }
554 :
555 : // schedule repaint
556 0 : invalidateRowRange( i_first, ROW_INVALID );
557 :
558 : // call selection handlers, if necessary
559 0 : if ( selectionChanged )
560 0 : m_rAntiImpl.Select();
561 0 : }
562 :
563 : //------------------------------------------------------------------------------------------------------------------
564 0 : void TableControl_Impl::rowsRemoved( RowPos i_first, RowPos i_last )
565 : {
566 0 : sal_Int32 firstRemovedRow = i_first;
567 0 : sal_Int32 lastRemovedRow = i_last;
568 :
569 : // adjust selection, if necessary
570 0 : bool selectionChanged = false;
571 0 : if ( i_first == -1 )
572 : {
573 0 : selectionChanged = markAllRowsAsDeselected();
574 :
575 0 : firstRemovedRow = 0;
576 0 : lastRemovedRow = m_nRowCount - 1;
577 : }
578 : else
579 : {
580 0 : ENSURE_OR_RETURN_VOID( i_last >= i_first, "TableControl_Impl::rowsRemoved: illegal indexes!" );
581 :
582 0 : for ( sal_Int32 row = i_first; row <= i_last; ++row )
583 : {
584 0 : if ( markRowAsDeselected( row ) )
585 0 : selectionChanged = true;
586 : }
587 :
588 0 : if ( lcl_adjustSelectedRows( m_aSelectedRows, i_last + 1, i_first - i_last - 1 ) )
589 0 : selectionChanged = true;
590 : }
591 :
592 : // adjust cached row count
593 0 : m_nRowCount = m_pModel->getRowCount();
594 :
595 : // adjust the current row, if it is larger than the row count now
596 0 : if ( m_nCurRow >= m_nRowCount )
597 : {
598 0 : if ( m_nRowCount > 0 )
599 0 : goTo( m_nCurColumn, m_nRowCount - 1 );
600 : else
601 0 : m_nCurRow = ROW_INVALID;
602 : }
603 :
604 : // relayout, since the scrollbar need might have changed
605 0 : impl_ni_relayout();
606 :
607 : // notify A11Y events
608 0 : if ( impl_isAccessibleAlive() )
609 : {
610 : commitTableEvent(
611 : AccessibleEventId::TABLE_MODEL_CHANGED,
612 : makeAny( AccessibleTableModelChange(
613 : AccessibleTableModelChangeType::DELETE,
614 : firstRemovedRow,
615 : lastRemovedRow,
616 : 0,
617 0 : m_pModel->getColumnCount()
618 : ) ),
619 : Any()
620 0 : );
621 : }
622 :
623 : // schedule a repaint
624 0 : invalidateRowRange( firstRemovedRow, ROW_INVALID );
625 :
626 : // call selection handlers, if necessary
627 0 : if ( selectionChanged )
628 0 : m_rAntiImpl.Select();
629 : }
630 :
631 : //------------------------------------------------------------------------------------------------------------------
632 0 : void TableControl_Impl::columnInserted( ColPos const i_colIndex )
633 : {
634 0 : m_nColumnCount = m_pModel->getColumnCount();
635 0 : impl_ni_relayout();
636 :
637 0 : m_rAntiImpl.Invalidate();
638 :
639 : OSL_UNUSED( i_colIndex );
640 0 : }
641 :
642 : //------------------------------------------------------------------------------------------------------------------
643 0 : void TableControl_Impl::columnRemoved( ColPos const i_colIndex )
644 : {
645 0 : m_nColumnCount = m_pModel->getColumnCount();
646 :
647 : // adjust the current column, if it is larger than the column count now
648 0 : if ( m_nCurColumn >= m_nColumnCount )
649 : {
650 0 : if ( m_nColumnCount > 0 )
651 0 : goTo( m_nCurColumn - 1, m_nCurRow );
652 : else
653 0 : m_nCurColumn = COL_INVALID;
654 : }
655 :
656 0 : impl_ni_relayout();
657 :
658 0 : m_rAntiImpl.Invalidate();
659 :
660 : OSL_UNUSED( i_colIndex );
661 0 : }
662 :
663 : //------------------------------------------------------------------------------------------------------------------
664 0 : void TableControl_Impl::allColumnsRemoved()
665 : {
666 0 : m_nColumnCount = m_pModel->getColumnCount();
667 0 : impl_ni_relayout();
668 :
669 0 : m_rAntiImpl.Invalidate();
670 0 : }
671 :
672 : //------------------------------------------------------------------------------------------------------------------
673 0 : void TableControl_Impl::cellsUpdated( ColPos const i_firstCol, ColPos i_lastCol, RowPos const i_firstRow, RowPos const i_lastRow )
674 : {
675 0 : invalidateRowRange( i_firstRow, i_lastRow );
676 :
677 : OSL_UNUSED( i_firstCol );
678 : OSL_UNUSED( i_lastCol );
679 0 : }
680 :
681 : //------------------------------------------------------------------------------------------------------------------
682 0 : void TableControl_Impl::tableMetricsChanged()
683 : {
684 0 : impl_ni_updateCachedTableMetrics();
685 0 : impl_ni_relayout();
686 0 : m_rAntiImpl.Invalidate();
687 0 : }
688 :
689 : //------------------------------------------------------------------------------------------------------------------
690 0 : void TableControl_Impl::impl_invalidateColumn( ColPos const i_column )
691 : {
692 : DBG_CHECK_ME();
693 :
694 0 : Rectangle const aAllCellsArea( impl_getAllVisibleCellsArea() );
695 :
696 0 : const TableColumnGeometry aColumn( *this, aAllCellsArea, i_column );
697 0 : if ( aColumn.isValid() )
698 0 : m_rAntiImpl.Invalidate( aColumn.getRect() );
699 0 : }
700 :
701 : //------------------------------------------------------------------------------------------------------------------
702 0 : void TableControl_Impl::columnChanged( ColPos const i_column, ColumnAttributeGroup const i_attributeGroup )
703 : {
704 0 : ColumnAttributeGroup nGroup( i_attributeGroup );
705 0 : if ( nGroup & COL_ATTRS_APPEARANCE )
706 : {
707 0 : impl_invalidateColumn( i_column );
708 0 : nGroup &= ~COL_ATTRS_APPEARANCE;
709 : }
710 :
711 0 : if ( nGroup & COL_ATTRS_WIDTH )
712 : {
713 0 : if ( !m_bUpdatingColWidths )
714 : {
715 0 : impl_ni_relayout( i_column );
716 0 : invalidate( TableAreaAll );
717 : }
718 :
719 0 : nGroup &= ~COL_ATTRS_WIDTH;
720 : }
721 :
722 : OSL_ENSURE( ( nGroup == COL_ATTRS_NONE ) || ( i_attributeGroup == COL_ATTRS_ALL ),
723 : "TableControl_Impl::columnChanged: don't know how to handle this change!" );
724 0 : }
725 :
726 : //------------------------------------------------------------------------------------------------------------------
727 0 : Rectangle TableControl_Impl::impl_getAllVisibleCellsArea() const
728 : {
729 : DBG_CHECK_ME();
730 :
731 0 : Rectangle aArea( Point( 0, 0 ), Size( 0, 0 ) );
732 :
733 : // determine the right-most border of the last column which is
734 : // at least partially visible
735 0 : aArea.Right() = m_nRowHeaderWidthPixel;
736 0 : if ( !m_aColumnWidths.empty() )
737 : {
738 : // the number of pixels which are scrolled out of the left hand
739 : // side of the window
740 0 : const long nScrolledOutLeft = m_nLeftColumn == 0 ? 0 : m_aColumnWidths[ m_nLeftColumn - 1 ].getEnd();
741 :
742 0 : ColumnPositions::const_reverse_iterator loop = m_aColumnWidths.rbegin();
743 0 : do
744 : {
745 0 : aArea.Right() = loop->getEnd() - nScrolledOutLeft + m_nRowHeaderWidthPixel;
746 0 : ++loop;
747 : }
748 0 : while ( ( loop != m_aColumnWidths.rend() )
749 0 : && ( loop->getEnd() - nScrolledOutLeft >= aArea.Right() )
750 : );
751 : }
752 : // so far, aArea.Right() denotes the first pixel *after* the cell area
753 0 : --aArea.Right();
754 :
755 : // determine the last row which is at least partially visible
756 0 : aArea.Bottom() =
757 : m_nColHeaderHeightPixel
758 0 : + impl_getVisibleRows( true ) * m_nRowHeightPixel
759 0 : - 1;
760 :
761 0 : return aArea;
762 : }
763 :
764 : //------------------------------------------------------------------------------------------------------------------
765 0 : Rectangle TableControl_Impl::impl_getAllVisibleDataCellArea() const
766 : {
767 : DBG_CHECK_ME();
768 :
769 0 : Rectangle aArea( impl_getAllVisibleCellsArea() );
770 0 : aArea.Left() = m_nRowHeaderWidthPixel;
771 0 : aArea.Top() = m_nColHeaderHeightPixel;
772 0 : return aArea;
773 : }
774 :
775 : //------------------------------------------------------------------------------------------------------------------
776 0 : void TableControl_Impl::impl_ni_updateCachedTableMetrics()
777 : {
778 0 : m_nRowHeightPixel = m_rAntiImpl.LogicToPixel( Size( 0, m_pModel->getRowHeight() ), MAP_APPFONT ).Height();
779 :
780 0 : m_nColHeaderHeightPixel = 0;
781 0 : if ( m_pModel->hasColumnHeaders() )
782 0 : m_nColHeaderHeightPixel = m_rAntiImpl.LogicToPixel( Size( 0, m_pModel->getColumnHeaderHeight() ), MAP_APPFONT ).Height();
783 :
784 0 : m_nRowHeaderWidthPixel = 0;
785 0 : if ( m_pModel->hasRowHeaders() )
786 0 : m_nRowHeaderWidthPixel = m_rAntiImpl.LogicToPixel( Size( m_pModel->getRowHeaderWidth(), 0 ), MAP_APPFONT).Width();
787 0 : }
788 :
789 : //------------------------------------------------------------------------------------------------------------------
790 0 : void TableControl_Impl::impl_ni_updateCachedModelValues()
791 : {
792 0 : m_pInputHandler = m_pModel->getInputHandler();
793 0 : if ( !m_pInputHandler )
794 0 : m_pInputHandler.reset( new DefaultInputHandler );
795 :
796 0 : m_nColumnCount = m_pModel->getColumnCount();
797 0 : if ( m_nLeftColumn >= m_nColumnCount )
798 0 : m_nLeftColumn = ( m_nColumnCount > 0 ) ? m_nColumnCount - 1 : 0;
799 :
800 0 : m_nRowCount = m_pModel->getRowCount();
801 0 : if ( m_nTopRow >= m_nRowCount )
802 0 : m_nTopRow = ( m_nRowCount > 0 ) ? m_nRowCount - 1 : 0;
803 :
804 0 : impl_ni_updateCachedTableMetrics();
805 0 : }
806 :
807 : //------------------------------------------------------------------------------------------------------------------
808 : namespace
809 : {
810 : //..............................................................................................................
811 : /// determines whether a scrollbar is needed for the given values
812 0 : bool lcl_determineScrollbarNeed( long const i_position, ScrollbarVisibility const i_visibility,
813 : long const i_availableSpace, long const i_neededSpace )
814 : {
815 0 : if ( i_visibility == ScrollbarShowNever )
816 0 : return false;
817 0 : if ( i_visibility == ScrollbarShowAlways )
818 0 : return true;
819 0 : if ( i_position > 0 )
820 0 : return true;
821 0 : if ( i_availableSpace >= i_neededSpace )
822 0 : return false;
823 0 : return true;
824 : }
825 :
826 : //..............................................................................................................
827 0 : void lcl_setButtonRepeat( Window& _rWindow, sal_uLong _nDelay )
828 : {
829 0 : AllSettings aSettings = _rWindow.GetSettings();
830 0 : MouseSettings aMouseSettings = aSettings.GetMouseSettings();
831 :
832 0 : aMouseSettings.SetButtonRepeat( _nDelay );
833 0 : aSettings.SetMouseSettings( aMouseSettings );
834 :
835 0 : _rWindow.SetSettings( aSettings, sal_True );
836 0 : }
837 :
838 : //..............................................................................................................
839 0 : bool lcl_updateScrollbar( Window& _rParent, ScrollBar*& _rpBar,
840 : bool const i_needBar, long _nVisibleUnits,
841 : long _nPosition, long _nLineSize, long _nRange,
842 : bool _bHorizontal, const Link& _rScrollHandler )
843 : {
844 : // do we currently have the scrollbar?
845 0 : bool bHaveBar = _rpBar != NULL;
846 :
847 : // do we need to correct the scrollbar visibility?
848 0 : if ( bHaveBar && !i_needBar )
849 : {
850 0 : if ( _rpBar->IsTracking() )
851 0 : _rpBar->EndTracking();
852 0 : DELETEZ( _rpBar );
853 : }
854 0 : else if ( !bHaveBar && i_needBar )
855 : {
856 : _rpBar = new ScrollBar(
857 : &_rParent,
858 : WB_DRAG | ( _bHorizontal ? WB_HSCROLL : WB_VSCROLL )
859 0 : );
860 0 : _rpBar->SetScrollHdl( _rScrollHandler );
861 : // get some speed into the scrolling ....
862 0 : lcl_setButtonRepeat( *_rpBar, 0 );
863 : }
864 :
865 0 : if ( _rpBar )
866 : {
867 0 : _rpBar->SetRange( Range( 0, _nRange ) );
868 0 : _rpBar->SetVisibleSize( _nVisibleUnits );
869 0 : _rpBar->SetPageSize( _nVisibleUnits );
870 0 : _rpBar->SetLineSize( _nLineSize );
871 0 : _rpBar->SetThumbPos( _nPosition );
872 0 : _rpBar->Show();
873 : }
874 :
875 0 : return ( bHaveBar != i_needBar );
876 : }
877 :
878 : //..............................................................................................................
879 : /** returns the number of rows fitting into the given range,
880 : for the given row height. Partially fitting rows are counted, too, if the
881 : respective parameter says so.
882 : */
883 0 : TableSize lcl_getRowsFittingInto( long _nOverallHeight, long _nRowHeightPixel, bool _bAcceptPartialRow = false )
884 : {
885 : return _bAcceptPartialRow
886 : ? ( _nOverallHeight + ( _nRowHeightPixel - 1 ) ) / _nRowHeightPixel
887 0 : : _nOverallHeight / _nRowHeightPixel;
888 : }
889 :
890 : //..............................................................................................................
891 : /** returns the number of columns fitting into the given area,
892 : with the first visible column as given. Partially fitting columns are counted, too,
893 : if the respective parameter says so.
894 : */
895 0 : TableSize lcl_getColumnsVisibleWithin( const Rectangle& _rArea, ColPos _nFirstVisibleColumn,
896 : const TableControl_Impl& _rControl, bool _bAcceptPartialRow )
897 : {
898 0 : TableSize visibleColumns = 0;
899 0 : TableColumnGeometry aColumn( _rControl, _rArea, _nFirstVisibleColumn );
900 0 : while ( aColumn.isValid() )
901 : {
902 0 : if ( !_bAcceptPartialRow )
903 0 : if ( aColumn.getRect().Right() > _rArea.Right() )
904 : // this column is only partially visible, and this is not allowed
905 0 : break;
906 :
907 0 : aColumn.moveRight();
908 0 : ++visibleColumns;
909 : }
910 0 : return visibleColumns;
911 : }
912 :
913 : }
914 :
915 : //------------------------------------------------------------------------------------------------------------------
916 0 : long TableControl_Impl::impl_ni_calculateColumnWidths( ColPos const i_assumeInflexibleColumnsUpToIncluding,
917 : bool const i_assumeVerticalScrollbar, ::std::vector< long >& o_newColWidthsPixel ) const
918 : {
919 : // the available horizontal space
920 0 : long gridWidthPixel = m_rAntiImpl.GetOutputSizePixel().Width();
921 0 : ENSURE_OR_RETURN( !!m_pModel, "TableControl_Impl::impl_ni_calculateColumnWidths: not allowed without a model!", gridWidthPixel );
922 0 : if ( m_pModel->hasRowHeaders() && ( gridWidthPixel != 0 ) )
923 : {
924 0 : gridWidthPixel -= m_nRowHeaderWidthPixel;
925 : }
926 :
927 0 : if ( i_assumeVerticalScrollbar && ( m_pModel->getVerticalScrollbarVisibility() != ScrollbarShowNever ) )
928 : {
929 0 : long nScrollbarMetrics = m_rAntiImpl.GetSettings().GetStyleSettings().GetScrollBarSize();
930 0 : gridWidthPixel -= nScrollbarMetrics;
931 : }
932 :
933 : // no need to do anything without columns
934 0 : TableSize const colCount = m_pModel->getColumnCount();
935 0 : if ( colCount == 0 )
936 0 : return gridWidthPixel;
937 :
938 : // collect some meta data for our columns:
939 : // - their current (pixel) metrics
940 0 : long accumulatedCurrentWidth = 0;
941 0 : ::std::vector< long > currentColWidths;
942 0 : currentColWidths.reserve( colCount );
943 : typedef ::std::vector< ::std::pair< long, long > > ColumnLimits;
944 0 : ColumnLimits effectiveColumnLimits;
945 0 : effectiveColumnLimits.reserve( colCount );
946 0 : long accumulatedMinWidth = 0;
947 0 : long accumulatedMaxWidth = 0;
948 : // - their relative flexibility
949 0 : ::std::vector< ::sal_Int32 > columnFlexibilities;
950 0 : columnFlexibilities.reserve( colCount );
951 0 : long flexibilityDenominator = 0;
952 0 : size_t flexibleColumnCount = 0;
953 0 : for ( ColPos col = 0; col < colCount; ++col )
954 : {
955 0 : PColumnModel const pColumn = m_pModel->getColumnModel( col );
956 0 : ENSURE_OR_THROW( !!pColumn, "invalid column returned by the model!" );
957 :
958 : // current width
959 0 : long const currentWidth = appFontWidthToPixel( pColumn->getWidth() );
960 0 : currentColWidths.push_back( currentWidth );
961 :
962 : // accumulated width
963 0 : accumulatedCurrentWidth += currentWidth;
964 :
965 : // flexibility
966 0 : ::sal_Int32 flexibility = pColumn->getFlexibility();
967 : OSL_ENSURE( flexibility >= 0, "TableControl_Impl::impl_ni_calculateColumnWidths: a column's flexibility should be non-negative." );
968 0 : if ( ( flexibility < 0 ) // normalization
969 0 : || ( !pColumn->isResizable() ) // column not resizeable => no auto-resize
970 : || ( col <= i_assumeInflexibleColumnsUpToIncluding ) // column shall be treated as inflexible => respec this
971 : )
972 0 : flexibility = 0;
973 :
974 : // min/max width
975 0 : long effectiveMin = currentWidth, effectiveMax = currentWidth;
976 : // if the column is not flexible, it will not be asked for min/max, but we assume the current width as limit then
977 0 : if ( flexibility > 0 )
978 : {
979 0 : long const minWidth = appFontWidthToPixel( pColumn->getMinWidth() );
980 0 : if ( minWidth > 0 )
981 0 : effectiveMin = minWidth;
982 : else
983 0 : effectiveMin = MIN_COLUMN_WIDTH_PIXEL;
984 :
985 0 : long const maxWidth = appFontWidthToPixel( pColumn->getMaxWidth() );
986 : OSL_ENSURE( minWidth <= maxWidth, "TableControl_Impl::impl_ni_calculateColumnWidths: pretty undecided 'bout its width limits, this column!" );
987 0 : if ( ( maxWidth > 0 ) && ( maxWidth >= minWidth ) )
988 0 : effectiveMax = maxWidth;
989 : else
990 0 : effectiveMax = gridWidthPixel; // TODO: any better guess here?
991 :
992 0 : if ( effectiveMin == effectiveMax )
993 : // if the min and the max are identical, this implies no flexibility at all
994 0 : flexibility = 0;
995 : }
996 :
997 0 : columnFlexibilities.push_back( flexibility );
998 0 : flexibilityDenominator += flexibility;
999 0 : if ( flexibility > 0 )
1000 0 : ++flexibleColumnCount;
1001 :
1002 0 : effectiveColumnLimits.push_back( ::std::pair< long, long >( effectiveMin, effectiveMax ) );
1003 0 : accumulatedMinWidth += effectiveMin;
1004 0 : accumulatedMaxWidth += effectiveMax;
1005 0 : }
1006 :
1007 0 : o_newColWidthsPixel = currentColWidths;
1008 0 : if ( flexibilityDenominator == 0 )
1009 : {
1010 : // no column is flexible => don't adjust anything
1011 : }
1012 0 : else if ( gridWidthPixel > accumulatedCurrentWidth )
1013 : { // we have space to give away ...
1014 0 : long distributePixel = gridWidthPixel - accumulatedCurrentWidth;
1015 0 : if ( gridWidthPixel > accumulatedMaxWidth )
1016 : {
1017 : // ... but the column's maximal widths are still less than we have
1018 : // => set them all to max
1019 0 : for ( size_t i = 0; i < size_t( colCount ); ++i )
1020 : {
1021 0 : o_newColWidthsPixel[i] = effectiveColumnLimits[i].second;
1022 : }
1023 : }
1024 : else
1025 : {
1026 0 : bool startOver = false;
1027 0 : do
1028 : {
1029 0 : startOver = false;
1030 : // distribute the remaining space amongst all columns with a positive flexibility
1031 0 : for ( size_t i=0; i<o_newColWidthsPixel.size() && !startOver; ++i )
1032 : {
1033 0 : long const columnFlexibility = columnFlexibilities[i];
1034 0 : if ( columnFlexibility == 0 )
1035 0 : continue;
1036 :
1037 0 : long newColWidth = currentColWidths[i] + columnFlexibility * distributePixel / flexibilityDenominator;
1038 :
1039 0 : if ( newColWidth > effectiveColumnLimits[i].second )
1040 : { // that was too much, we hit the col's maximum
1041 : // set the new width to exactly this maximum
1042 0 : newColWidth = effectiveColumnLimits[i].second;
1043 : // adjust the flexibility denominator ...
1044 0 : flexibilityDenominator -= columnFlexibility;
1045 0 : columnFlexibilities[i] = 0;
1046 0 : --flexibleColumnCount;
1047 : // ... and the remaining width ...
1048 0 : long const difference = newColWidth - currentColWidths[i];
1049 0 : distributePixel -= difference;
1050 : // ... this way, we ensure that the width not taken up by this column is consumed by the other
1051 : // flexible ones (if there are some)
1052 :
1053 : // and start over with the first column, since there might be earlier columns which need
1054 : // to be recalculated now
1055 0 : startOver = true;
1056 : }
1057 :
1058 0 : o_newColWidthsPixel[i] = newColWidth;
1059 : }
1060 : }
1061 : while ( startOver );
1062 :
1063 : // are there pixels left (might be caused by rounding errors)?
1064 0 : distributePixel = gridWidthPixel - ::std::accumulate( o_newColWidthsPixel.begin(), o_newColWidthsPixel.end(), 0 );
1065 0 : while ( ( distributePixel > 0 ) && ( flexibleColumnCount > 0 ) )
1066 : {
1067 : // yes => ignore relative flexibilities, and subsequently distribute single pixels to all flexible
1068 : // columns which did not yet reach their maximum.
1069 0 : for ( size_t i=0; ( i < o_newColWidthsPixel.size() ) && ( distributePixel > 0 ); ++i )
1070 : {
1071 0 : if ( columnFlexibilities[i] == 0 )
1072 0 : continue;
1073 :
1074 : OSL_ENSURE( o_newColWidthsPixel[i] <= effectiveColumnLimits[i].second,
1075 : "TableControl_Impl::impl_ni_calculateColumnWidths: inconsitency!" );
1076 0 : if ( o_newColWidthsPixel[i] >= effectiveColumnLimits[i].first )
1077 : {
1078 0 : columnFlexibilities[i] = 0;
1079 0 : --flexibleColumnCount;
1080 0 : continue;
1081 : }
1082 :
1083 0 : ++o_newColWidthsPixel[i];
1084 0 : --distributePixel;
1085 : }
1086 : }
1087 : }
1088 : }
1089 0 : else if ( gridWidthPixel < accumulatedCurrentWidth )
1090 : { // we need to take away some space from the columns which allow it ...
1091 0 : long takeAwayPixel = accumulatedCurrentWidth - gridWidthPixel;
1092 0 : if ( gridWidthPixel < accumulatedMinWidth )
1093 : {
1094 : // ... but the column's minimal widths are still more than we have
1095 : // => set them all to min
1096 0 : for ( size_t i = 0; i < size_t( colCount ); ++i )
1097 : {
1098 0 : o_newColWidthsPixel[i] = effectiveColumnLimits[i].first;
1099 : }
1100 : }
1101 : else
1102 : {
1103 0 : bool startOver = false;
1104 0 : do
1105 : {
1106 0 : startOver = false;
1107 : // take away the space we need from the columns with a positive flexibility
1108 0 : for ( size_t i=0; i<o_newColWidthsPixel.size() && !startOver; ++i )
1109 : {
1110 0 : long const columnFlexibility = columnFlexibilities[i];
1111 0 : if ( columnFlexibility == 0 )
1112 0 : continue;
1113 :
1114 0 : long newColWidth = currentColWidths[i] - columnFlexibility * takeAwayPixel / flexibilityDenominator;
1115 :
1116 0 : if ( newColWidth < effectiveColumnLimits[i].first )
1117 : { // that was too much, we hit the col's minimum
1118 : // set the new width to exactly this minimum
1119 0 : newColWidth = effectiveColumnLimits[i].first;
1120 : // adjust the flexibility denominator ...
1121 0 : flexibilityDenominator -= columnFlexibility;
1122 0 : columnFlexibilities[i] = 0;
1123 0 : --flexibleColumnCount;
1124 : // ... and the remaining width ...
1125 0 : long const difference = currentColWidths[i] - newColWidth;
1126 0 : takeAwayPixel -= difference;
1127 :
1128 : // and start over with the first column, since there might be earlier columns which need
1129 : // to be recalculated now
1130 0 : startOver = true;
1131 : }
1132 :
1133 0 : o_newColWidthsPixel[i] = newColWidth;
1134 : }
1135 : }
1136 : while ( startOver );
1137 :
1138 : // are there pixels left (might be caused by rounding errors)?
1139 0 : takeAwayPixel = ::std::accumulate( o_newColWidthsPixel.begin(), o_newColWidthsPixel.end(), 0 ) - gridWidthPixel;
1140 0 : while ( ( takeAwayPixel > 0 ) && ( flexibleColumnCount > 0 ) )
1141 : {
1142 : // yes => ignore relative flexibilities, and subsequently take away pixels from all flexible
1143 : // columns which did not yet reach their minimum.
1144 0 : for ( size_t i=0; ( i < o_newColWidthsPixel.size() ) && ( takeAwayPixel > 0 ); ++i )
1145 : {
1146 0 : if ( columnFlexibilities[i] == 0 )
1147 0 : continue;
1148 :
1149 : OSL_ENSURE( o_newColWidthsPixel[i] >= effectiveColumnLimits[i].first,
1150 : "TableControl_Impl::impl_ni_calculateColumnWidths: inconsitency!" );
1151 0 : if ( o_newColWidthsPixel[i] <= effectiveColumnLimits[i].first )
1152 : {
1153 0 : columnFlexibilities[i] = 0;
1154 0 : --flexibleColumnCount;
1155 0 : continue;
1156 : }
1157 :
1158 0 : --o_newColWidthsPixel[i];
1159 0 : --takeAwayPixel;
1160 : }
1161 : }
1162 : }
1163 : }
1164 :
1165 0 : return gridWidthPixel;
1166 : }
1167 :
1168 : //------------------------------------------------------------------------------------------------------------------
1169 0 : void TableControl_Impl::impl_ni_relayout( ColPos const i_assumeInflexibleColumnsUpToIncluding )
1170 : {
1171 0 : ENSURE_OR_RETURN_VOID( !m_bUpdatingColWidths, "TableControl_Impl::impl_ni_relayout: recursive call detected!" );
1172 :
1173 0 : m_aColumnWidths.resize( 0 );
1174 0 : if ( !m_pModel )
1175 : return;
1176 :
1177 0 : ::comphelper::FlagRestorationGuard const aWidthUpdateFlag( m_bUpdatingColWidths, true );
1178 0 : SuppressCursor aHideCursor( *this );
1179 :
1180 : // layouting steps:
1181 : //
1182 : // 1. adjust column widths, leaving space for a vertical scrollbar
1183 : // 2. determine need for a vertical scrollbar
1184 : // - V-YES: all fine, result from 1. is still valid
1185 : // - V-NO: result from 1. is still under consideration
1186 : //
1187 : // 3. determine need for a horizontal scrollbar
1188 : // - H-NO: all fine, result from 2. is still valid
1189 : // - H-YES: reconsider need for a vertical scrollbar, if result of 2. was V-NO
1190 : // - V-YES: all fine, result from 1. is still valid
1191 : // - V-NO: redistribute the remaining space (if any) amongst all columns which allow it
1192 :
1193 0 : ::std::vector< long > newWidthsPixel;
1194 0 : long gridWidthPixel = impl_ni_calculateColumnWidths( i_assumeInflexibleColumnsUpToIncluding, true, newWidthsPixel );
1195 :
1196 : // the width/height of a scrollbar, needed several times below
1197 0 : long const nScrollbarMetrics = m_rAntiImpl.GetSettings().GetStyleSettings().GetScrollBarSize();
1198 :
1199 : // determine the playground for the data cells (excluding headers)
1200 : // TODO: what if the control is smaller than needed for the headers/scrollbars?
1201 0 : Rectangle aDataCellPlayground( Point( 0, 0 ), m_rAntiImpl.GetOutputSizePixel() );
1202 0 : aDataCellPlayground.Left() = m_nRowHeaderWidthPixel;
1203 0 : aDataCellPlayground.Top() = m_nColHeaderHeightPixel;
1204 :
1205 : OSL_ENSURE( ( m_nRowCount == m_pModel->getRowCount() ) && ( m_nColumnCount == m_pModel->getColumnCount() ),
1206 : "TableControl_Impl::impl_ni_relayout: how is this expected to work with invalid data?" );
1207 0 : long const nAllColumnsWidth = ::std::accumulate( newWidthsPixel.begin(), newWidthsPixel.end(), 0 );
1208 :
1209 0 : ScrollbarVisibility const eVertScrollbar = m_pModel->getVerticalScrollbarVisibility();
1210 0 : ScrollbarVisibility const eHorzScrollbar = m_pModel->getHorizontalScrollbarVisibility();
1211 :
1212 : // do we need a vertical scrollbar?
1213 : bool bNeedVerticalScrollbar = lcl_determineScrollbarNeed(
1214 0 : m_nTopRow, eVertScrollbar, aDataCellPlayground.GetHeight(), m_nRowHeightPixel * m_nRowCount );
1215 0 : bool bFirstRoundVScrollNeed = false;
1216 0 : if ( bNeedVerticalScrollbar )
1217 : {
1218 0 : aDataCellPlayground.Right() -= nScrollbarMetrics;
1219 0 : bFirstRoundVScrollNeed = true;
1220 : }
1221 :
1222 : // do we need a horizontal scrollbar?
1223 : bool const bNeedHorizontalScrollbar = lcl_determineScrollbarNeed(
1224 0 : m_nLeftColumn, eHorzScrollbar, aDataCellPlayground.GetWidth(), nAllColumnsWidth );
1225 0 : if ( bNeedHorizontalScrollbar )
1226 : {
1227 0 : aDataCellPlayground.Bottom() -= nScrollbarMetrics;
1228 :
1229 : // now that we just found that we need a horizontal scrollbar,
1230 : // the need for a vertical one may have changed, since the horizontal
1231 : // SB might just occupy enough space so that not all rows do fit
1232 : // anymore
1233 0 : if ( !bFirstRoundVScrollNeed )
1234 : {
1235 : bNeedVerticalScrollbar = lcl_determineScrollbarNeed(
1236 0 : m_nTopRow, eVertScrollbar, aDataCellPlayground.GetHeight(), m_nRowHeightPixel * m_nRowCount );
1237 0 : if ( bNeedVerticalScrollbar )
1238 : {
1239 0 : aDataCellPlayground.Right() -= nScrollbarMetrics;
1240 : }
1241 : }
1242 : }
1243 :
1244 : // the initial call to impl_ni_calculateColumnWidths assumed that we need a vertical scrollbar. If, by now,
1245 : // we know that this is not the case, re-calculate the column widths.
1246 0 : if ( !bNeedVerticalScrollbar )
1247 0 : gridWidthPixel = impl_ni_calculateColumnWidths( i_assumeInflexibleColumnsUpToIncluding, false, newWidthsPixel );
1248 :
1249 : // update the column objects with the new widths we finally calculated
1250 0 : TableSize const colCount = m_pModel->getColumnCount();
1251 0 : m_aColumnWidths.reserve( colCount );
1252 0 : long accumulatedWidthPixel = m_nRowHeaderWidthPixel;
1253 0 : bool anyColumnWidthChanged = false;
1254 0 : for ( ColPos col = 0; col < colCount; ++col )
1255 : {
1256 0 : const long columnStart = accumulatedWidthPixel;
1257 0 : const long columnEnd = columnStart + newWidthsPixel[col];
1258 0 : m_aColumnWidths.push_back( MutableColumnMetrics( columnStart, columnEnd ) );
1259 0 : accumulatedWidthPixel = columnEnd;
1260 :
1261 : // and don't forget to forward this to the column models
1262 0 : PColumnModel const pColumn = m_pModel->getColumnModel( col );
1263 0 : ENSURE_OR_THROW( !!pColumn, "invalid column returned by the model!" );
1264 :
1265 0 : long const oldColumnWidthAppFont = pColumn->getWidth();
1266 0 : long const newColumnWidthAppFont = pixelWidthToAppFont( newWidthsPixel[col] );
1267 0 : pColumn->setWidth( newColumnWidthAppFont );
1268 :
1269 0 : anyColumnWidthChanged |= ( oldColumnWidthAppFont != newColumnWidthAppFont );
1270 0 : }
1271 :
1272 : // if the column widths changed, ensure everything is repainted
1273 0 : if ( anyColumnWidthChanged )
1274 0 : invalidate( TableAreaAll );
1275 :
1276 : // if the column resizing happened to leave some space at the right, but there are columns
1277 : // scrolled out to the left, scroll them in
1278 0 : while ( ( m_nLeftColumn > 0 )
1279 0 : && ( accumulatedWidthPixel - m_aColumnWidths[ m_nLeftColumn - 1 ].getStart() <= gridWidthPixel )
1280 : )
1281 : {
1282 0 : --m_nLeftColumn;
1283 : }
1284 :
1285 : // now adjust the column metrics, since they currently ignore the horizontal scroll position
1286 0 : if ( m_nLeftColumn > 0 )
1287 : {
1288 0 : const long offsetPixel = m_aColumnWidths[ 0 ].getStart() - m_aColumnWidths[ m_nLeftColumn ].getStart();
1289 0 : for ( ColumnPositions::iterator colPos = m_aColumnWidths.begin();
1290 0 : colPos != m_aColumnWidths.end();
1291 : ++colPos
1292 : )
1293 : {
1294 0 : colPos->move( offsetPixel );
1295 : }
1296 : }
1297 :
1298 : // show or hide the scrollbars as needed, and position the data window
1299 0 : impl_ni_positionChildWindows( aDataCellPlayground, bNeedVerticalScrollbar, bNeedHorizontalScrollbar );
1300 : }
1301 :
1302 : //------------------------------------------------------------------------------------------------------------------
1303 0 : void TableControl_Impl::impl_ni_positionChildWindows( Rectangle const & i_dataCellPlayground,
1304 : bool const i_verticalScrollbar, bool const i_horizontalScrollbar )
1305 : {
1306 0 : long const nScrollbarMetrics = m_rAntiImpl.GetSettings().GetStyleSettings().GetScrollBarSize();
1307 :
1308 : // create or destroy the vertical scrollbar, as needed
1309 : lcl_updateScrollbar(
1310 : m_rAntiImpl,
1311 : m_pVScroll,
1312 : i_verticalScrollbar,
1313 : lcl_getRowsFittingInto( i_dataCellPlayground.GetHeight(), m_nRowHeightPixel ),
1314 : // visible units
1315 : m_nTopRow, // current position
1316 : 1, // line size
1317 : m_nRowCount, // range
1318 : false, // vertical
1319 : LINK( this, TableControl_Impl, OnScroll ) // scroll handler
1320 0 : );
1321 :
1322 : // position it
1323 0 : if ( m_pVScroll )
1324 : {
1325 : Rectangle aScrollbarArea(
1326 0 : Point( i_dataCellPlayground.Right() + 1, 0 ),
1327 0 : Size( nScrollbarMetrics, i_dataCellPlayground.Bottom() + 1 )
1328 0 : );
1329 : m_pVScroll->SetPosSizePixel(
1330 0 : aScrollbarArea.TopLeft(), aScrollbarArea.GetSize() );
1331 : }
1332 :
1333 : // create or destroy the horizontal scrollbar, as needed
1334 : lcl_updateScrollbar(
1335 : m_rAntiImpl,
1336 : m_pHScroll,
1337 : i_horizontalScrollbar,
1338 : lcl_getColumnsVisibleWithin( i_dataCellPlayground, m_nLeftColumn, *this, false ),
1339 : // visible units
1340 : m_nLeftColumn, // current position
1341 : 1, // line size
1342 : m_nColumnCount, // range
1343 : true, // horizontal
1344 : LINK( this, TableControl_Impl, OnScroll ) // scroll handler
1345 0 : );
1346 :
1347 : // position it
1348 0 : if ( m_pHScroll )
1349 : {
1350 0 : TableSize const nVisibleUnits = lcl_getColumnsVisibleWithin( i_dataCellPlayground, m_nLeftColumn, *this, false );
1351 0 : TableMetrics const nRange = m_nColumnCount;
1352 0 : if( m_nLeftColumn + nVisibleUnits == nRange - 1 )
1353 : {
1354 0 : if ( m_aColumnWidths[ nRange - 1 ].getStart() - m_aColumnWidths[ m_nLeftColumn ].getEnd() + m_aColumnWidths[ nRange-1 ].getWidth() > i_dataCellPlayground.GetWidth() )
1355 : {
1356 0 : m_pHScroll->SetVisibleSize( nVisibleUnits -1 );
1357 0 : m_pHScroll->SetPageSize( nVisibleUnits - 1 );
1358 : }
1359 : }
1360 : Rectangle aScrollbarArea(
1361 0 : Point( 0, i_dataCellPlayground.Bottom() + 1 ),
1362 0 : Size( i_dataCellPlayground.Right() + 1, nScrollbarMetrics )
1363 0 : );
1364 : m_pHScroll->SetPosSizePixel(
1365 0 : aScrollbarArea.TopLeft(), aScrollbarArea.GetSize() );
1366 : }
1367 :
1368 : // the corner window connecting the two scrollbars in the lower right corner
1369 0 : bool bHaveScrollCorner = NULL != m_pScrollCorner;
1370 0 : bool bNeedScrollCorner = ( NULL != m_pHScroll ) && ( NULL != m_pVScroll );
1371 0 : if ( bHaveScrollCorner && !bNeedScrollCorner )
1372 : {
1373 0 : DELETEZ( m_pScrollCorner );
1374 : }
1375 0 : else if ( !bHaveScrollCorner && bNeedScrollCorner )
1376 : {
1377 0 : m_pScrollCorner = new ScrollBarBox( &m_rAntiImpl );
1378 0 : m_pScrollCorner->SetSizePixel( Size( nScrollbarMetrics, nScrollbarMetrics ) );
1379 0 : m_pScrollCorner->SetPosPixel( Point( i_dataCellPlayground.Right() + 1, i_dataCellPlayground.Bottom() + 1 ) );
1380 0 : m_pScrollCorner->Show();
1381 : }
1382 0 : else if(bHaveScrollCorner && bNeedScrollCorner)
1383 : {
1384 0 : m_pScrollCorner->SetPosPixel( Point( i_dataCellPlayground.Right() + 1, i_dataCellPlayground.Bottom() + 1 ) );
1385 0 : m_pScrollCorner->Show();
1386 : }
1387 :
1388 : // resize the data window
1389 0 : m_pDataWindow->SetSizePixel( Size(
1390 0 : i_dataCellPlayground.GetWidth() + m_nRowHeaderWidthPixel,
1391 0 : i_dataCellPlayground.GetHeight() + m_nColHeaderHeightPixel
1392 0 : ) );
1393 0 : }
1394 :
1395 : //------------------------------------------------------------------------------------------------------------------
1396 0 : void TableControl_Impl::onResize()
1397 : {
1398 : DBG_CHECK_ME();
1399 :
1400 0 : impl_ni_relayout();
1401 0 : checkCursorPosition();
1402 0 : }
1403 :
1404 : //------------------------------------------------------------------------------------------------------------------
1405 0 : void TableControl_Impl::doPaintContent( const Rectangle& _rUpdateRect )
1406 : {
1407 : DBG_CHECK_ME();
1408 :
1409 0 : if ( !getModel() )
1410 : return;
1411 0 : PTableRenderer pRenderer = getModel()->getRenderer();
1412 : DBG_ASSERT( !!pRenderer, "TableDataWindow::doPaintContent: invalid renderer!" );
1413 0 : if ( !pRenderer )
1414 : return;
1415 :
1416 : // our current style settings, to be passed to the renderer
1417 0 : const StyleSettings& rStyle = m_rAntiImpl.GetSettings().GetStyleSettings();
1418 0 : m_nRowCount = m_pModel->getRowCount();
1419 : // the area occupied by all (at least partially) visible cells, including
1420 : // headers
1421 0 : Rectangle const aAllCellsWithHeaders( impl_getAllVisibleCellsArea() );
1422 :
1423 : // ............................
1424 : // draw the header column area
1425 0 : if ( m_pModel->hasColumnHeaders() )
1426 : {
1427 : TableRowGeometry const aHeaderRow( *this, Rectangle( Point( 0, 0 ),
1428 0 : aAllCellsWithHeaders.BottomRight() ), ROW_COL_HEADERS );
1429 0 : Rectangle const aColRect(aHeaderRow.getRect());
1430 0 : pRenderer->PaintHeaderArea(
1431 0 : *m_pDataWindow, aColRect, true, false, rStyle
1432 0 : );
1433 : // Note that strictly, aHeaderRow.getRect() also contains the intersection between column
1434 : // and row header area. However, below we go to paint this intersection, again,
1435 : // so this hopefully doesn't hurt if we already paint it here.
1436 :
1437 0 : for ( TableCellGeometry aCell( aHeaderRow, m_nLeftColumn );
1438 : aCell.isValid();
1439 : aCell.moveRight()
1440 : )
1441 : {
1442 0 : if ( _rUpdateRect.GetIntersection( aCell.getRect() ).IsEmpty() )
1443 0 : continue;
1444 :
1445 0 : bool isActiveColumn = ( aCell.getColumn() == getCurrentColumn() );
1446 0 : bool isSelectedColumn = false;
1447 0 : pRenderer->PaintColumnHeader( aCell.getColumn(), isActiveColumn, isSelectedColumn,
1448 0 : *m_pDataWindow, aCell.getRect(), rStyle );
1449 : }
1450 : }
1451 : // the area occupied by the row header, if any
1452 0 : Rectangle aRowHeaderArea;
1453 0 : if ( m_pModel->hasRowHeaders() )
1454 : {
1455 0 : aRowHeaderArea = aAllCellsWithHeaders;
1456 0 : aRowHeaderArea.Right() = m_nRowHeaderWidthPixel - 1;
1457 :
1458 0 : TableSize const nVisibleRows = impl_getVisibleRows( true );
1459 0 : TableSize nActualRows = nVisibleRows;
1460 0 : if ( m_nTopRow + nActualRows > m_nRowCount )
1461 0 : nActualRows = m_nRowCount - m_nTopRow;
1462 0 : aRowHeaderArea.Bottom() = m_nColHeaderHeightPixel + m_nRowHeightPixel * nActualRows - 1;
1463 :
1464 0 : pRenderer->PaintHeaderArea( *m_pDataWindow, aRowHeaderArea, false, true, rStyle );
1465 : // Note that strictly, aRowHeaderArea also contains the intersection between column
1466 : // and row header area. However, below we go to paint this intersection, again,
1467 : // so this hopefully doesn't hurt if we already paint it here.
1468 :
1469 0 : if ( m_pModel->hasColumnHeaders() )
1470 : {
1471 : TableCellGeometry const aIntersection( *this, Rectangle( Point( 0, 0 ),
1472 0 : aAllCellsWithHeaders.BottomRight() ), COL_ROW_HEADERS, ROW_COL_HEADERS );
1473 0 : Rectangle const aInters( aIntersection.getRect() );
1474 0 : pRenderer->PaintHeaderArea(
1475 0 : *m_pDataWindow, aInters, true, true, rStyle
1476 0 : );
1477 : }
1478 : }
1479 :
1480 : // ............................
1481 : // draw the table content row by row
1482 :
1483 0 : TableSize colCount = getModel()->getColumnCount();
1484 :
1485 : // paint all rows
1486 0 : Rectangle const aAllDataCellsArea( impl_getAllVisibleDataCellArea() );
1487 0 : for ( TableRowGeometry aRowIterator( *this, aAllCellsWithHeaders, getTopRow() );
1488 : aRowIterator.isValid();
1489 : aRowIterator.moveDown() )
1490 : {
1491 0 : if ( _rUpdateRect.GetIntersection( aRowIterator.getRect() ).IsEmpty() )
1492 0 : continue;
1493 :
1494 0 : bool const isControlFocused = m_rAntiImpl.HasControlFocus();
1495 0 : bool const isSelectedRow = isRowSelected( aRowIterator.getRow() );
1496 :
1497 0 : Rectangle const aRect = aRowIterator.getRect().GetIntersection( aAllDataCellsArea );
1498 :
1499 : // give the redenderer a chance to prepare the row
1500 0 : pRenderer->PrepareRow(
1501 : aRowIterator.getRow(), isControlFocused, isSelectedRow,
1502 0 : *m_pDataWindow, aRect, rStyle
1503 0 : );
1504 :
1505 : // paint the row header
1506 0 : if ( m_pModel->hasRowHeaders() )
1507 : {
1508 0 : const Rectangle aCurrentRowHeader( aRowHeaderArea.GetIntersection( aRowIterator.getRect() ) );
1509 0 : pRenderer->PaintRowHeader( isControlFocused, isSelectedRow, *m_pDataWindow, aCurrentRowHeader,
1510 0 : rStyle );
1511 : }
1512 :
1513 0 : if ( !colCount )
1514 0 : continue;
1515 :
1516 : // paint all cells in this row
1517 0 : for ( TableCellGeometry aCell( aRowIterator, m_nLeftColumn );
1518 : aCell.isValid();
1519 : aCell.moveRight()
1520 : )
1521 : {
1522 0 : bool isSelectedColumn = false;
1523 0 : pRenderer->PaintCell( aCell.getColumn(), isSelectedRow || isSelectedColumn, isControlFocused,
1524 0 : *m_pDataWindow, aCell.getRect(), rStyle );
1525 : }
1526 0 : }
1527 : }
1528 : //------------------------------------------------------------------------------------------------------------------
1529 0 : void TableControl_Impl::hideCursor()
1530 : {
1531 : DBG_CHECK_ME();
1532 :
1533 0 : if ( ++m_nCursorHidden == 1 )
1534 0 : impl_ni_doSwitchCursor( false );
1535 0 : }
1536 :
1537 : //------------------------------------------------------------------------------------------------------------------
1538 0 : void TableControl_Impl::showCursor()
1539 : {
1540 : DBG_CHECK_ME();
1541 :
1542 : DBG_ASSERT( m_nCursorHidden > 0, "TableControl_Impl::showCursor: cursor not hidden!" );
1543 0 : if ( --m_nCursorHidden == 0 )
1544 0 : impl_ni_doSwitchCursor( true );
1545 0 : }
1546 :
1547 : //------------------------------------------------------------------------------------------------------------------
1548 0 : bool TableControl_Impl::dispatchAction( TableControlAction _eAction )
1549 : {
1550 : DBG_CHECK_ME();
1551 :
1552 0 : bool bSuccess = false;
1553 0 : bool selectionChanged = false;
1554 :
1555 0 : switch ( _eAction )
1556 : {
1557 : case cursorDown:
1558 0 : if ( m_pSelEngine->GetSelectionMode() == SINGLE_SELECTION )
1559 : {
1560 : //if other rows already selected, deselect them
1561 0 : if(!m_aSelectedRows.empty())
1562 : {
1563 0 : invalidateSelectedRows();
1564 0 : m_aSelectedRows.clear();
1565 : }
1566 0 : if ( m_nCurRow < m_nRowCount-1 )
1567 : {
1568 0 : ++m_nCurRow;
1569 0 : m_aSelectedRows.push_back(m_nCurRow);
1570 : }
1571 : else
1572 0 : m_aSelectedRows.push_back(m_nCurRow);
1573 0 : invalidateRow( m_nCurRow );
1574 0 : ensureVisible(m_nCurColumn,m_nCurRow,false);
1575 0 : selectionChanged = true;
1576 0 : bSuccess = true;
1577 : }
1578 : else
1579 : {
1580 0 : if ( m_nCurRow < m_nRowCount - 1 )
1581 0 : bSuccess = goTo( m_nCurColumn, m_nCurRow + 1 );
1582 : }
1583 0 : break;
1584 :
1585 : case cursorUp:
1586 0 : if(m_pSelEngine->GetSelectionMode() == SINGLE_SELECTION)
1587 : {
1588 0 : if(!m_aSelectedRows.empty())
1589 : {
1590 0 : invalidateSelectedRows();
1591 0 : m_aSelectedRows.clear();
1592 : }
1593 0 : if(m_nCurRow>0)
1594 : {
1595 0 : --m_nCurRow;
1596 0 : m_aSelectedRows.push_back(m_nCurRow);
1597 0 : invalidateRow( m_nCurRow );
1598 : }
1599 : else
1600 : {
1601 0 : m_aSelectedRows.push_back(m_nCurRow);
1602 0 : invalidateRow( m_nCurRow );
1603 : }
1604 0 : ensureVisible(m_nCurColumn,m_nCurRow,false);
1605 0 : selectionChanged = true;
1606 0 : bSuccess = true;
1607 : }
1608 : else
1609 : {
1610 0 : if ( m_nCurRow > 0 )
1611 0 : bSuccess = goTo( m_nCurColumn, m_nCurRow - 1 );
1612 : }
1613 0 : break;
1614 : case cursorLeft:
1615 0 : if ( m_nCurColumn > 0 )
1616 0 : bSuccess = goTo( m_nCurColumn - 1, m_nCurRow );
1617 : else
1618 0 : if ( ( m_nCurColumn == 0) && ( m_nCurRow > 0 ) )
1619 0 : bSuccess = goTo( m_nColumnCount - 1, m_nCurRow - 1 );
1620 0 : break;
1621 :
1622 : case cursorRight:
1623 0 : if ( m_nCurColumn < m_nColumnCount - 1 )
1624 0 : bSuccess = goTo( m_nCurColumn + 1, m_nCurRow );
1625 : else
1626 0 : if ( ( m_nCurColumn == m_nColumnCount - 1 ) && ( m_nCurRow < m_nRowCount - 1 ) )
1627 0 : bSuccess = goTo( 0, m_nCurRow + 1 );
1628 0 : break;
1629 :
1630 : case cursorToLineStart:
1631 0 : bSuccess = goTo( 0, m_nCurRow );
1632 0 : break;
1633 :
1634 : case cursorToLineEnd:
1635 0 : bSuccess = goTo( m_nColumnCount - 1, m_nCurRow );
1636 0 : break;
1637 :
1638 : case cursorToFirstLine:
1639 0 : bSuccess = goTo( m_nCurColumn, 0 );
1640 0 : break;
1641 :
1642 : case cursorToLastLine:
1643 0 : bSuccess = goTo( m_nCurColumn, m_nRowCount - 1 );
1644 0 : break;
1645 :
1646 : case cursorPageUp:
1647 : {
1648 0 : RowPos nNewRow = ::std::max( (RowPos)0, m_nCurRow - impl_getVisibleRows( false ) );
1649 0 : bSuccess = goTo( m_nCurColumn, nNewRow );
1650 : }
1651 0 : break;
1652 :
1653 : case cursorPageDown:
1654 : {
1655 0 : RowPos nNewRow = ::std::min( m_nRowCount - 1, m_nCurRow + impl_getVisibleRows( false ) );
1656 0 : bSuccess = goTo( m_nCurColumn, nNewRow );
1657 : }
1658 0 : break;
1659 :
1660 : case cursorTopLeft:
1661 0 : bSuccess = goTo( 0, 0 );
1662 0 : break;
1663 :
1664 : case cursorBottomRight:
1665 0 : bSuccess = goTo( m_nColumnCount - 1, m_nRowCount - 1 );
1666 0 : break;
1667 :
1668 : case cursorSelectRow:
1669 : {
1670 0 : if(m_pSelEngine->GetSelectionMode() == NO_SELECTION)
1671 0 : return bSuccess = false;
1672 : //pos is the position of the current row in the vector of selected rows, if current row is selected
1673 0 : int pos = getRowSelectedNumber(m_aSelectedRows, m_nCurRow);
1674 : //if current row is selected, it should be deselected, when ALT+SPACE are pressed
1675 0 : if(pos>-1)
1676 : {
1677 0 : m_aSelectedRows.erase(m_aSelectedRows.begin()+pos);
1678 0 : if(m_aSelectedRows.empty() && m_nAnchor != -1)
1679 0 : m_nAnchor = -1;
1680 : }
1681 : //else select the row->put it in the vector
1682 : else
1683 0 : m_aSelectedRows.push_back(m_nCurRow);
1684 0 : invalidateRow( m_nCurRow );
1685 0 : selectionChanged = true;
1686 0 : bSuccess = true;
1687 : }
1688 0 : break;
1689 : case cursorSelectRowUp:
1690 : {
1691 0 : if(m_pSelEngine->GetSelectionMode() == NO_SELECTION)
1692 0 : return bSuccess = false;
1693 0 : else if(m_pSelEngine->GetSelectionMode() == SINGLE_SELECTION)
1694 : {
1695 : //if there are other selected rows, deselect them
1696 0 : return false;
1697 : }
1698 : else
1699 : {
1700 : //there are other selected rows
1701 0 : if(!m_aSelectedRows.empty())
1702 : {
1703 : //the anchor wasn't set -> a region is not selected, that's why clear all selection
1704 : //and select the current row
1705 0 : if(m_nAnchor==-1)
1706 : {
1707 0 : invalidateSelectedRows();
1708 0 : m_aSelectedRows.clear();
1709 0 : m_aSelectedRows.push_back(m_nCurRow);
1710 0 : invalidateRow( m_nCurRow );
1711 : }
1712 : else
1713 : {
1714 : //a region is already selected, prevRow is last selected row and the row above - nextRow - should be selected
1715 0 : int prevRow = getRowSelectedNumber(m_aSelectedRows, m_nCurRow);
1716 0 : int nextRow = getRowSelectedNumber(m_aSelectedRows, m_nCurRow-1);
1717 0 : if(prevRow>-1)
1718 : {
1719 : //if m_nCurRow isn't the upper one, can move up, otherwise not
1720 0 : if(m_nCurRow>0)
1721 0 : m_nCurRow--;
1722 : else
1723 0 : return bSuccess = true;
1724 : //if nextRow already selected, deselect it, otherwise select it
1725 0 : if(nextRow>-1 && m_aSelectedRows[nextRow] == m_nCurRow)
1726 : {
1727 0 : m_aSelectedRows.erase(m_aSelectedRows.begin()+prevRow);
1728 0 : invalidateRow( m_nCurRow + 1 );
1729 : }
1730 : else
1731 : {
1732 0 : m_aSelectedRows.push_back(m_nCurRow);
1733 0 : invalidateRow( m_nCurRow );
1734 : }
1735 : }
1736 : else
1737 : {
1738 0 : if(m_nCurRow>0)
1739 : {
1740 0 : m_aSelectedRows.push_back(m_nCurRow);
1741 0 : m_nCurRow--;
1742 0 : m_aSelectedRows.push_back(m_nCurRow);
1743 0 : invalidateSelectedRegion( m_nCurRow+1, m_nCurRow );
1744 : }
1745 : }
1746 : }
1747 : }
1748 : else
1749 : {
1750 : //if nothing is selected and the current row isn't the upper one
1751 : //select the current and one row above
1752 : //otherwise select only the upper row
1753 0 : if(m_nCurRow>0)
1754 : {
1755 0 : m_aSelectedRows.push_back(m_nCurRow);
1756 0 : m_nCurRow--;
1757 0 : m_aSelectedRows.push_back(m_nCurRow);
1758 0 : invalidateSelectedRegion( m_nCurRow+1, m_nCurRow );
1759 : }
1760 : else
1761 : {
1762 0 : m_aSelectedRows.push_back(m_nCurRow);
1763 0 : invalidateRow( m_nCurRow );
1764 : }
1765 : }
1766 0 : m_pSelEngine->SetAnchor(sal_True);
1767 0 : m_nAnchor = m_nCurRow;
1768 0 : ensureVisible(m_nCurColumn, m_nCurRow, false);
1769 0 : selectionChanged = true;
1770 0 : bSuccess = true;
1771 : }
1772 : }
1773 0 : break;
1774 : case cursorSelectRowDown:
1775 : {
1776 0 : if(m_pSelEngine->GetSelectionMode() == NO_SELECTION)
1777 0 : bSuccess = false;
1778 0 : else if(m_pSelEngine->GetSelectionMode() == SINGLE_SELECTION)
1779 : {
1780 0 : bSuccess = false;
1781 : }
1782 : else
1783 : {
1784 0 : if(!m_aSelectedRows.empty())
1785 : {
1786 : //the anchor wasn't set -> a region is not selected, that's why clear all selection
1787 : //and select the current row
1788 0 : if(m_nAnchor==-1)
1789 : {
1790 0 : invalidateSelectedRows();
1791 0 : m_aSelectedRows.clear();
1792 0 : m_aSelectedRows.push_back(m_nCurRow);
1793 0 : invalidateRow( m_nCurRow );
1794 : }
1795 : else
1796 : {
1797 : //a region is already selected, prevRow is last selected row and the row beneath - nextRow - should be selected
1798 0 : int prevRow = getRowSelectedNumber(m_aSelectedRows, m_nCurRow);
1799 0 : int nextRow = getRowSelectedNumber(m_aSelectedRows, m_nCurRow+1);
1800 0 : if(prevRow>-1)
1801 : {
1802 : //if m_nCurRow isn't the last one, can move down, otherwise not
1803 0 : if(m_nCurRow<m_nRowCount-1)
1804 0 : m_nCurRow++;
1805 : else
1806 0 : return bSuccess = true;
1807 : //if next row already selected, deselect it, otherwise select it
1808 0 : if(nextRow>-1 && m_aSelectedRows[nextRow] == m_nCurRow)
1809 : {
1810 0 : m_aSelectedRows.erase(m_aSelectedRows.begin()+prevRow);
1811 0 : invalidateRow( m_nCurRow - 1 );
1812 : }
1813 : else
1814 : {
1815 0 : m_aSelectedRows.push_back(m_nCurRow);
1816 0 : invalidateRow( m_nCurRow );
1817 : }
1818 : }
1819 : else
1820 : {
1821 0 : if(m_nCurRow<m_nRowCount-1)
1822 : {
1823 0 : m_aSelectedRows.push_back(m_nCurRow);
1824 0 : m_nCurRow++;
1825 0 : m_aSelectedRows.push_back(m_nCurRow);
1826 0 : invalidateSelectedRegion( m_nCurRow-1, m_nCurRow );
1827 : }
1828 : }
1829 : }
1830 : }
1831 : else
1832 : {
1833 : //there wasn't any selection, select current and row beneath, otherwise only row beneath
1834 0 : if(m_nCurRow<m_nRowCount-1)
1835 : {
1836 0 : m_aSelectedRows.push_back(m_nCurRow);
1837 0 : m_nCurRow++;
1838 0 : m_aSelectedRows.push_back(m_nCurRow);
1839 0 : invalidateSelectedRegion( m_nCurRow-1, m_nCurRow );
1840 : }
1841 : else
1842 : {
1843 0 : m_aSelectedRows.push_back(m_nCurRow);
1844 0 : invalidateRow( m_nCurRow );
1845 : }
1846 : }
1847 0 : m_pSelEngine->SetAnchor(sal_True);
1848 0 : m_nAnchor = m_nCurRow;
1849 0 : ensureVisible(m_nCurColumn, m_nCurRow, false);
1850 0 : selectionChanged = true;
1851 0 : bSuccess = true;
1852 : }
1853 : }
1854 0 : break;
1855 :
1856 : case cursorSelectRowAreaTop:
1857 : {
1858 0 : if(m_pSelEngine->GetSelectionMode() == NO_SELECTION)
1859 0 : bSuccess = false;
1860 0 : else if(m_pSelEngine->GetSelectionMode() == SINGLE_SELECTION)
1861 0 : bSuccess = false;
1862 : else
1863 : {
1864 : //select the region between the current and the upper row
1865 0 : RowPos iter = m_nCurRow;
1866 0 : invalidateSelectedRegion( m_nCurRow, 0 );
1867 : //put the rows in vector
1868 0 : while(iter>=0)
1869 : {
1870 0 : if ( !isRowSelected( iter ) )
1871 0 : m_aSelectedRows.push_back(iter);
1872 0 : --iter;
1873 : }
1874 0 : m_nCurRow = 0;
1875 0 : m_nAnchor = m_nCurRow;
1876 0 : m_pSelEngine->SetAnchor(sal_True);
1877 0 : ensureVisible(m_nCurColumn, 0, false);
1878 0 : selectionChanged = true;
1879 0 : bSuccess = true;
1880 : }
1881 : }
1882 0 : break;
1883 :
1884 : case cursorSelectRowAreaBottom:
1885 : {
1886 0 : if(m_pSelEngine->GetSelectionMode() == NO_SELECTION)
1887 0 : return bSuccess = false;
1888 0 : else if(m_pSelEngine->GetSelectionMode() == SINGLE_SELECTION)
1889 0 : return bSuccess = false;
1890 : //select the region between the current and the last row
1891 0 : RowPos iter = m_nCurRow;
1892 0 : invalidateSelectedRegion( m_nCurRow, m_nRowCount-1 );
1893 : //put the rows in the vector
1894 0 : while(iter<=m_nRowCount)
1895 : {
1896 0 : if ( !isRowSelected( iter ) )
1897 0 : m_aSelectedRows.push_back(iter);
1898 0 : ++iter;
1899 : }
1900 0 : m_nCurRow = m_nRowCount-1;
1901 0 : m_nAnchor = m_nCurRow;
1902 0 : m_pSelEngine->SetAnchor(sal_True);
1903 0 : ensureVisible(m_nCurColumn, m_nRowCount-1, false);
1904 0 : selectionChanged = true;
1905 0 : bSuccess = true;
1906 : }
1907 0 : break;
1908 : default:
1909 : OSL_FAIL( "TableControl_Impl::dispatchAction: unsupported action!" );
1910 0 : break;
1911 : }
1912 :
1913 0 : if ( bSuccess && selectionChanged )
1914 : {
1915 0 : m_rAntiImpl.Select();
1916 : }
1917 :
1918 0 : return bSuccess;
1919 : }
1920 :
1921 : //------------------------------------------------------------------------------------------------------------------
1922 0 : void TableControl_Impl::impl_ni_doSwitchCursor( bool _bShow )
1923 : {
1924 0 : PTableRenderer pRenderer = !!m_pModel ? m_pModel->getRenderer() : PTableRenderer();
1925 0 : if ( !!pRenderer )
1926 : {
1927 0 : Rectangle aCellRect;
1928 0 : impl_getCellRect( m_nCurColumn, m_nCurRow, aCellRect );
1929 0 : if ( _bShow )
1930 0 : pRenderer->ShowCellCursor( *m_pDataWindow, aCellRect );
1931 : else
1932 0 : pRenderer->HideCellCursor( *m_pDataWindow, aCellRect );
1933 0 : }
1934 0 : }
1935 :
1936 : //------------------------------------------------------------------------------------------------------------------
1937 0 : void TableControl_Impl::impl_getCellRect( ColPos _nColumn, RowPos _nRow, Rectangle& _rCellRect ) const
1938 : {
1939 : DBG_CHECK_ME();
1940 :
1941 0 : if ( !m_pModel
1942 : || ( COL_INVALID == _nColumn )
1943 : || ( ROW_INVALID == _nRow )
1944 : )
1945 : {
1946 0 : _rCellRect.SetEmpty();
1947 0 : return;
1948 : }
1949 :
1950 0 : TableCellGeometry aCell( *this, impl_getAllVisibleCellsArea(), _nColumn, _nRow );
1951 0 : _rCellRect = aCell.getRect();
1952 : }
1953 :
1954 : //------------------------------------------------------------------------------------------------------------------
1955 0 : RowPos TableControl_Impl::getRowAtPoint( const Point& rPoint ) const
1956 : {
1957 : DBG_CHECK_ME();
1958 0 : return impl_getRowForAbscissa( rPoint.Y() );
1959 : }
1960 :
1961 : //------------------------------------------------------------------------------------------------------------------
1962 0 : ColPos TableControl_Impl::getColAtPoint( const Point& rPoint ) const
1963 : {
1964 : DBG_CHECK_ME();
1965 0 : return impl_getColumnForOrdinate( rPoint.X() );
1966 : }
1967 :
1968 : //------------------------------------------------------------------------------------------------------------------
1969 0 : TableCell TableControl_Impl::hitTest( Point const & i_point ) const
1970 : {
1971 0 : TableCell aCell( getColAtPoint( i_point ), getRowAtPoint( i_point ) );
1972 0 : if ( aCell.nColumn > COL_ROW_HEADERS )
1973 : {
1974 0 : PColumnModel const pColumn = m_pModel->getColumnModel( aCell.nColumn );
1975 0 : MutableColumnMetrics const & rColInfo( m_aColumnWidths[ aCell.nColumn ] );
1976 0 : if ( ( rColInfo.getEnd() - 3 <= i_point.X() )
1977 0 : && ( rColInfo.getEnd() >= i_point.X() )
1978 0 : && pColumn->isResizable()
1979 : )
1980 : {
1981 0 : aCell.eArea = ColumnDivider;
1982 0 : }
1983 : }
1984 0 : return aCell;
1985 : }
1986 :
1987 : //------------------------------------------------------------------------------------------------------------------
1988 0 : ColumnMetrics TableControl_Impl::getColumnMetrics( ColPos const i_column ) const
1989 : {
1990 : DBG_CHECK_ME();
1991 :
1992 0 : ENSURE_OR_RETURN( ( i_column >= 0 ) && ( i_column < m_pModel->getColumnCount() ),
1993 : "TableControl_Impl::getColumnMetrics: illegal column index!", ColumnMetrics() );
1994 0 : return (ColumnMetrics const &)m_aColumnWidths[ i_column ];
1995 : }
1996 :
1997 : //------------------------------------------------------------------------------------------------------------------
1998 0 : PTableModel TableControl_Impl::getModel() const
1999 : {
2000 0 : return m_pModel;
2001 : }
2002 :
2003 : //------------------------------------------------------------------------------------------------------------------
2004 0 : RowPos TableControl_Impl::getCurrentColumn() const
2005 : {
2006 0 : return m_nCurColumn;
2007 : }
2008 :
2009 : //------------------------------------------------------------------------------------------------------------------
2010 0 : RowPos TableControl_Impl::getCurrentRow() const
2011 : {
2012 0 : return m_nCurRow;
2013 : }
2014 :
2015 : //------------------------------------------------------------------------------------------------------------------
2016 0 : ::Size TableControl_Impl::getTableSizePixel() const
2017 : {
2018 0 : return m_pDataWindow->GetOutputSizePixel();
2019 : }
2020 :
2021 : //------------------------------------------------------------------------------------------------------------------
2022 0 : void TableControl_Impl::setPointer( Pointer const & i_pointer )
2023 : {
2024 : DBG_CHECK_ME();
2025 0 : m_pDataWindow->SetPointer( i_pointer );
2026 0 : }
2027 :
2028 : //------------------------------------------------------------------------------------------------------------------
2029 0 : void TableControl_Impl::captureMouse()
2030 : {
2031 0 : m_pDataWindow->CaptureMouse();
2032 0 : }
2033 :
2034 : //------------------------------------------------------------------------------------------------------------------
2035 0 : void TableControl_Impl::releaseMouse()
2036 : {
2037 0 : m_pDataWindow->ReleaseMouse();
2038 0 : }
2039 :
2040 : //------------------------------------------------------------------------------------------------------------------
2041 0 : void TableControl_Impl::invalidate( TableArea const i_what )
2042 : {
2043 0 : switch ( i_what )
2044 : {
2045 : case TableAreaColumnHeaders:
2046 0 : m_pDataWindow->Invalidate( calcHeaderRect( true ) );
2047 0 : break;
2048 :
2049 : case TableAreaRowHeaders:
2050 0 : m_pDataWindow->Invalidate( calcHeaderRect( false ) );
2051 0 : break;
2052 :
2053 : case TableAreaDataArea:
2054 0 : m_pDataWindow->Invalidate( impl_getAllVisibleDataCellArea() );
2055 0 : break;
2056 :
2057 : case TableAreaAll:
2058 0 : m_pDataWindow->Invalidate();
2059 0 : break;
2060 : }
2061 0 : }
2062 :
2063 : //------------------------------------------------------------------------------------------------------------------
2064 0 : long TableControl_Impl::pixelWidthToAppFont( long const i_pixels ) const
2065 : {
2066 0 : return m_pDataWindow->PixelToLogic( Size( i_pixels, 0 ), MAP_APPFONT ).Width();
2067 : }
2068 :
2069 : //------------------------------------------------------------------------------------------------------------------
2070 0 : long TableControl_Impl::appFontWidthToPixel( long const i_appFontUnits ) const
2071 : {
2072 0 : return m_pDataWindow->LogicToPixel( Size( i_appFontUnits, 0 ), MAP_APPFONT ).Width();
2073 : }
2074 :
2075 : //------------------------------------------------------------------------------------------------------------------
2076 0 : void TableControl_Impl::hideTracking()
2077 : {
2078 0 : m_pDataWindow->HideTracking();
2079 0 : }
2080 :
2081 : //------------------------------------------------------------------------------------------------------------------
2082 0 : void TableControl_Impl::showTracking( Rectangle const & i_location, sal_uInt16 const i_flags )
2083 : {
2084 0 : m_pDataWindow->ShowTracking( i_location, i_flags );
2085 0 : }
2086 :
2087 : //------------------------------------------------------------------------------------------------------------------
2088 0 : bool TableControl_Impl::activateCell( ColPos const i_col, RowPos const i_row )
2089 : {
2090 : DBG_CHECK_ME();
2091 0 : return goTo( i_col, i_row );
2092 : }
2093 :
2094 : //------------------------------------------------------------------------------------------------------------------
2095 0 : void TableControl_Impl::invalidateSelectedRegion( RowPos _nPrevRow, RowPos _nCurRow )
2096 : {
2097 : DBG_CHECK_ME();
2098 : // get the visible area of the table control and set the Left and right border of the region to be repainted
2099 0 : Rectangle const aAllCells( impl_getAllVisibleCellsArea() );
2100 :
2101 0 : Rectangle aInvalidateRect;
2102 0 : aInvalidateRect.Left() = aAllCells.Left();
2103 0 : aInvalidateRect.Right() = aAllCells.Right();
2104 : // if only one row is selected
2105 0 : if ( _nPrevRow == _nCurRow )
2106 : {
2107 0 : Rectangle aCellRect;
2108 0 : impl_getCellRect( m_nCurColumn, _nCurRow, aCellRect );
2109 0 : aInvalidateRect.Top() = aCellRect.Top();
2110 0 : aInvalidateRect.Bottom() = aCellRect.Bottom();
2111 : }
2112 : //if the region is above the current row
2113 0 : else if(_nPrevRow < _nCurRow )
2114 : {
2115 0 : Rectangle aCellRect;
2116 0 : impl_getCellRect( m_nCurColumn, _nPrevRow, aCellRect );
2117 0 : aInvalidateRect.Top() = aCellRect.Top();
2118 0 : impl_getCellRect( m_nCurColumn, _nCurRow, aCellRect );
2119 0 : aInvalidateRect.Bottom() = aCellRect.Bottom();
2120 : }
2121 : //if the region is beneath the current row
2122 : else
2123 : {
2124 0 : Rectangle aCellRect;
2125 0 : impl_getCellRect( m_nCurColumn, _nCurRow, aCellRect );
2126 0 : aInvalidateRect.Top() = aCellRect.Top();
2127 0 : impl_getCellRect( m_nCurColumn, _nPrevRow, aCellRect );
2128 0 : aInvalidateRect.Bottom() = aCellRect.Bottom();
2129 : }
2130 0 : m_pDataWindow->Invalidate( aInvalidateRect );
2131 0 : }
2132 :
2133 : //------------------------------------------------------------------------------------------------------------------
2134 0 : void TableControl_Impl::invalidateSelectedRows()
2135 : {
2136 0 : for ( ::std::vector< RowPos >::iterator selRow = m_aSelectedRows.begin();
2137 0 : selRow != m_aSelectedRows.end();
2138 : ++selRow
2139 : )
2140 : {
2141 0 : invalidateRow( *selRow );
2142 : }
2143 0 : }
2144 :
2145 : //------------------------------------------------------------------------------------------------------------------
2146 0 : void TableControl_Impl::invalidateRowRange( RowPos const i_firstRow, RowPos const i_lastRow )
2147 : {
2148 0 : RowPos const firstRow = i_firstRow < m_nTopRow ? m_nTopRow : i_firstRow;
2149 0 : RowPos const lastVisibleRow = m_nTopRow + impl_getVisibleRows( true ) - 1;
2150 0 : RowPos const lastRow = ( ( i_lastRow == ROW_INVALID ) || ( i_lastRow > lastVisibleRow ) ) ? lastVisibleRow : i_lastRow;
2151 :
2152 0 : Rectangle aInvalidateRect;
2153 :
2154 0 : Rectangle const aVisibleCellsArea( impl_getAllVisibleCellsArea() );
2155 0 : TableRowGeometry aRow( *this, aVisibleCellsArea, firstRow, true );
2156 0 : while ( aRow.isValid() && ( aRow.getRow() <= lastRow ) )
2157 : {
2158 0 : aInvalidateRect.Union( aRow.getRect() );
2159 0 : aRow.moveDown();
2160 : }
2161 :
2162 0 : if ( i_lastRow == ROW_INVALID )
2163 0 : aInvalidateRect.Bottom() = m_pDataWindow->GetOutputSizePixel().Height();
2164 :
2165 0 : m_pDataWindow->Invalidate( aInvalidateRect );
2166 0 : }
2167 :
2168 : //------------------------------------------------------------------------------
2169 0 : void TableControl_Impl::checkCursorPosition()
2170 : {
2171 : DBG_CHECK_ME();
2172 :
2173 0 : TableSize nVisibleRows = impl_getVisibleRows(true);
2174 0 : TableSize nVisibleCols = impl_getVisibleColumns(true);
2175 0 : if ( ( m_nTopRow + nVisibleRows > m_nRowCount )
2176 : && ( m_nRowCount >= nVisibleRows )
2177 : )
2178 : {
2179 0 : --m_nTopRow;
2180 : }
2181 : else
2182 : {
2183 0 : m_nTopRow = 0;
2184 : }
2185 :
2186 0 : if ( ( m_nLeftColumn + nVisibleCols > m_nColumnCount )
2187 : && ( m_nColumnCount >= nVisibleCols )
2188 : )
2189 : {
2190 0 : --m_nLeftColumn;
2191 : }
2192 : else
2193 : {
2194 0 : m_nLeftColumn = 0;
2195 : }
2196 :
2197 0 : m_pDataWindow->Invalidate();
2198 0 : }
2199 :
2200 : //--------------------------------------------------------------------
2201 0 : TableSize TableControl_Impl::impl_getVisibleRows( bool _bAcceptPartialRow ) const
2202 : {
2203 : DBG_CHECK_ME();
2204 :
2205 : DBG_ASSERT( m_pDataWindow, "TableControl_Impl::impl_getVisibleRows: no data window!" );
2206 :
2207 : return lcl_getRowsFittingInto(
2208 0 : m_pDataWindow->GetOutputSizePixel().Height() - m_nColHeaderHeightPixel,
2209 : m_nRowHeightPixel,
2210 : _bAcceptPartialRow
2211 0 : );
2212 : }
2213 :
2214 : //--------------------------------------------------------------------
2215 0 : TableSize TableControl_Impl::impl_getVisibleColumns( bool _bAcceptPartialCol ) const
2216 : {
2217 : DBG_CHECK_ME();
2218 :
2219 : DBG_ASSERT( m_pDataWindow, "TableControl_Impl::impl_getVisibleColumns: no data window!" );
2220 :
2221 : return lcl_getColumnsVisibleWithin(
2222 0 : Rectangle( Point( 0, 0 ), m_pDataWindow->GetOutputSizePixel() ),
2223 : m_nLeftColumn,
2224 : *this,
2225 : _bAcceptPartialCol
2226 0 : );
2227 : }
2228 :
2229 : //--------------------------------------------------------------------
2230 0 : bool TableControl_Impl::goTo( ColPos _nColumn, RowPos _nRow )
2231 : {
2232 : DBG_CHECK_ME();
2233 :
2234 : // TODO: give veto listeners a chance
2235 :
2236 0 : if ( ( _nColumn < 0 ) || ( _nColumn >= m_nColumnCount )
2237 : || ( _nRow < 0 ) || ( _nRow >= m_nRowCount )
2238 : )
2239 : {
2240 : OSL_ENSURE( false, "TableControl_Impl::goTo: invalid row or column index!" );
2241 0 : return false;
2242 : }
2243 :
2244 0 : SuppressCursor aHideCursor( *this );
2245 0 : m_nCurColumn = _nColumn;
2246 0 : m_nCurRow = _nRow;
2247 :
2248 : // ensure that the new cell is visible
2249 0 : ensureVisible( m_nCurColumn, m_nCurRow, false );
2250 0 : return true;
2251 : }
2252 :
2253 : //--------------------------------------------------------------------
2254 0 : void TableControl_Impl::ensureVisible( ColPos _nColumn, RowPos _nRow, bool _bAcceptPartialVisibility )
2255 : {
2256 : DBG_CHECK_ME();
2257 : DBG_ASSERT( ( _nColumn >= 0 ) && ( _nColumn < m_nColumnCount )
2258 : && ( _nRow >= 0 ) && ( _nRow < m_nRowCount ),
2259 : "TableControl_Impl::ensureVisible: invalid coordinates!" );
2260 :
2261 0 : SuppressCursor aHideCursor( *this );
2262 :
2263 0 : if ( _nColumn < m_nLeftColumn )
2264 0 : impl_scrollColumns( _nColumn - m_nLeftColumn );
2265 : else
2266 : {
2267 0 : TableSize nVisibleColumns = impl_getVisibleColumns( _bAcceptPartialVisibility );
2268 0 : if ( _nColumn > m_nLeftColumn + nVisibleColumns - 1 )
2269 : {
2270 0 : impl_scrollColumns( _nColumn - ( m_nLeftColumn + nVisibleColumns - 1 ) );
2271 : // TODO: since not all columns have the same width, this might in theory result
2272 : // in the column still not being visible.
2273 : }
2274 : }
2275 :
2276 0 : if ( _nRow < m_nTopRow )
2277 0 : impl_scrollRows( _nRow - m_nTopRow );
2278 : else
2279 : {
2280 0 : TableSize nVisibleRows = impl_getVisibleRows( _bAcceptPartialVisibility );
2281 0 : if ( _nRow > m_nTopRow + nVisibleRows - 1 )
2282 0 : impl_scrollRows( _nRow - ( m_nTopRow + nVisibleRows - 1 ) );
2283 0 : }
2284 0 : }
2285 :
2286 : //--------------------------------------------------------------------
2287 0 : ::rtl::OUString TableControl_Impl::getCellContentAsString( RowPos const i_row, ColPos const i_col )
2288 : {
2289 0 : Any aCellValue;
2290 0 : m_pModel->getCellContent( i_col, i_row, aCellValue );
2291 :
2292 0 : ::rtl::OUString sCellStringContent;
2293 0 : m_pModel->getRenderer()->GetFormattedCellString( aCellValue, i_col, i_row, sCellStringContent );
2294 :
2295 0 : return sCellStringContent;
2296 : }
2297 :
2298 : //--------------------------------------------------------------------
2299 0 : TableSize TableControl_Impl::impl_ni_ScrollRows( TableSize _nRowDelta )
2300 : {
2301 : // compute new top row
2302 : RowPos nNewTopRow =
2303 : ::std::max(
2304 0 : ::std::min( (RowPos)( m_nTopRow + _nRowDelta ), (RowPos)( m_nRowCount - 1 ) ),
2305 : (RowPos)0
2306 0 : );
2307 :
2308 0 : RowPos nOldTopRow = m_nTopRow;
2309 0 : m_nTopRow = nNewTopRow;
2310 :
2311 : // if updates are enabled currently, scroll the viewport
2312 0 : if ( m_nTopRow != nOldTopRow )
2313 : {
2314 : DBG_SUSPEND_INV( INV_SCROLL_POSITION );
2315 0 : SuppressCursor aHideCursor( *this );
2316 : // TODO: call a onStartScroll at our listener (or better an own onStartScroll,
2317 : // which hides the cursor and then calls the listener)
2318 : // Same for onEndScroll
2319 :
2320 : // scroll the view port, if possible
2321 0 : long nPixelDelta = m_nRowHeightPixel * ( m_nTopRow - nOldTopRow );
2322 :
2323 0 : Rectangle aDataArea( Point( 0, m_nColHeaderHeightPixel ), m_pDataWindow->GetOutputSizePixel() );
2324 :
2325 0 : if ( m_pDataWindow->GetBackground().IsScrollable()
2326 0 : && abs( nPixelDelta ) < aDataArea.GetHeight()
2327 : )
2328 : {
2329 0 : m_pDataWindow->Scroll( 0, (long)-nPixelDelta, aDataArea, SCROLL_CLIP | SCROLL_UPDATE | SCROLL_CHILDREN);
2330 : }
2331 : else
2332 0 : m_pDataWindow->Invalidate( INVALIDATE_UPDATE );
2333 :
2334 : // update the position at the vertical scrollbar
2335 0 : if ( m_pVScroll != NULL )
2336 0 : m_pVScroll->SetThumbPos( m_nTopRow );
2337 : }
2338 :
2339 : // The scroll bar availaility might change when we scrolled.
2340 : // For instance, imagine a view with 10 rows, if which 5 fit into the window, numbered 1 to 10.
2341 : // Now let
2342 : // - the user scroll to row number 6, so the last 5 rows are visible
2343 : // - somebody remove the last 4 rows
2344 : // - the user scroll to row number 5 being the top row, so the last two rows are visible
2345 : // - somebody remove row number 6
2346 : // - the user scroll to row number 1
2347 : // => in this case, the need for the scrollbar vanishes immediately.
2348 0 : if ( m_nTopRow == 0 )
2349 0 : m_rAntiImpl.PostUserEvent( LINK( this, TableControl_Impl, OnUpdateScrollbars ) );
2350 :
2351 0 : return (TableSize)( m_nTopRow - nOldTopRow );
2352 : }
2353 :
2354 : //--------------------------------------------------------------------
2355 0 : TableSize TableControl_Impl::impl_scrollRows( TableSize const i_rowDelta )
2356 : {
2357 : DBG_CHECK_ME();
2358 0 : return impl_ni_ScrollRows( i_rowDelta );
2359 : }
2360 :
2361 : //--------------------------------------------------------------------
2362 0 : TableSize TableControl_Impl::impl_ni_ScrollColumns( TableSize _nColumnDelta )
2363 : {
2364 : // compute new left column
2365 : const ColPos nNewLeftColumn =
2366 : ::std::max(
2367 0 : ::std::min( (ColPos)( m_nLeftColumn + _nColumnDelta ), (ColPos)( m_nColumnCount - 1 ) ),
2368 : (ColPos)0
2369 0 : );
2370 :
2371 0 : const ColPos nOldLeftColumn = m_nLeftColumn;
2372 0 : m_nLeftColumn = nNewLeftColumn;
2373 :
2374 : // if updates are enabled currently, scroll the viewport
2375 0 : if ( m_nLeftColumn != nOldLeftColumn )
2376 : {
2377 : DBG_SUSPEND_INV( INV_SCROLL_POSITION );
2378 0 : SuppressCursor aHideCursor( *this );
2379 : // TODO: call a onStartScroll at our listener (or better an own onStartScroll,
2380 : // which hides the cursor and then calls the listener)
2381 : // Same for onEndScroll
2382 :
2383 : // scroll the view port, if possible
2384 0 : const Rectangle aDataArea( Point( m_nRowHeaderWidthPixel, 0 ), m_pDataWindow->GetOutputSizePixel() );
2385 :
2386 : long nPixelDelta =
2387 0 : m_aColumnWidths[ nOldLeftColumn ].getStart()
2388 0 : - m_aColumnWidths[ m_nLeftColumn ].getStart();
2389 :
2390 : // update our column positions
2391 : // Do this *before* scrolling, as SCROLL_UPDATE will trigger a paint, which already needs the correct
2392 : // information in m_aColumnWidths
2393 0 : for ( ColumnPositions::iterator colPos = m_aColumnWidths.begin();
2394 0 : colPos != m_aColumnWidths.end();
2395 : ++colPos
2396 : )
2397 : {
2398 0 : colPos->move( nPixelDelta );
2399 : }
2400 :
2401 : // scroll the window content (if supported and possible), or invalidate the complete window
2402 0 : if ( m_pDataWindow->GetBackground().IsScrollable()
2403 0 : && abs( nPixelDelta ) < aDataArea.GetWidth()
2404 : )
2405 : {
2406 0 : m_pDataWindow->Scroll( nPixelDelta, 0, aDataArea, SCROLL_CLIP | SCROLL_UPDATE );
2407 : }
2408 : else
2409 0 : m_pDataWindow->Invalidate( INVALIDATE_UPDATE );
2410 :
2411 : // update the position at the horizontal scrollbar
2412 0 : if ( m_pHScroll != NULL )
2413 0 : m_pHScroll->SetThumbPos( m_nLeftColumn );
2414 : }
2415 :
2416 : // The scroll bar availaility might change when we scrolled. This is because we do not hide
2417 : // the scrollbar when it is, in theory, unnecessary, but currently at a position > 0. In this case, it will
2418 : // be auto-hidden when it's scrolled back to pos 0.
2419 0 : if ( m_nLeftColumn == 0 )
2420 0 : m_rAntiImpl.PostUserEvent( LINK( this, TableControl_Impl, OnUpdateScrollbars ) );
2421 :
2422 0 : return (TableSize)( m_nLeftColumn - nOldLeftColumn );
2423 : }
2424 :
2425 : //------------------------------------------------------------------------------------------------------------------
2426 0 : TableSize TableControl_Impl::impl_scrollColumns( TableSize const i_columnDelta )
2427 : {
2428 : DBG_CHECK_ME();
2429 0 : return impl_ni_ScrollColumns( i_columnDelta );
2430 : }
2431 :
2432 : //------------------------------------------------------------------------------------------------------------------
2433 0 : SelectionEngine* TableControl_Impl::getSelEngine()
2434 : {
2435 0 : return m_pSelEngine;
2436 : }
2437 :
2438 : //------------------------------------------------------------------------------------------------------------------
2439 0 : ScrollBar* TableControl_Impl::getHorzScrollbar()
2440 : {
2441 0 : return m_pHScroll;
2442 : }
2443 :
2444 : //------------------------------------------------------------------------------------------------------------------
2445 0 : ScrollBar* TableControl_Impl::getVertScrollbar()
2446 : {
2447 0 : return m_pVScroll;
2448 : }
2449 :
2450 : //------------------------------------------------------------------------------------------------------------------
2451 0 : bool TableControl_Impl::isRowSelected( RowPos i_row ) const
2452 : {
2453 0 : return ::std::find( m_aSelectedRows.begin(), m_aSelectedRows.end(), i_row ) != m_aSelectedRows.end();
2454 : }
2455 :
2456 : //------------------------------------------------------------------------------------------------------------------
2457 0 : RowPos TableControl_Impl::getSelectedRowIndex( size_t const i_selectionIndex ) const
2458 : {
2459 0 : if ( i_selectionIndex < m_aSelectedRows.size() )
2460 0 : return m_aSelectedRows[ i_selectionIndex ];
2461 0 : return ROW_INVALID;
2462 : }
2463 :
2464 : //------------------------------------------------------------------------------------------------------------------
2465 0 : int TableControl_Impl::getRowSelectedNumber(const ::std::vector<RowPos>& selectedRows, RowPos current)
2466 : {
2467 0 : std::vector<RowPos>::const_iterator it = ::std::find(selectedRows.begin(),selectedRows.end(),current);
2468 0 : if ( it != selectedRows.end() )
2469 : {
2470 0 : return it - selectedRows.begin();
2471 : }
2472 0 : return -1;
2473 : }
2474 :
2475 : //--------------------------------------------------------------------
2476 0 : ColPos TableControl_Impl::impl_getColumnForOrdinate( long const i_ordinate ) const
2477 : {
2478 : DBG_CHECK_ME();
2479 :
2480 0 : if ( ( m_aColumnWidths.empty() ) || ( i_ordinate < 0 ) )
2481 0 : return COL_INVALID;
2482 :
2483 0 : if ( i_ordinate < m_nRowHeaderWidthPixel )
2484 0 : return COL_ROW_HEADERS;
2485 :
2486 : ColumnPositions::const_iterator lowerBound = ::std::lower_bound(
2487 : m_aColumnWidths.begin(),
2488 : m_aColumnWidths.end(),
2489 : MutableColumnMetrics(i_ordinate+1, i_ordinate+1),
2490 : ColumnInfoPositionLess()
2491 0 : );
2492 0 : if ( lowerBound == m_aColumnWidths.end() )
2493 : {
2494 : // point is *behind* the start of the last column ...
2495 0 : if ( i_ordinate < m_aColumnWidths.rbegin()->getEnd() )
2496 : // ... but still before its end
2497 0 : return m_nColumnCount - 1;
2498 0 : return COL_INVALID;
2499 : }
2500 0 : return lowerBound - m_aColumnWidths.begin();
2501 : }
2502 :
2503 : //--------------------------------------------------------------------
2504 0 : RowPos TableControl_Impl::impl_getRowForAbscissa( long const i_abscissa ) const
2505 : {
2506 : DBG_CHECK_ME();
2507 :
2508 0 : if ( i_abscissa < 0 )
2509 0 : return ROW_INVALID;
2510 :
2511 0 : if ( i_abscissa < m_nColHeaderHeightPixel )
2512 0 : return ROW_COL_HEADERS;
2513 :
2514 0 : long const abscissa = i_abscissa - m_nColHeaderHeightPixel;
2515 0 : long const row = m_nTopRow + abscissa / m_nRowHeightPixel;
2516 0 : return row < m_pModel->getRowCount() ? row : ROW_INVALID;
2517 : }
2518 :
2519 : //--------------------------------------------------------------------
2520 0 : bool TableControl_Impl::markRowAsDeselected( RowPos const i_rowIndex )
2521 : {
2522 : DBG_CHECK_ME();
2523 :
2524 0 : ::std::vector< RowPos >::iterator selPos = ::std::find( m_aSelectedRows.begin(), m_aSelectedRows.end(), i_rowIndex );
2525 0 : if ( selPos == m_aSelectedRows.end() )
2526 0 : return false;
2527 :
2528 0 : m_aSelectedRows.erase( selPos );
2529 0 : return true;
2530 : }
2531 :
2532 : //--------------------------------------------------------------------
2533 0 : bool TableControl_Impl::markRowAsSelected( RowPos const i_rowIndex )
2534 : {
2535 : DBG_CHECK_ME();
2536 :
2537 0 : if ( isRowSelected( i_rowIndex ) )
2538 0 : return false;
2539 :
2540 0 : SelectionMode const eSelMode = getSelEngine()->GetSelectionMode();
2541 0 : switch ( eSelMode )
2542 : {
2543 : case SINGLE_SELECTION:
2544 0 : if ( !m_aSelectedRows.empty() )
2545 : {
2546 : OSL_ENSURE( m_aSelectedRows.size() == 1, "TableControl::markRowAsSelected: SingleSelection with more than one selected element?" );
2547 0 : m_aSelectedRows[0] = i_rowIndex;
2548 0 : break;
2549 : }
2550 : // fall through
2551 :
2552 : case MULTIPLE_SELECTION:
2553 0 : m_aSelectedRows.push_back( i_rowIndex );
2554 0 : break;
2555 :
2556 : default:
2557 : OSL_ENSURE( false, "TableControl_Impl::markRowAsSelected: unsupported selection mode!" );
2558 0 : return false;
2559 : }
2560 :
2561 0 : return true;
2562 : }
2563 :
2564 : //--------------------------------------------------------------------
2565 0 : bool TableControl_Impl::markAllRowsAsDeselected()
2566 : {
2567 0 : if ( m_aSelectedRows.empty() )
2568 0 : return false;
2569 :
2570 0 : m_aSelectedRows.clear();
2571 0 : return true;
2572 : }
2573 :
2574 : //--------------------------------------------------------------------
2575 0 : bool TableControl_Impl::markAllRowsAsSelected()
2576 : {
2577 : DBG_CHECK_ME();
2578 :
2579 0 : SelectionMode const eSelMode = getSelEngine()->GetSelectionMode();
2580 0 : ENSURE_OR_RETURN_FALSE( eSelMode == MULTIPLE_SELECTION, "TableControl_Impl::markAllRowsAsSelected: unsupported selection mode!" );
2581 :
2582 0 : if ( m_aSelectedRows.size() == size_t( m_pModel->getRowCount() ) )
2583 : {
2584 : #if OSL_DEBUG_LEVEL > 0
2585 : for ( TableSize row = 0; row < m_pModel->getRowCount(); ++row )
2586 : {
2587 : OSL_ENSURE( isRowSelected( row ), "TableControl_Impl::markAllRowsAsSelected: inconsistency in the selected rows!" );
2588 : }
2589 : #endif
2590 : // already all rows marked as selected
2591 0 : return false;
2592 : }
2593 :
2594 0 : m_aSelectedRows.clear();
2595 0 : for ( RowPos i=0; i < m_pModel->getRowCount(); ++i )
2596 0 : m_aSelectedRows.push_back(i);
2597 :
2598 0 : return true;
2599 : }
2600 :
2601 : //--------------------------------------------------------------------
2602 0 : void TableControl_Impl::commitAccessibleEvent( sal_Int16 const i_eventID, const Any& i_newValue, const Any& i_oldValue )
2603 : {
2604 0 : impl_commitAccessibleEvent( i_eventID, i_newValue, i_oldValue );
2605 0 : }
2606 :
2607 : //--------------------------------------------------------------------
2608 0 : void TableControl_Impl::commitCellEvent( sal_Int16 const i_eventID, const Any& i_newValue, const Any& i_oldValue )
2609 : {
2610 : DBG_CHECK_ME();
2611 0 : if ( impl_isAccessibleAlive() )
2612 0 : m_pAccessibleTable->commitCellEvent( i_eventID, i_newValue, i_oldValue );
2613 0 : }
2614 :
2615 : //--------------------------------------------------------------------
2616 0 : void TableControl_Impl::commitTableEvent( sal_Int16 const i_eventID, const Any& i_newValue, const Any& i_oldValue )
2617 : {
2618 : DBG_CHECK_ME();
2619 0 : if ( impl_isAccessibleAlive() )
2620 0 : m_pAccessibleTable->commitTableEvent( i_eventID, i_newValue, i_oldValue );
2621 0 : }
2622 :
2623 : //--------------------------------------------------------------------
2624 0 : Rectangle TableControl_Impl::calcHeaderRect(bool bColHeader)
2625 : {
2626 0 : Rectangle const aRectTableWithHeaders( impl_getAllVisibleCellsArea() );
2627 0 : Size const aSizeTableWithHeaders( aRectTableWithHeaders.GetSize() );
2628 0 : if ( bColHeader )
2629 0 : return Rectangle( aRectTableWithHeaders.TopLeft(), Size( aSizeTableWithHeaders.Width(), m_nColHeaderHeightPixel ) );
2630 : else
2631 0 : return Rectangle( aRectTableWithHeaders.TopLeft(), Size( m_nRowHeaderWidthPixel, aSizeTableWithHeaders.Height() ) );
2632 : }
2633 :
2634 : //--------------------------------------------------------------------
2635 0 : Rectangle TableControl_Impl::calcHeaderCellRect( bool bColHeader, sal_Int32 nPos )
2636 : {
2637 0 : Rectangle const aHeaderRect = calcHeaderRect( bColHeader );
2638 : TableCellGeometry const aGeometry(
2639 : *this, aHeaderRect,
2640 : bColHeader ? nPos : COL_ROW_HEADERS,
2641 : bColHeader ? ROW_COL_HEADERS : nPos
2642 0 : );
2643 0 : return aGeometry.getRect();
2644 : }
2645 :
2646 : //--------------------------------------------------------------------
2647 0 : Rectangle TableControl_Impl::calcTableRect()
2648 : {
2649 0 : return impl_getAllVisibleDataCellArea();
2650 : }
2651 :
2652 : //--------------------------------------------------------------------
2653 0 : Rectangle TableControl_Impl::calcCellRect( sal_Int32 nRow, sal_Int32 nCol )
2654 : {
2655 0 : Rectangle aCellRect;
2656 0 : impl_getCellRect( nRow, nCol, aCellRect );
2657 0 : return aCellRect;
2658 : }
2659 :
2660 : //--------------------------------------------------------------------
2661 0 : IMPL_LINK( TableControl_Impl, OnUpdateScrollbars, void*, /**/ )
2662 : {
2663 : DBG_CHECK_ME();
2664 : // TODO: can't we simply use lcl_updateScrollbar here, so the scrollbars ranges are updated, instead of
2665 : // doing a complete re-layout?
2666 0 : impl_ni_relayout();
2667 0 : return 1L;
2668 : }
2669 :
2670 : //--------------------------------------------------------------------
2671 0 : IMPL_LINK( TableControl_Impl, OnScroll, ScrollBar*, _pScrollbar )
2672 : {
2673 : DBG_ASSERT( ( _pScrollbar == m_pVScroll ) || ( _pScrollbar == m_pHScroll ),
2674 : "TableControl_Impl::OnScroll: where did this come from?" );
2675 :
2676 0 : if ( _pScrollbar == m_pVScroll )
2677 0 : impl_ni_ScrollRows( _pScrollbar->GetDelta() );
2678 : else
2679 0 : impl_ni_ScrollColumns( _pScrollbar->GetDelta() );
2680 :
2681 0 : return 0L;
2682 : }
2683 :
2684 : //------------------------------------------------------------------------------------------------------------------
2685 0 : Reference< XAccessible > TableControl_Impl::getAccessible( Window& i_parentWindow )
2686 : {
2687 : DBG_TESTSOLARMUTEX();
2688 0 : if ( m_pAccessibleTable == NULL )
2689 : {
2690 0 : Reference< XAccessible > const xAccParent = i_parentWindow.GetAccessible();
2691 0 : if ( xAccParent.is() )
2692 : {
2693 0 : m_pAccessibleTable = m_aFactoryAccess.getFactory().createAccessibleTableControl(
2694 : xAccParent, m_rAntiImpl
2695 0 : );
2696 0 : }
2697 : }
2698 :
2699 0 : Reference< XAccessible > xAccessible;
2700 0 : if ( m_pAccessibleTable )
2701 0 : xAccessible = m_pAccessibleTable->getMyself();
2702 0 : return xAccessible;
2703 : }
2704 :
2705 : //------------------------------------------------------------------------------------------------------------------
2706 0 : void TableControl_Impl::disposeAccessible()
2707 : {
2708 0 : if ( m_pAccessibleTable )
2709 0 : m_pAccessibleTable->dispose();
2710 0 : m_pAccessibleTable = NULL;
2711 0 : }
2712 :
2713 : //------------------------------------------------------------------------------------------------------------------
2714 0 : bool TableControl_Impl::impl_isAccessibleAlive() const
2715 : {
2716 : DBG_CHECK_ME();
2717 0 : return ( NULL != m_pAccessibleTable ) && m_pAccessibleTable->isAlive();
2718 : }
2719 :
2720 : //------------------------------------------------------------------------------------------------------------------
2721 0 : void TableControl_Impl::impl_commitAccessibleEvent( sal_Int16 const i_eventID, Any const & i_newValue, Any const & i_oldValue )
2722 : {
2723 : DBG_CHECK_ME();
2724 0 : if ( impl_isAccessibleAlive() )
2725 0 : m_pAccessibleTable->commitEvent( i_eventID, i_newValue, i_oldValue );
2726 0 : }
2727 :
2728 : //==================================================================================================================
2729 : //= TableFunctionSet
2730 : //==================================================================================================================
2731 : //------------------------------------------------------------------------------------------------------------------
2732 0 : TableFunctionSet::TableFunctionSet(TableControl_Impl* _pTableControl)
2733 : :m_pTableControl( _pTableControl)
2734 0 : ,m_nCurrentRow( ROW_INVALID )
2735 : {
2736 0 : }
2737 : //------------------------------------------------------------------------------------------------------------------
2738 0 : TableFunctionSet::~TableFunctionSet()
2739 : {
2740 0 : }
2741 : //------------------------------------------------------------------------------------------------------------------
2742 0 : void TableFunctionSet::BeginDrag()
2743 : {
2744 0 : }
2745 : //------------------------------------------------------------------------------------------------------------------
2746 0 : void TableFunctionSet::CreateAnchor()
2747 : {
2748 0 : m_pTableControl->setAnchor( m_pTableControl->getCurRow() );
2749 0 : }
2750 :
2751 : //------------------------------------------------------------------------------------------------------------------
2752 0 : void TableFunctionSet::DestroyAnchor()
2753 : {
2754 0 : m_pTableControl->setAnchor( ROW_INVALID );
2755 0 : }
2756 :
2757 : //------------------------------------------------------------------------------------------------------------------
2758 0 : sal_Bool TableFunctionSet::SetCursorAtPoint(const Point& rPoint, sal_Bool bDontSelectAtCursor)
2759 : {
2760 0 : sal_Bool bHandled = sal_False;
2761 : // newRow is the row which includes the point, getCurRow() is the last selected row, before the mouse click
2762 0 : RowPos newRow = m_pTableControl->getRowAtPoint( rPoint );
2763 0 : if ( newRow == ROW_COL_HEADERS )
2764 0 : newRow = m_pTableControl->getTopRow();
2765 :
2766 0 : ColPos newCol = m_pTableControl->getColAtPoint( rPoint );
2767 0 : if ( newCol == COL_ROW_HEADERS )
2768 0 : newCol = m_pTableControl->getLeftColumn();
2769 :
2770 0 : if ( ( newRow == ROW_INVALID ) || ( newCol == COL_INVALID ) )
2771 0 : return sal_False;
2772 :
2773 0 : if ( bDontSelectAtCursor )
2774 : {
2775 0 : if ( m_pTableControl->getSelectedRowCount() > 1 )
2776 0 : m_pTableControl->getSelEngine()->AddAlways(sal_True);
2777 0 : bHandled = sal_True;
2778 : }
2779 0 : else if ( m_pTableControl->getAnchor() == m_pTableControl->getCurRow() )
2780 : {
2781 : //selecting region,
2782 0 : int diff = m_pTableControl->getCurRow() - newRow;
2783 : //selected region lies above the last selection
2784 0 : if( diff >= 0)
2785 : {
2786 : //put selected rows in vector
2787 0 : while ( m_pTableControl->getAnchor() >= newRow )
2788 : {
2789 0 : m_pTableControl->markRowAsSelected( m_pTableControl->getAnchor() );
2790 0 : m_pTableControl->setAnchor( m_pTableControl->getAnchor() - 1 );
2791 0 : diff--;
2792 : }
2793 0 : m_pTableControl->setAnchor( m_pTableControl->getAnchor() + 1 );
2794 : }
2795 : //selected region lies beneath the last selected row
2796 : else
2797 : {
2798 0 : while ( m_pTableControl->getAnchor() <= newRow )
2799 : {
2800 0 : m_pTableControl->markRowAsSelected( m_pTableControl->getAnchor() );
2801 0 : m_pTableControl->setAnchor( m_pTableControl->getAnchor() + 1 );
2802 0 : diff++;
2803 : }
2804 0 : m_pTableControl->setAnchor( m_pTableControl->getAnchor() - 1 );
2805 : }
2806 0 : m_pTableControl->invalidateSelectedRegion( m_pTableControl->getCurRow(), newRow );
2807 0 : bHandled = sal_True;
2808 : }
2809 : //no region selected
2810 : else
2811 : {
2812 0 : if ( !m_pTableControl->hasRowSelection() )
2813 0 : m_pTableControl->markRowAsSelected( newRow );
2814 : else
2815 : {
2816 0 : if ( m_pTableControl->getSelEngine()->GetSelectionMode() == SINGLE_SELECTION )
2817 : {
2818 0 : DeselectAll();
2819 0 : m_pTableControl->markRowAsSelected( newRow );
2820 : }
2821 : else
2822 : {
2823 0 : m_pTableControl->markRowAsSelected( newRow );
2824 : }
2825 : }
2826 0 : if ( m_pTableControl->getSelectedRowCount() > 1 && m_pTableControl->getSelEngine()->GetSelectionMode() != SINGLE_SELECTION )
2827 0 : m_pTableControl->getSelEngine()->AddAlways(sal_True);
2828 :
2829 0 : m_pTableControl->invalidateRow( newRow );
2830 0 : bHandled = sal_True;
2831 : }
2832 0 : m_pTableControl->goTo( newCol, newRow );
2833 0 : return bHandled;
2834 : }
2835 : //------------------------------------------------------------------------------------------------------------------
2836 0 : sal_Bool TableFunctionSet::IsSelectionAtPoint( const Point& rPoint )
2837 : {
2838 0 : m_pTableControl->getSelEngine()->AddAlways(sal_False);
2839 0 : if ( !m_pTableControl->hasRowSelection() )
2840 0 : return sal_False;
2841 : else
2842 : {
2843 0 : RowPos curRow = m_pTableControl->getRowAtPoint( rPoint );
2844 0 : m_pTableControl->setAnchor( ROW_INVALID );
2845 0 : bool selected = m_pTableControl->isRowSelected( curRow );
2846 0 : m_nCurrentRow = curRow;
2847 0 : return selected;
2848 : }
2849 : }
2850 : //------------------------------------------------------------------------------------------------------------------
2851 0 : void TableFunctionSet::DeselectAtPoint( const Point& rPoint )
2852 : {
2853 : (void)rPoint;
2854 0 : m_pTableControl->invalidateRow( m_nCurrentRow );
2855 0 : m_pTableControl->markRowAsDeselected( m_nCurrentRow );
2856 0 : }
2857 :
2858 : //------------------------------------------------------------------------------------------------------------------
2859 0 : void TableFunctionSet::DeselectAll()
2860 : {
2861 0 : if ( m_pTableControl->hasRowSelection() )
2862 : {
2863 0 : for ( size_t i=0; i<m_pTableControl->getSelectedRowCount(); ++i )
2864 : {
2865 0 : RowPos const rowIndex = m_pTableControl->getSelectedRowIndex(i);
2866 0 : m_pTableControl->invalidateRow( rowIndex );
2867 : }
2868 :
2869 0 : m_pTableControl->markAllRowsAsDeselected();
2870 : }
2871 0 : }
2872 :
2873 : //......................................................................................................................
2874 108 : } } // namespace svt::table
2875 : //......................................................................................................................
2876 :
2877 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|