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