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