LibreOffice Module toolkit (master) 1
tablecontrol_impl.cxx
Go to the documentation of this file.
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
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
37#include <vcl/seleng.hxx>
38#include <vcl/settings.hxx>
39#include <vcl/image.hxx>
41#include <tools/debug.hxx>
42
43#include <cstdlib>
44#include <numeric>
45
46#define MIN_COLUMN_WIDTH_PIXEL 4
47
48
49namespace svt::table
50{
51
52
53 using ::com::sun::star::accessibility::AccessibleTableModelChange;
54 using ::com::sun::star::uno::Any;
55 using ::com::sun::star::accessibility::XAccessible;
56 using ::com::sun::star::uno::Reference;
57
58 namespace AccessibleEventId = ::com::sun::star::accessibility::AccessibleEventId;
59 namespace AccessibleTableModelChangeType = ::com::sun::star::accessibility::AccessibleTableModelChangeType;
60
61
62 //= SuppressCursor
63
64 namespace {
65
66 class SuppressCursor
67 {
68 private:
69 ITableControl& m_rTable;
70
71 public:
72 explicit SuppressCursor( ITableControl& _rTable )
73 :m_rTable( _rTable )
74 {
75 m_rTable.hideCursor();
76 }
77 ~SuppressCursor()
78 {
79 m_rTable.showCursor();
80 }
81 };
82
83
84 //= EmptyTableModel
85
92 class EmptyTableModel : public ITableModel
93 {
94 public:
95 EmptyTableModel()
96 {
97 }
98
99 // ITableModel overridables
100 virtual TableSize getColumnCount() const override
101 {
102 return 0;
103 }
104 virtual TableSize getRowCount() const override
105 {
106 return 0;
107 }
108 virtual bool hasColumnHeaders() const override
109 {
110 return false;
111 }
112 virtual bool hasRowHeaders() const override
113 {
114 return false;
115 }
116 virtual PColumnModel getColumnModel( ColPos ) override
117 {
118 OSL_FAIL( "EmptyTableModel::getColumnModel: invalid call!" );
119 return PColumnModel();
120 }
121 virtual PTableRenderer getRenderer() const override
122 {
123 return PTableRenderer();
124 }
125 virtual PTableInputHandler getInputHandler() const override
126 {
127 return PTableInputHandler();
128 }
129 virtual TableMetrics getRowHeight() const override
130 {
131 return 5 * 100;
132 }
133 virtual TableMetrics getColumnHeaderHeight() const override
134 {
135 return 0;
136 }
137 virtual TableMetrics getRowHeaderWidth() const override
138 {
139 return 0;
140 }
141 virtual ScrollbarVisibility getVerticalScrollbarVisibility() const override
142 {
143 return ScrollbarShowNever;
144 }
145 virtual ScrollbarVisibility getHorizontalScrollbarVisibility() const override
146 {
147 return ScrollbarShowNever;
148 }
149 virtual void addTableModelListener( const PTableModelListener& ) override {}
150 virtual void removeTableModelListener( const PTableModelListener& ) override {}
151 virtual ::std::optional< ::Color > getLineColor() const override
152 {
153 return ::std::optional< ::Color >();
154 }
155 virtual ::std::optional< ::Color > getHeaderBackgroundColor() const override
156 {
157 return ::std::optional< ::Color >();
158 }
159 virtual ::std::optional< ::Color > getHeaderTextColor() const override
160 {
161 return ::std::optional< ::Color >();
162 }
163 virtual ::std::optional< ::Color > getActiveSelectionBackColor() const override
164 {
165 return ::std::optional< ::Color >();
166 }
167 virtual ::std::optional< ::Color > getInactiveSelectionBackColor() const override
168 {
169 return ::std::optional< ::Color >();
170 }
171 virtual ::std::optional< ::Color > getActiveSelectionTextColor() const override
172 {
173 return ::std::optional< ::Color >();
174 }
175 virtual ::std::optional< ::Color > getInactiveSelectionTextColor() const override
176 {
177 return ::std::optional< ::Color >();
178 }
179 virtual ::std::optional< ::Color > getTextColor() const override
180 {
181 return ::std::optional< ::Color >();
182 }
183 virtual ::std::optional< ::Color > getTextLineColor() const override
184 {
185 return ::std::optional< ::Color >();
186 }
187 virtual ::std::optional< ::std::vector< ::Color > > getRowBackgroundColors() const override
188 {
189 return ::std::optional< ::std::vector< ::Color > >();
190 }
191 virtual css::style::VerticalAlignment getVerticalAlign() const override
192 {
193 return css::style::VerticalAlignment(0);
194 }
195 virtual ITableDataSort* getSortAdapter() override
196 {
197 return nullptr;
198 }
199 virtual bool isEnabled() const override
200 {
201 return true;
202 }
203 virtual void getCellContent( ColPos const, RowPos const, css::uno::Any& o_cellContent ) override
204 {
205 o_cellContent.clear();
206 }
207 virtual void getCellToolTip( ColPos const, RowPos const, css::uno::Any& ) override
208 {
209 }
210 virtual Any getRowHeading( RowPos const ) const override
211 {
212 return Any();
213 }
214 };
215
216 }
217
219 :m_rAntiImpl ( _rAntiImpl )
220 ,m_pModel ( std::make_shared<EmptyTableModel>() )
221 ,m_pInputHandler ( )
222 ,m_nRowHeightPixel ( 15 )
223 ,m_nColHeaderHeightPixel( 0 )
224 ,m_nRowHeaderWidthPixel ( 0 )
225 ,m_nColumnCount ( 0 )
226 ,m_nRowCount ( 0 )
227 ,m_nCurColumn ( COL_INVALID )
228 ,m_nCurRow ( ROW_INVALID )
229 ,m_nLeftColumn ( 0 )
230 ,m_nTopRow ( 0 )
231 ,m_nCursorHidden ( 1 )
232 ,m_pDataWindow ( VclPtr<TableDataWindow>::Create( *this ) )
233 ,m_pVScroll ( nullptr )
234 ,m_pHScroll ( nullptr )
235 ,m_pScrollCorner ( nullptr )
236 ,m_aSelectedRows ( )
237 ,m_pTableFunctionSet ( new TableFunctionSet( this ) )
238 ,m_nAnchor ( -1 )
239 ,m_bUpdatingColWidths ( false )
240 ,m_pAccessibleTable ( nullptr )
241 {
243 m_pSelEngine->SetSelectionMode(SelectionMode::Single);
244 m_pDataWindow->SetPosPixel( Point( 0, 0 ) );
245 m_pDataWindow->Show();
246 }
247
249 {
253 m_pDataWindow.disposeAndClear();
254 m_pTableFunctionSet.reset();
255 m_pSelEngine.reset();
256 }
257
259 {
260 SuppressCursor aHideCursor( *this );
261
262 if ( m_pModel )
263 m_pModel->removeTableModelListener( shared_from_this() );
264
265 m_pModel = _pModel;
266 if ( !m_pModel)
267 m_pModel = std::make_shared<EmptyTableModel>();
268
269 m_pModel->addTableModelListener( shared_from_this() );
270
273
274 // recalc some model-dependent cached info
277
278 // completely invalidate
280
281 // reset cursor to (0,0)
282 if ( m_nRowCount ) m_nCurRow = 0;
283 if ( m_nColumnCount ) m_nCurColumn = 0;
284 }
285
286
287 namespace
288 {
289 bool lcl_adjustSelectedRows( ::std::vector< RowPos >& io_selectionIndexes, RowPos const i_firstAffectedRowIndex, TableSize const i_offset )
290 {
291 bool didChanges = false;
292 for (auto & selectionIndex : io_selectionIndexes)
293 {
294 if ( selectionIndex < i_firstAffectedRowIndex )
295 continue;
296 selectionIndex += i_offset;
297 didChanges = true;
298 }
299 return didChanges;
300 }
301 }
302
303
305 {
306 OSL_PRECOND( i_last >= i_first, "TableControl_Impl::rowsInserted: invalid row indexes!" );
307
308 TableSize const insertedRows = i_last - i_first + 1;
309
310 // adjust selection, if necessary
311 bool const selectionChanged = lcl_adjustSelectedRows( m_aSelectedRows, i_first, insertedRows );
312
313 // adjust our cached row count
314 m_nRowCount = m_pModel->getRowCount();
315
316 // if the rows have been inserted before the current row, adjust this
317 if ( i_first <= m_nCurRow )
318 goTo( m_nCurColumn, m_nCurRow + insertedRows );
319
320 // relayout, since the scrollbar need might have changed
322
323 // notify A1YY events
325 {
326 impl_commitAccessibleEvent( AccessibleEventId::TABLE_MODEL_CHANGED,
327 Any( AccessibleTableModelChange( AccessibleTableModelChangeType::ROWS_INSERTED, i_first, i_last, -1, -1 ) )
328 );
329 }
330
331 // schedule repaint
333
334 // call selection handlers, if necessary
335 if ( selectionChanged )
337 }
338
339
341 {
342 sal_Int32 firstRemovedRow = i_first;
343 sal_Int32 lastRemovedRow = i_last;
344
345 // adjust selection, if necessary
346 bool selectionChanged = false;
347 if ( i_first == -1 )
348 {
350
351 firstRemovedRow = 0;
352 lastRemovedRow = m_nRowCount - 1;
353 }
354 else
355 {
356 ENSURE_OR_RETURN_VOID( i_last >= i_first, "TableControl_Impl::rowsRemoved: illegal indexes!" );
357
358 for ( sal_Int32 row = i_first; row <= i_last; ++row )
359 {
360 if ( markRowAsDeselected( row ) )
361 selectionChanged = true;
362 }
363
364 if ( lcl_adjustSelectedRows( m_aSelectedRows, i_last + 1, i_first - i_last - 1 ) )
365 selectionChanged = true;
366 }
367
368 // adjust cached row count
369 m_nRowCount = m_pModel->getRowCount();
370
371 // adjust the current row, if it is larger than the row count now
372 if ( m_nCurRow >= m_nRowCount )
373 {
374 if ( m_nRowCount > 0 )
376 else
377 {
379 m_nTopRow = 0;
380 }
381 }
382 else if ( m_nRowCount == 0 )
383 {
384 m_nTopRow = 0;
385 }
386
387
388 // relayout, since the scrollbar need might have changed
390
391 // notify A11Y events
393 {
395 AccessibleEventId::TABLE_MODEL_CHANGED,
396 Any( AccessibleTableModelChange(
397 AccessibleTableModelChangeType::ROWS_REMOVED,
398 firstRemovedRow,
399 lastRemovedRow,
400 -1,
401 -1
402 ) ),
403 Any()
404 );
405 }
406
407 // schedule a repaint
408 invalidateRowRange( firstRemovedRow, ROW_INVALID );
409
410 // call selection handlers, if necessary
411 if ( selectionChanged )
413 }
414
415
417 {
418 m_nColumnCount = m_pModel->getColumnCount();
420
422 }
423
424
426 {
427 m_nColumnCount = m_pModel->getColumnCount();
428
429 // adjust the current column, if it is larger than the column count now
431 {
432 if ( m_nColumnCount > 0 )
434 else
436 }
437
439
441 }
442
443
445 {
446 m_nColumnCount = m_pModel->getColumnCount();
448
450 }
451
452
453 void TableControl_Impl::cellsUpdated( RowPos const i_firstRow, RowPos const i_lastRow )
454 {
455 invalidateRowRange( i_firstRow, i_lastRow );
456 }
457
458
460 {
464 }
465
466
468 {
469 tools::Rectangle const aAllCellsArea( impl_getAllVisibleCellsArea() );
470
471 const TableColumnGeometry aColumn( *this, aAllCellsArea, i_column );
472 if ( aColumn.isValid() )
473 m_rAntiImpl.Invalidate( aColumn.getRect() );
474 }
475
476
477 void TableControl_Impl::columnChanged( ColPos const i_column, ColumnAttributeGroup const i_attributeGroup )
478 {
479 ColumnAttributeGroup nGroup( i_attributeGroup );
481 {
482 impl_invalidateColumn( i_column );
483 nGroup &= ~ColumnAttributeGroup::APPEARANCE;
484 }
485
486 if ( nGroup & ColumnAttributeGroup::WIDTH )
487 {
489 {
490 impl_ni_relayout( i_column );
492 }
493
494 nGroup &= ~ColumnAttributeGroup::WIDTH;
495 }
496
497 OSL_ENSURE( ( nGroup == ColumnAttributeGroup::NONE ) || ( i_attributeGroup == ColumnAttributeGroup::ALL ),
498 "TableControl_Impl::columnChanged: don't know how to handle this change!" );
499 }
500
501
503 {
504 tools::Rectangle aArea( Point( 0, 0 ), Size( 0, 0 ) );
505
506 // determine the right-most border of the last column which is
507 // at least partially visible
509 if ( !m_aColumnWidths.empty() )
510 {
511 // the number of pixels which are scrolled out of the left hand
512 // side of the window
513 const tools::Long nScrolledOutLeft = m_nLeftColumn == 0 ? 0 : m_aColumnWidths[ m_nLeftColumn - 1 ].getEnd();
514
515 ColumnPositions::const_reverse_iterator loop = m_aColumnWidths.rbegin();
516 do
517 {
518 aArea.SetRight(loop->getEnd() - nScrolledOutLeft);
519 ++loop;
520 }
521 while ( ( loop != m_aColumnWidths.rend() )
522 && ( loop->getEnd() - nScrolledOutLeft >= aArea.Right() )
523 );
524 }
525 // so far, aArea.Right() denotes the first pixel *after* the cell area
526 aArea.AdjustRight( -1 );
527
528 // determine the last row which is at least partially visible
529 aArea.SetBottom(
532 - 1 );
533
534 return aArea;
535 }
536
537
539 {
543 return aArea;
544 }
545
546
548 {
549 m_nRowHeightPixel = m_rAntiImpl.LogicToPixel(Size(0, m_pModel->getRowHeight()), MapMode(MapUnit::MapAppFont)).Height();
550
552 if ( m_pModel->hasColumnHeaders() )
553 m_nColHeaderHeightPixel = m_rAntiImpl.LogicToPixel(Size(0, m_pModel->getColumnHeaderHeight()), MapMode(MapUnit::MapAppFont)).Height();
554
556 if ( m_pModel->hasRowHeaders() )
557 m_nRowHeaderWidthPixel = m_rAntiImpl.LogicToPixel(Size(m_pModel->getRowHeaderWidth(), 0), MapMode(MapUnit::MapAppFont)).Width();
558 }
559
560
562 {
563 m_pInputHandler = m_pModel->getInputHandler();
564 if ( !m_pInputHandler )
565 m_pInputHandler = std::make_shared<DefaultInputHandler>();
566
567 m_nColumnCount = m_pModel->getColumnCount();
569 m_nLeftColumn = ( m_nColumnCount > 0 ) ? m_nColumnCount - 1 : 0;
570
571 m_nRowCount = m_pModel->getRowCount();
572 if ( m_nTopRow >= m_nRowCount )
573 m_nTopRow = ( m_nRowCount > 0 ) ? m_nRowCount - 1 : 0;
574
576 }
577
578
579 namespace
580 {
581
583 bool lcl_determineScrollbarNeed( tools::Long const i_position, ScrollbarVisibility const i_visibility,
584 tools::Long const i_availableSpace, tools::Long const i_neededSpace )
585 {
586 if ( i_visibility == ScrollbarShowNever )
587 return false;
588 if ( i_visibility == ScrollbarShowAlways )
589 return true;
590 if ( i_position > 0 )
591 return true;
592 if ( i_availableSpace >= i_neededSpace )
593 return false;
594 return true;
595 }
596
597
598 void lcl_setButtonRepeat( vcl::Window& _rWindow )
599 {
600 AllSettings aSettings = _rWindow.GetSettings();
601 MouseSettings aMouseSettings = aSettings.GetMouseSettings();
602
603 aMouseSettings.SetButtonRepeat( 0 );
604 aSettings.SetMouseSettings( aMouseSettings );
605
606 _rWindow.SetSettings( aSettings, true );
607 }
608
609
610 bool lcl_updateScrollbar( vcl::Window& _rParent, VclPtr<ScrollBar>& _rpBar,
611 bool const i_needBar, tools::Long _nVisibleUnits,
612 tools::Long _nPosition, tools::Long _nRange,
613 bool _bHorizontal, const Link<ScrollBar*,void>& _rScrollHandler )
614 {
615 // do we currently have the scrollbar?
616 bool bHaveBar = _rpBar != nullptr;
617
618 // do we need to correct the scrollbar visibility?
619 if ( bHaveBar && !i_needBar )
620 {
621 if ( _rpBar->IsTracking() )
622 _rpBar->EndTracking();
623 _rpBar.disposeAndClear();
624 }
625 else if ( !bHaveBar && i_needBar )
626 {
628
629 &_rParent,
630 WB_DRAG | ( _bHorizontal ? WB_HSCROLL : WB_VSCROLL )
631 );
632 _rpBar->SetScrollHdl( _rScrollHandler );
633 // get some speed into the scrolling...
634 lcl_setButtonRepeat( *_rpBar );
635 }
636
637 if ( _rpBar )
638 {
639 _rpBar->SetRange( Range( 0, _nRange ) );
640 _rpBar->SetVisibleSize( _nVisibleUnits );
641 _rpBar->SetPageSize( _nVisibleUnits );
642 _rpBar->SetLineSize( 1 );
643 _rpBar->SetThumbPos( _nPosition );
644 _rpBar->Show();
645 }
646
647 return ( bHaveBar != i_needBar );
648 }
649
650
655 TableSize lcl_getRowsFittingInto( tools::Long _nOverallHeight, tools::Long _nRowHeightPixel, bool _bAcceptPartialRow )
656 {
657 return _bAcceptPartialRow
658 ? ( _nOverallHeight + ( _nRowHeightPixel - 1 ) ) / _nRowHeightPixel
659 : _nOverallHeight / _nRowHeightPixel;
660 }
661
662
667 TableSize lcl_getColumnsVisibleWithin( const tools::Rectangle& _rArea, ColPos _nFirstVisibleColumn,
668 const TableControl_Impl& _rControl, bool _bAcceptPartialRow )
669 {
670 TableSize visibleColumns = 0;
671 TableColumnGeometry aColumn( _rControl, _rArea, _nFirstVisibleColumn );
672 while ( aColumn.isValid() )
673 {
674 if ( !_bAcceptPartialRow )
675 if ( aColumn.getRect().Right() > _rArea.Right() )
676 // this column is only partially visible, and this is not allowed
677 break;
678
679 aColumn.moveRight();
680 ++visibleColumns;
681 }
682 return visibleColumns;
683 }
684
685 }
686
687
688 tools::Long TableControl_Impl::impl_ni_calculateColumnWidths( ColPos const i_assumeInflexibleColumnsUpToIncluding,
689 bool const i_assumeVerticalScrollbar, ::std::vector< tools::Long >& o_newColWidthsPixel ) const
690 {
691 // the available horizontal space
692 tools::Long gridWidthPixel = m_rAntiImpl.GetOutputSizePixel().Width();
693 ENSURE_OR_RETURN( !!m_pModel, "TableControl_Impl::impl_ni_calculateColumnWidths: not allowed without a model!", gridWidthPixel );
694 if ( m_pModel->hasRowHeaders() && ( gridWidthPixel != 0 ) )
695 {
696 gridWidthPixel -= m_nRowHeaderWidthPixel;
697 }
698
699 if ( i_assumeVerticalScrollbar && ( m_pModel->getVerticalScrollbarVisibility() != ScrollbarShowNever ) )
700 {
702 gridWidthPixel -= nScrollbarMetrics;
703 }
704
705 // no need to do anything without columns
706 TableSize const colCount = m_pModel->getColumnCount();
707 if ( colCount == 0 )
708 return gridWidthPixel;
709
710 // collect some meta data for our columns:
711 // - their current (pixel) metrics
712 tools::Long accumulatedCurrentWidth = 0;
713 ::std::vector< tools::Long > currentColWidths;
714 currentColWidths.reserve( colCount );
715 typedef ::std::vector< ::std::pair< tools::Long, long > > ColumnLimits;
716 ColumnLimits effectiveColumnLimits;
717 effectiveColumnLimits.reserve( colCount );
718 tools::Long accumulatedMinWidth = 0;
719 tools::Long accumulatedMaxWidth = 0;
720 // - their relative flexibility
721 ::std::vector< ::sal_Int32 > columnFlexibilities;
722 columnFlexibilities.reserve( colCount );
723 tools::Long flexibilityDenominator = 0;
724 size_t flexibleColumnCount = 0;
725 for ( ColPos col = 0; col < colCount; ++col )
726 {
727 PColumnModel const pColumn = m_pModel->getColumnModel( col );
728 ENSURE_OR_THROW( !!pColumn, "invalid column returned by the model!" );
729
730 // current width
731 tools::Long const currentWidth = appFontWidthToPixel( pColumn->getWidth() );
732 currentColWidths.push_back( currentWidth );
733
734 // accumulated width
735 accumulatedCurrentWidth += currentWidth;
736
737 // flexibility
738 ::sal_Int32 flexibility = pColumn->getFlexibility();
739 OSL_ENSURE( flexibility >= 0, "TableControl_Impl::impl_ni_calculateColumnWidths: a column's flexibility should be non-negative." );
740 if ( ( flexibility < 0 ) // normalization
741 || ( !pColumn->isResizable() ) // column not resizable => no auto-resize
742 || ( col <= i_assumeInflexibleColumnsUpToIncluding ) // column shall be treated as inflexible => respect this
743 )
744 flexibility = 0;
745
746 // min/max width
747 tools::Long effectiveMin = currentWidth, effectiveMax = currentWidth;
748 // if the column is not flexible, it will not be asked for min/max, but we assume the current width as limit then
749 if ( flexibility > 0 )
750 {
751 tools::Long const minWidth = appFontWidthToPixel( pColumn->getMinWidth() );
752 if ( minWidth > 0 )
753 effectiveMin = minWidth;
754 else
755 effectiveMin = MIN_COLUMN_WIDTH_PIXEL;
756
757 tools::Long const maxWidth = appFontWidthToPixel( pColumn->getMaxWidth() );
758 OSL_ENSURE( minWidth <= maxWidth, "TableControl_Impl::impl_ni_calculateColumnWidths: pretty undecided 'bout its width limits, this column!" );
759 if ( ( maxWidth > 0 ) && ( maxWidth >= minWidth ) )
760 effectiveMax = maxWidth;
761 else
762 effectiveMax = gridWidthPixel; // TODO: any better guess here?
763
764 if ( effectiveMin == effectiveMax )
765 // if the min and the max are identical, this implies no flexibility at all
766 flexibility = 0;
767 }
768
769 columnFlexibilities.push_back( flexibility );
770 flexibilityDenominator += flexibility;
771 if ( flexibility > 0 )
772 ++flexibleColumnCount;
773
774 effectiveColumnLimits.emplace_back( effectiveMin, effectiveMax );
775 accumulatedMinWidth += effectiveMin;
776 accumulatedMaxWidth += effectiveMax;
777 }
778
779 o_newColWidthsPixel = currentColWidths;
780 if ( flexibilityDenominator == 0 )
781 {
782 // no column is flexible => don't adjust anything
783 }
784 else if ( gridWidthPixel > accumulatedCurrentWidth )
785 { // we have space to give away ...
786 tools::Long distributePixel = gridWidthPixel - accumulatedCurrentWidth;
787 if ( gridWidthPixel > accumulatedMaxWidth )
788 {
789 // ... but the column's maximal widths are still less than we have
790 // => set them all to max
791 for ( svt::table::TableSize i = 0; i < colCount; ++i )
792 {
793 o_newColWidthsPixel[i] = effectiveColumnLimits[i].second;
794 }
795 }
796 else
797 {
798 bool startOver = false;
799 do
800 {
801 startOver = false;
802 // distribute the remaining space amongst all columns with a positive flexibility
803 for ( size_t i=0; i<o_newColWidthsPixel.size() && !startOver; ++i )
804 {
805 tools::Long const columnFlexibility = columnFlexibilities[i];
806 if ( columnFlexibility == 0 )
807 continue;
808
809 tools::Long newColWidth = currentColWidths[i] + columnFlexibility * distributePixel / flexibilityDenominator;
810
811 if ( newColWidth > effectiveColumnLimits[i].second )
812 { // that was too much, we hit the col's maximum
813 // set the new width to exactly this maximum
814 newColWidth = effectiveColumnLimits[i].second;
815 // adjust the flexibility denominator ...
816 flexibilityDenominator -= columnFlexibility;
817 columnFlexibilities[i] = 0;
818 --flexibleColumnCount;
819 // ... and the remaining width ...
820 tools::Long const difference = newColWidth - currentColWidths[i];
821 distributePixel -= difference;
822 // ... this way, we ensure that the width not taken up by this column is consumed by the other
823 // flexible ones (if there are some)
824
825 // and start over with the first column, since there might be earlier columns which need
826 // to be recalculated now
827 startOver = true;
828 }
829
830 o_newColWidthsPixel[i] = newColWidth;
831 }
832 }
833 while ( startOver );
834
835 // are there pixels left (might be caused by rounding errors)?
836 distributePixel = gridWidthPixel - ::std::accumulate( o_newColWidthsPixel.begin(), o_newColWidthsPixel.end(), 0 );
837 while ( ( distributePixel > 0 ) && ( flexibleColumnCount > 0 ) )
838 {
839 // yes => ignore relative flexibilities, and subsequently distribute single pixels to all flexible
840 // columns which did not yet reach their maximum.
841 for ( size_t i=0; ( i < o_newColWidthsPixel.size() ) && ( distributePixel > 0 ); ++i )
842 {
843 if ( columnFlexibilities[i] == 0 )
844 continue;
845
846 OSL_ENSURE( o_newColWidthsPixel[i] <= effectiveColumnLimits[i].second,
847 "TableControl_Impl::impl_ni_calculateColumnWidths: inconsistency!" );
848 if ( o_newColWidthsPixel[i] >= effectiveColumnLimits[i].first )
849 {
850 columnFlexibilities[i] = 0;
851 --flexibleColumnCount;
852 continue;
853 }
854
855 ++o_newColWidthsPixel[i];
856 --distributePixel;
857 }
858 }
859 }
860 }
861 else if ( gridWidthPixel < accumulatedCurrentWidth )
862 { // we need to take away some space from the columns which allow it ...
863 tools::Long takeAwayPixel = accumulatedCurrentWidth - gridWidthPixel;
864 if ( gridWidthPixel < accumulatedMinWidth )
865 {
866 // ... but the column's minimal widths are still more than we have
867 // => set them all to min
868 for ( svt::table::TableSize i = 0; i < colCount; ++i )
869 {
870 o_newColWidthsPixel[i] = effectiveColumnLimits[i].first;
871 }
872 }
873 else
874 {
875 bool startOver = false;
876 do
877 {
878 startOver = false;
879 // take away the space we need from the columns with a positive flexibility
880 for ( size_t i=0; i<o_newColWidthsPixel.size() && !startOver; ++i )
881 {
882 tools::Long const columnFlexibility = columnFlexibilities[i];
883 if ( columnFlexibility == 0 )
884 continue;
885
886 tools::Long newColWidth = currentColWidths[i] - columnFlexibility * takeAwayPixel / flexibilityDenominator;
887
888 if ( newColWidth < effectiveColumnLimits[i].first )
889 { // that was too much, we hit the col's minimum
890 // set the new width to exactly this minimum
891 newColWidth = effectiveColumnLimits[i].first;
892 // adjust the flexibility denominator ...
893 flexibilityDenominator -= columnFlexibility;
894 columnFlexibilities[i] = 0;
895 --flexibleColumnCount;
896 // ... and the remaining width ...
897 tools::Long const difference = currentColWidths[i] - newColWidth;
898 takeAwayPixel -= difference;
899
900 // and start over with the first column, since there might be earlier columns which need
901 // to be recalculated now
902 startOver = true;
903 }
904
905 o_newColWidthsPixel[i] = newColWidth;
906 }
907 }
908 while ( startOver );
909
910 // are there pixels left (might be caused by rounding errors)?
911 takeAwayPixel = ::std::accumulate( o_newColWidthsPixel.begin(), o_newColWidthsPixel.end(), 0 ) - gridWidthPixel;
912 while ( ( takeAwayPixel > 0 ) && ( flexibleColumnCount > 0 ) )
913 {
914 // yes => ignore relative flexibilities, and subsequently take away pixels from all flexible
915 // columns which did not yet reach their minimum.
916 for ( size_t i=0; ( i < o_newColWidthsPixel.size() ) && ( takeAwayPixel > 0 ); ++i )
917 {
918 if ( columnFlexibilities[i] == 0 )
919 continue;
920
921 OSL_ENSURE( o_newColWidthsPixel[i] >= effectiveColumnLimits[i].first,
922 "TableControl_Impl::impl_ni_calculateColumnWidths: inconsistency!" );
923 if ( o_newColWidthsPixel[i] <= effectiveColumnLimits[i].first )
924 {
925 columnFlexibilities[i] = 0;
926 --flexibleColumnCount;
927 continue;
928 }
929
930 --o_newColWidthsPixel[i];
931 --takeAwayPixel;
932 }
933 }
934 }
935 }
936
937 return gridWidthPixel;
938 }
939
940
941 void TableControl_Impl::impl_ni_relayout( ColPos const i_assumeInflexibleColumnsUpToIncluding )
942 {
943 ENSURE_OR_RETURN_VOID( !m_bUpdatingColWidths, "TableControl_Impl::impl_ni_relayout: recursive call detected!" );
944
945 m_aColumnWidths.resize( 0 );
946 if ( !m_pModel )
947 return;
948
949 ::comphelper::FlagRestorationGuard const aWidthUpdateFlag( m_bUpdatingColWidths, true );
950 SuppressCursor aHideCursor( *this );
951
952 // layouting steps:
953
954 // 1. adjust column widths, leaving space for a vertical scrollbar
955 // 2. determine need for a vertical scrollbar
956 // - V-YES: all fine, result from 1. is still valid
957 // - V-NO: result from 1. is still under consideration
958
959 // 3. determine need for a horizontal scrollbar
960 // - H-NO: all fine, result from 2. is still valid
961 // - H-YES: reconsider need for a vertical scrollbar, if result of 2. was V-NO
962 // - V-YES: all fine, result from 1. is still valid
963 // - V-NO: redistribute the remaining space (if any) amongst all columns which allow it
964
965 ::std::vector< tools::Long > newWidthsPixel;
966 tools::Long gridWidthPixel = impl_ni_calculateColumnWidths( i_assumeInflexibleColumnsUpToIncluding, true, newWidthsPixel );
967
968 // the width/height of a scrollbar, needed several times below
970
971 // determine the playground for the data cells (excluding headers)
972 // TODO: what if the control is smaller than needed for the headers/scrollbars?
973 tools::Rectangle aDataCellPlayground( Point( 0, 0 ), m_rAntiImpl.GetOutputSizePixel() );
974 aDataCellPlayground.SetLeft( m_nRowHeaderWidthPixel );
975 aDataCellPlayground.SetTop( m_nColHeaderHeightPixel );
976
977 OSL_ENSURE( ( m_nRowCount == m_pModel->getRowCount() ) && ( m_nColumnCount == m_pModel->getColumnCount() ),
978 "TableControl_Impl::impl_ni_relayout: how is this expected to work with invalid data?" );
979 tools::Long const nAllColumnsWidth = ::std::accumulate( newWidthsPixel.begin(), newWidthsPixel.end(), 0 );
980
981 ScrollbarVisibility const eVertScrollbar = m_pModel->getVerticalScrollbarVisibility();
982 ScrollbarVisibility const eHorzScrollbar = m_pModel->getHorizontalScrollbarVisibility();
983
984 // do we need a vertical scrollbar?
985 bool bNeedVerticalScrollbar = lcl_determineScrollbarNeed(
986 m_nTopRow, eVertScrollbar, aDataCellPlayground.GetHeight(), m_nRowHeightPixel * m_nRowCount );
987 bool bFirstRoundVScrollNeed = false;
988 if ( bNeedVerticalScrollbar )
989 {
990 aDataCellPlayground.AdjustRight( -nScrollbarMetrics );
991 bFirstRoundVScrollNeed = true;
992 }
993
994 // do we need a horizontal scrollbar?
995 bool const bNeedHorizontalScrollbar = lcl_determineScrollbarNeed(
996 m_nLeftColumn, eHorzScrollbar, aDataCellPlayground.GetWidth(), nAllColumnsWidth );
997 if ( bNeedHorizontalScrollbar )
998 {
999 aDataCellPlayground.AdjustBottom( -nScrollbarMetrics );
1000
1001 // now that we just found that we need a horizontal scrollbar,
1002 // the need for a vertical one may have changed, since the horizontal
1003 // SB might just occupy enough space so that not all rows do fit
1004 // anymore
1005 if ( !bFirstRoundVScrollNeed )
1006 {
1007 bNeedVerticalScrollbar = lcl_determineScrollbarNeed(
1008 m_nTopRow, eVertScrollbar, aDataCellPlayground.GetHeight(), m_nRowHeightPixel * m_nRowCount );
1009 if ( bNeedVerticalScrollbar )
1010 {
1011 aDataCellPlayground.AdjustRight( -nScrollbarMetrics );
1012 }
1013 }
1014 }
1015
1016 // the initial call to impl_ni_calculateColumnWidths assumed that we need a vertical scrollbar. If, by now,
1017 // we know that this is not the case, re-calculate the column widths.
1018 if ( !bNeedVerticalScrollbar )
1019 gridWidthPixel = impl_ni_calculateColumnWidths( i_assumeInflexibleColumnsUpToIncluding, false, newWidthsPixel );
1020
1021 // update the column objects with the new widths we finally calculated
1022 TableSize const colCount = m_pModel->getColumnCount();
1023 m_aColumnWidths.reserve( colCount );
1024 tools::Long accumulatedWidthPixel = m_nRowHeaderWidthPixel;
1025 bool anyColumnWidthChanged = false;
1026 for ( ColPos col = 0; col < colCount; ++col )
1027 {
1028 const tools::Long columnStart = accumulatedWidthPixel;
1029 const tools::Long columnEnd = columnStart + newWidthsPixel[col];
1030 m_aColumnWidths.emplace_back( columnStart, columnEnd );
1031 accumulatedWidthPixel = columnEnd;
1032
1033 // and don't forget to forward this to the column models
1034 PColumnModel const pColumn = m_pModel->getColumnModel( col );
1035 ENSURE_OR_THROW( !!pColumn, "invalid column returned by the model!" );
1036
1037 tools::Long const oldColumnWidthAppFont = pColumn->getWidth();
1038 tools::Long const newColumnWidthAppFont = pixelWidthToAppFont( newWidthsPixel[col] );
1039 pColumn->setWidth( newColumnWidthAppFont );
1040
1041 anyColumnWidthChanged |= ( oldColumnWidthAppFont != newColumnWidthAppFont );
1042 }
1043
1044 // if the column widths changed, ensure everything is repainted
1045 if ( anyColumnWidthChanged )
1047
1048 // if the column resizing happened to leave some space at the right, but there are columns
1049 // scrolled out to the left, scroll them in
1050 while ( ( m_nLeftColumn > 0 )
1051 && ( accumulatedWidthPixel - m_aColumnWidths[ m_nLeftColumn - 1 ].getStart() <= gridWidthPixel )
1052 )
1053 {
1054 --m_nLeftColumn;
1055 }
1056
1057 // now adjust the column metrics, since they currently ignore the horizontal scroll position
1058 if ( m_nLeftColumn > 0 )
1059 {
1060 const tools::Long offsetPixel = m_aColumnWidths[ 0 ].getStart() - m_aColumnWidths[ m_nLeftColumn ].getStart();
1061 for (auto & columnWidth : m_aColumnWidths)
1062 {
1063 columnWidth.move( offsetPixel );
1064 }
1065 }
1066
1067 // show or hide the scrollbars as needed, and position the data window
1068 impl_ni_positionChildWindows( aDataCellPlayground, bNeedVerticalScrollbar, bNeedHorizontalScrollbar );
1069 }
1070
1071
1073 bool const i_verticalScrollbar, bool const i_horizontalScrollbar )
1074 {
1076
1077 // create or destroy the vertical scrollbar, as needed
1078 lcl_updateScrollbar(
1080 m_pVScroll,
1081 i_verticalScrollbar,
1082 lcl_getRowsFittingInto( i_dataCellPlayground.GetHeight(), m_nRowHeightPixel, false ),
1083 // visible units
1084 m_nTopRow, // current position
1085 m_nRowCount, // range
1086 false, // vertical
1087 LINK( this, TableControl_Impl, OnScroll ) // scroll handler
1088 );
1089
1090 // position it
1091 if ( m_pVScroll )
1092 {
1093 tools::Rectangle aScrollbarArea(
1094 Point( i_dataCellPlayground.Right() + 1, 0 ),
1095 Size( nScrollbarMetrics, i_dataCellPlayground.Bottom() + 1 )
1096 );
1097 m_pVScroll->SetPosSizePixel(
1098 aScrollbarArea.TopLeft(), aScrollbarArea.GetSize() );
1099 }
1100
1101 // create or destroy the horizontal scrollbar, as needed
1102 lcl_updateScrollbar(
1104 m_pHScroll,
1105 i_horizontalScrollbar,
1106 lcl_getColumnsVisibleWithin( i_dataCellPlayground, m_nLeftColumn, *this, false ),
1107 // visible units
1108 m_nLeftColumn, // current position
1109 m_nColumnCount, // range
1110 true, // horizontal
1111 LINK( this, TableControl_Impl, OnScroll ) // scroll handler
1112 );
1113
1114 // position it
1115 if ( m_pHScroll )
1116 {
1117 TableSize const nVisibleUnits = lcl_getColumnsVisibleWithin( i_dataCellPlayground, m_nLeftColumn, *this, false );
1118 TableMetrics const nRange = m_nColumnCount;
1119 if( m_nLeftColumn + nVisibleUnits == nRange - 1 )
1120 {
1121 if ( m_aColumnWidths[ nRange - 1 ].getStart() - m_aColumnWidths[ m_nLeftColumn ].getEnd() + m_aColumnWidths[ nRange-1 ].getWidth() > i_dataCellPlayground.GetWidth() )
1122 {
1123 m_pHScroll->SetVisibleSize( nVisibleUnits -1 );
1124 m_pHScroll->SetPageSize( nVisibleUnits - 1 );
1125 }
1126 }
1127 tools::Rectangle aScrollbarArea(
1128 Point( 0, i_dataCellPlayground.Bottom() + 1 ),
1129 Size( i_dataCellPlayground.Right() + 1, nScrollbarMetrics )
1130 );
1131 m_pHScroll->SetPosSizePixel(
1132 aScrollbarArea.TopLeft(), aScrollbarArea.GetSize() );
1133 }
1134
1135 // the corner window connecting the two scrollbars in the lower right corner
1136 bool bHaveScrollCorner = nullptr != m_pScrollCorner;
1137 bool bNeedScrollCorner = ( nullptr != m_pHScroll ) && ( nullptr != m_pVScroll );
1138 if ( bHaveScrollCorner && !bNeedScrollCorner )
1139 {
1141 }
1142 else if ( !bHaveScrollCorner && bNeedScrollCorner )
1143 {
1145 m_pScrollCorner->SetSizePixel( Size( nScrollbarMetrics, nScrollbarMetrics ) );
1146 m_pScrollCorner->SetPosPixel( Point( i_dataCellPlayground.Right() + 1, i_dataCellPlayground.Bottom() + 1 ) );
1147 m_pScrollCorner->Show();
1148 }
1149 else if(bHaveScrollCorner && bNeedScrollCorner)
1150 {
1151 m_pScrollCorner->SetPosPixel( Point( i_dataCellPlayground.Right() + 1, i_dataCellPlayground.Bottom() + 1 ) );
1152 m_pScrollCorner->Show();
1153 }
1154
1155 // resize the data window
1156 m_pDataWindow->SetSizePixel( Size(
1157 i_dataCellPlayground.GetWidth() + m_nRowHeaderWidthPixel,
1158 i_dataCellPlayground.GetHeight() + m_nColHeaderHeightPixel
1159 ) );
1160 }
1161
1162
1164 {
1167 }
1168
1169
1171 {
1172 if (!getModel())
1173 return;
1174 PTableRenderer pRenderer = getModel()->getRenderer();
1175 DBG_ASSERT(!!pRenderer, "TableDataWindow::doPaintContent: invalid renderer!");
1176 if (!pRenderer)
1177 return;
1178
1179 // our current style settings, to be passed to the renderer
1180 const StyleSettings& rStyle = rRenderContext.GetSettings().GetStyleSettings();
1181 m_nRowCount = m_pModel->getRowCount();
1182 // the area occupied by all (at least partially) visible cells, including
1183 // headers
1184 tools::Rectangle const aAllCellsWithHeaders( impl_getAllVisibleCellsArea() );
1185
1186 // draw the header column area
1187 if (m_pModel->hasColumnHeaders())
1188 {
1189 TableRowGeometry const aHeaderRow(*this, tools::Rectangle(Point(0, 0), aAllCellsWithHeaders.BottomRight()), ROW_COL_HEADERS);
1190 tools::Rectangle const aColRect(aHeaderRow.getRect());
1191 pRenderer->PaintHeaderArea(rRenderContext, aColRect, true, false, rStyle);
1192 // Note that strictly, aHeaderRow.getRect() also contains the intersection between column
1193 // and row header area. However, below we go to paint this intersection, again,
1194 // so this hopefully doesn't hurt if we already paint it here.
1195
1196 for (TableCellGeometry aCell(aHeaderRow, m_nLeftColumn); aCell.isValid(); aCell.moveRight())
1197 {
1198 if (_rUpdateRect.GetIntersection(aCell.getRect()).IsEmpty())
1199 continue;
1200
1201 pRenderer->PaintColumnHeader(aCell.getColumn(), rRenderContext, aCell.getRect(), rStyle);
1202 }
1203 }
1204 // the area occupied by the row header, if any
1205 tools::Rectangle aRowHeaderArea;
1206 if (m_pModel->hasRowHeaders())
1207 {
1208 aRowHeaderArea = aAllCellsWithHeaders;
1209 aRowHeaderArea.SetRight( m_nRowHeaderWidthPixel - 1 );
1210
1211 TableSize const nVisibleRows = impl_getVisibleRows(true);
1212 TableSize nActualRows = nVisibleRows;
1213 if (m_nTopRow + nActualRows > m_nRowCount)
1214 nActualRows = m_nRowCount - m_nTopRow;
1215 aRowHeaderArea.SetBottom( m_nColHeaderHeightPixel + m_nRowHeightPixel * nActualRows - 1 );
1216
1217 pRenderer->PaintHeaderArea(rRenderContext, aRowHeaderArea, false, true, rStyle);
1218 // Note that strictly, aRowHeaderArea also contains the intersection between column
1219 // and row header area. However, below we go to paint this intersection, again,
1220 // so this hopefully doesn't hurt if we already paint it here.
1221
1222 if (m_pModel->hasColumnHeaders())
1223 {
1224 TableCellGeometry const aIntersection(*this, tools::Rectangle(Point(0, 0), aAllCellsWithHeaders.BottomRight()),
1226 tools::Rectangle const aInters(aIntersection.getRect());
1227 pRenderer->PaintHeaderArea(rRenderContext, aInters, true, true, rStyle);
1228 }
1229 }
1230
1231 // draw the table content row by row
1232 TableSize colCount = getModel()->getColumnCount();
1233
1234 // paint all rows
1235 tools::Rectangle const aAllDataCellsArea(impl_getAllVisibleDataCellArea());
1236 for (TableRowGeometry aRowIterator(*this, aAllCellsWithHeaders, getTopRow()); aRowIterator.isValid(); aRowIterator.moveDown())
1237 {
1238 if (_rUpdateRect.GetIntersection(aRowIterator.getRect() ).IsEmpty())
1239 continue;
1240
1241 bool const isControlFocused = m_rAntiImpl.HasControlFocus();
1242 bool const isSelectedRow = isRowSelected(aRowIterator.getRow());
1243
1244 tools::Rectangle const aRect = aRowIterator.getRect().GetIntersection(aAllDataCellsArea);
1245
1246 // give the renderer a chance to prepare the row
1247 pRenderer->PrepareRow(aRowIterator.getRow(), isControlFocused, isSelectedRow, rRenderContext, aRect, rStyle);
1248
1249 // paint the row header
1250 if (m_pModel->hasRowHeaders())
1251 {
1252 const tools::Rectangle aCurrentRowHeader(aRowHeaderArea.GetIntersection(aRowIterator.getRect()));
1253 pRenderer->PaintRowHeader(rRenderContext, aCurrentRowHeader, rStyle);
1254 }
1255
1256 if (!colCount)
1257 continue;
1258
1259 // paint all cells in this row
1260 for (TableCellGeometry aCell(aRowIterator, m_nLeftColumn); aCell.isValid(); aCell.moveRight())
1261 {
1262 pRenderer->PaintCell(aCell.getColumn(), isSelectedRow, isControlFocused,
1263 rRenderContext, aCell.getRect(), rStyle);
1264 }
1265 }
1266 }
1267
1269 {
1270 if ( ++m_nCursorHidden == 1 )
1271 impl_ni_doSwitchCursor( false );
1272 }
1273
1274
1276 {
1277 DBG_ASSERT( m_nCursorHidden > 0, "TableControl_Impl::showCursor: cursor not hidden!" );
1278 if ( --m_nCursorHidden == 0 )
1279 impl_ni_doSwitchCursor( true );
1280 }
1281
1282
1284 {
1285 bool bSuccess = false;
1286 bool selectionChanged = false;
1287
1288 switch ( _eAction )
1289 {
1290 case cursorDown:
1291 if ( m_pSelEngine->GetSelectionMode() == SelectionMode::Single )
1292 {
1293 //if other rows already selected, deselect them
1294 if(!m_aSelectedRows.empty())
1295 {
1297 m_aSelectedRows.clear();
1298 }
1299 if ( m_nCurRow < m_nRowCount-1 )
1300 {
1301 ++m_nCurRow;
1302 m_aSelectedRows.push_back(m_nCurRow);
1303 }
1304 else
1305 m_aSelectedRows.push_back(m_nCurRow);
1308 selectionChanged = true;
1309 bSuccess = true;
1310 }
1311 else
1312 {
1313 if ( m_nCurRow < m_nRowCount - 1 )
1314 bSuccess = goTo( m_nCurColumn, m_nCurRow + 1 );
1315 }
1316 break;
1317
1318 case cursorUp:
1319 if(m_pSelEngine->GetSelectionMode() == SelectionMode::Single)
1320 {
1321 if(!m_aSelectedRows.empty())
1322 {
1324 m_aSelectedRows.clear();
1325 }
1326 if(m_nCurRow>0)
1327 {
1328 --m_nCurRow;
1329 m_aSelectedRows.push_back(m_nCurRow);
1331 }
1332 else
1333 {
1334 m_aSelectedRows.push_back(m_nCurRow);
1336 }
1338 selectionChanged = true;
1339 bSuccess = true;
1340 }
1341 else
1342 {
1343 if ( m_nCurRow > 0 )
1344 bSuccess = goTo( m_nCurColumn, m_nCurRow - 1 );
1345 }
1346 break;
1347 case cursorLeft:
1348 if ( m_nCurColumn > 0 )
1349 bSuccess = goTo( m_nCurColumn - 1, m_nCurRow );
1350 else
1351 if ( ( m_nCurColumn == 0) && ( m_nCurRow > 0 ) )
1352 bSuccess = goTo( m_nColumnCount - 1, m_nCurRow - 1 );
1353 break;
1354
1355 case cursorRight:
1356 if ( m_nCurColumn < m_nColumnCount - 1 )
1357 bSuccess = goTo( m_nCurColumn + 1, m_nCurRow );
1358 else
1359 if ( ( m_nCurColumn == m_nColumnCount - 1 ) && ( m_nCurRow < m_nRowCount - 1 ) )
1360 bSuccess = goTo( 0, m_nCurRow + 1 );
1361 break;
1362
1363 case cursorToLineStart:
1364 bSuccess = goTo( 0, m_nCurRow );
1365 break;
1366
1367 case cursorToLineEnd:
1368 bSuccess = goTo( m_nColumnCount - 1, m_nCurRow );
1369 break;
1370
1371 case cursorToFirstLine:
1372 bSuccess = goTo( m_nCurColumn, 0 );
1373 break;
1374
1375 case cursorToLastLine:
1376 bSuccess = goTo( m_nCurColumn, m_nRowCount - 1 );
1377 break;
1378
1379 case cursorPageUp:
1380 {
1381 RowPos nNewRow = ::std::max( RowPos(0), m_nCurRow - impl_getVisibleRows( false ) );
1382 bSuccess = goTo( m_nCurColumn, nNewRow );
1383 }
1384 break;
1385
1386 case cursorPageDown:
1387 {
1388 RowPos nNewRow = ::std::min( m_nRowCount - 1, m_nCurRow + impl_getVisibleRows( false ) );
1389 bSuccess = goTo( m_nCurColumn, nNewRow );
1390 }
1391 break;
1392
1393 case cursorTopLeft:
1394 bSuccess = goTo( 0, 0 );
1395 break;
1396
1397 case cursorBottomRight:
1398 bSuccess = goTo( m_nColumnCount - 1, m_nRowCount - 1 );
1399 break;
1400
1401 case cursorSelectRow:
1402 {
1403 if(m_pSelEngine->GetSelectionMode() == SelectionMode::NONE)
1404 return false;
1405 //pos is the position of the current row in the vector of selected rows, if current row is selected
1407 //if current row is selected, it should be deselected, when ALT+SPACE are pressed
1408 if(pos>-1)
1409 {
1410 m_aSelectedRows.erase(m_aSelectedRows.begin()+pos);
1411 if(m_aSelectedRows.empty() && m_nAnchor != -1)
1412 m_nAnchor = -1;
1413 }
1414 //else select the row->put it in the vector
1415 else
1416 m_aSelectedRows.push_back(m_nCurRow);
1418 selectionChanged = true;
1419 bSuccess = true;
1420 }
1421 break;
1422 case cursorSelectRowUp:
1423 {
1424 if(m_pSelEngine->GetSelectionMode() == SelectionMode::NONE)
1425 return false;
1426 else if(m_pSelEngine->GetSelectionMode() == SelectionMode::Single)
1427 {
1428 //if there are other selected rows, deselect them
1429 return false;
1430 }
1431 else
1432 {
1433 //there are other selected rows
1434 if(!m_aSelectedRows.empty())
1435 {
1436 //the anchor wasn't set -> a region is not selected, that's why clear all selection
1437 //and select the current row
1438 if(m_nAnchor==-1)
1439 {
1441 m_aSelectedRows.clear();
1442 m_aSelectedRows.push_back(m_nCurRow);
1444 }
1445 else
1446 {
1447 //a region is already selected, prevRow is last selected row and the row above - nextRow - should be selected
1450 if(prevRow>-1)
1451 {
1452 //if m_nCurRow isn't the upper one, can move up, otherwise not
1453 if(m_nCurRow>0)
1454 m_nCurRow--;
1455 else
1456 return true;
1457 //if nextRow already selected, deselect it, otherwise select it
1458 if(nextRow>-1 && m_aSelectedRows[nextRow] == m_nCurRow)
1459 {
1460 m_aSelectedRows.erase(m_aSelectedRows.begin()+prevRow);
1461 invalidateRow( m_nCurRow + 1 );
1462 }
1463 else
1464 {
1465 m_aSelectedRows.push_back(m_nCurRow);
1467 }
1468 }
1469 else
1470 {
1471 if(m_nCurRow>0)
1472 {
1473 m_aSelectedRows.push_back(m_nCurRow);
1474 m_nCurRow--;
1475 m_aSelectedRows.push_back(m_nCurRow);
1477 }
1478 }
1479 }
1480 }
1481 else
1482 {
1483 //if nothing is selected and the current row isn't the upper one
1484 //select the current and one row above
1485 //otherwise select only the upper row
1486 if(m_nCurRow>0)
1487 {
1488 m_aSelectedRows.push_back(m_nCurRow);
1489 m_nCurRow--;
1490 m_aSelectedRows.push_back(m_nCurRow);
1492 }
1493 else
1494 {
1495 m_aSelectedRows.push_back(m_nCurRow);
1497 }
1498 }
1499 m_pSelEngine->SetAnchor(true);
1502 selectionChanged = true;
1503 bSuccess = true;
1504 }
1505 }
1506 break;
1508 {
1509 if(m_pSelEngine->GetSelectionMode() == SelectionMode::NONE)
1510 bSuccess = false;
1511 else if(m_pSelEngine->GetSelectionMode() == SelectionMode::Single)
1512 {
1513 bSuccess = false;
1514 }
1515 else
1516 {
1517 if(!m_aSelectedRows.empty())
1518 {
1519 //the anchor wasn't set -> a region is not selected, that's why clear all selection
1520 //and select the current row
1521 if(m_nAnchor==-1)
1522 {
1524 m_aSelectedRows.clear();
1525 m_aSelectedRows.push_back(m_nCurRow);
1527 }
1528 else
1529 {
1530 //a region is already selected, prevRow is last selected row and the row beneath - nextRow - should be selected
1533 if(prevRow>-1)
1534 {
1535 //if m_nCurRow isn't the last one, can move down, otherwise not
1537 m_nCurRow++;
1538 else
1539 return true;
1540 //if next row already selected, deselect it, otherwise select it
1541 if(nextRow>-1 && m_aSelectedRows[nextRow] == m_nCurRow)
1542 {
1543 m_aSelectedRows.erase(m_aSelectedRows.begin()+prevRow);
1544 invalidateRow( m_nCurRow - 1 );
1545 }
1546 else
1547 {
1548 m_aSelectedRows.push_back(m_nCurRow);
1550 }
1551 }
1552 else
1553 {
1555 {
1556 m_aSelectedRows.push_back(m_nCurRow);
1557 m_nCurRow++;
1558 m_aSelectedRows.push_back(m_nCurRow);
1560 }
1561 }
1562 }
1563 }
1564 else
1565 {
1566 //there wasn't any selection, select current and row beneath, otherwise only row beneath
1568 {
1569 m_aSelectedRows.push_back(m_nCurRow);
1570 m_nCurRow++;
1571 m_aSelectedRows.push_back(m_nCurRow);
1573 }
1574 else
1575 {
1576 m_aSelectedRows.push_back(m_nCurRow);
1578 }
1579 }
1580 m_pSelEngine->SetAnchor(true);
1583 selectionChanged = true;
1584 bSuccess = true;
1585 }
1586 }
1587 break;
1588
1590 {
1591 if(m_pSelEngine->GetSelectionMode() == SelectionMode::NONE)
1592 bSuccess = false;
1593 else if(m_pSelEngine->GetSelectionMode() == SelectionMode::Single)
1594 bSuccess = false;
1595 else
1596 {
1597 //select the region between the current and the upper row
1598 RowPos iter = m_nCurRow;
1600 //put the rows in vector
1601 while(iter>=0)
1602 {
1603 if ( !isRowSelected( iter ) )
1604 m_aSelectedRows.push_back(iter);
1605 --iter;
1606 }
1607 m_nCurRow = 0;
1609 m_pSelEngine->SetAnchor(true);
1611 selectionChanged = true;
1612 bSuccess = true;
1613 }
1614 }
1615 break;
1616
1618 {
1619 if(m_pSelEngine->GetSelectionMode() == SelectionMode::NONE)
1620 return false;
1621 else if(m_pSelEngine->GetSelectionMode() == SelectionMode::Single)
1622 return false;
1623 //select the region between the current and the last row
1624 RowPos iter = m_nCurRow;
1626 //put the rows in the vector
1627 while(iter<=m_nRowCount)
1628 {
1629 if ( !isRowSelected( iter ) )
1630 m_aSelectedRows.push_back(iter);
1631 ++iter;
1632 }
1635 m_pSelEngine->SetAnchor(true);
1637 selectionChanged = true;
1638 bSuccess = true;
1639 }
1640 break;
1641 default:
1642 OSL_FAIL( "TableControl_Impl::dispatchAction: unsupported action!" );
1643 break;
1644 }
1645
1646 if ( bSuccess && selectionChanged )
1647 {
1649 }
1650
1651 return bSuccess;
1652 }
1653
1654
1656 {
1657 PTableRenderer pRenderer = m_pModel ? m_pModel->getRenderer() : PTableRenderer();
1658 if ( pRenderer )
1659 {
1660 tools::Rectangle aCellRect;
1662 if ( _bShow )
1663 pRenderer->ShowCellCursor( *m_pDataWindow, aCellRect );
1664 else
1665 pRenderer->HideCellCursor( *m_pDataWindow );
1666 }
1667 }
1668
1669
1670 void TableControl_Impl::impl_getCellRect( ColPos _nColumn, RowPos _nRow, tools::Rectangle& _rCellRect ) const
1671 {
1672 if ( !m_pModel
1673 || ( COL_INVALID == _nColumn )
1674 || ( ROW_INVALID == _nRow )
1675 )
1676 {
1677 _rCellRect.SetEmpty();
1678 return;
1679 }
1680
1681 TableCellGeometry aCell( *this, impl_getAllVisibleCellsArea(), _nColumn, _nRow );
1682 _rCellRect = aCell.getRect();
1683 }
1684
1685
1687 {
1688 return impl_getRowForAbscissa( rPoint.Y() );
1689 }
1690
1691
1693 {
1694 return impl_getColumnForOrdinate( rPoint.X() );
1695 }
1696
1697
1699 {
1700 TableCell aCell( getColAtPoint( i_point ), getRowAtPoint( i_point ) );
1701 if ( aCell.nColumn > COL_ROW_HEADERS )
1702 {
1703 PColumnModel const pColumn = m_pModel->getColumnModel( aCell.nColumn );
1704 MutableColumnMetrics const & rColInfo( m_aColumnWidths[ aCell.nColumn ] );
1705 if ( ( rColInfo.getEnd() - 3 <= i_point.X() )
1706 && ( rColInfo.getEnd() >= i_point.X() )
1707 && pColumn->isResizable()
1708 )
1709 {
1710 aCell.eArea = ColumnDivider;
1711 }
1712 }
1713 return aCell;
1714 }
1715
1716
1718 {
1719 ENSURE_OR_RETURN( ( i_column >= 0 ) && ( i_column < m_pModel->getColumnCount() ),
1720 "TableControl_Impl::getColumnMetrics: illegal column index!", ColumnMetrics() );
1721 return m_aColumnWidths[ i_column ];
1722 }
1723
1724
1726 {
1727 return m_pModel;
1728 }
1729
1730
1732 {
1733 return m_nCurColumn;
1734 }
1735
1736
1738 {
1739 return m_nCurRow;
1740 }
1741
1742
1744 {
1745 return m_pDataWindow->GetOutputSizePixel();
1746 }
1747
1748
1750 {
1751 m_pDataWindow->SetPointer( i_pointer );
1752 }
1753
1754
1756 {
1757 m_pDataWindow->CaptureMouse();
1758 }
1759
1760
1762 {
1763 m_pDataWindow->ReleaseMouse();
1764 }
1765
1766
1768 {
1769 switch ( i_what )
1770 {
1772 m_pDataWindow->Invalidate( calcHeaderRect( true ) );
1773 break;
1774
1776 m_pDataWindow->Invalidate( calcHeaderRect( false ) );
1777 break;
1778
1779 case TableArea::All:
1780 m_pDataWindow->Invalidate();
1781 m_pDataWindow->GetParent()->Invalidate( InvalidateFlags::Transparent );
1782 break;
1783 }
1784 }
1785
1786
1788 {
1789 return m_pDataWindow->PixelToLogic(Size(i_pixels, 0), MapMode(MapUnit::MapAppFont)).Width();
1790 }
1791
1792
1794 {
1795 return m_pDataWindow->LogicToPixel(Size(i_appFontUnits, 0), MapMode(MapUnit::MapAppFont)).Width();
1796 }
1797
1798
1800 {
1801 m_pDataWindow->HideTracking();
1802 }
1803
1804
1805 void TableControl_Impl::showTracking( tools::Rectangle const & i_location, ShowTrackFlags const i_flags )
1806 {
1807 m_pDataWindow->ShowTracking( i_location, i_flags );
1808 }
1809
1810
1811 void TableControl_Impl::activateCell( ColPos const i_col, RowPos const i_row )
1812 {
1813 goTo( i_col, i_row );
1814 }
1815
1816
1818 {
1819 // get the visible area of the table control and set the Left and right border of the region to be repainted
1820 tools::Rectangle const aAllCells( impl_getAllVisibleCellsArea() );
1821
1822 tools::Rectangle aInvalidateRect;
1823 aInvalidateRect.SetLeft( aAllCells.Left() );
1824 aInvalidateRect.SetRight( aAllCells.Right() );
1825 // if only one row is selected
1826 if ( _nPrevRow == _nCurRow )
1827 {
1828 tools::Rectangle aCellRect;
1829 impl_getCellRect( m_nCurColumn, _nCurRow, aCellRect );
1830 aInvalidateRect.SetTop( aCellRect.Top() );
1831 aInvalidateRect.SetBottom( aCellRect.Bottom() );
1832 }
1833 //if the region is above the current row
1834 else if(_nPrevRow < _nCurRow )
1835 {
1836 tools::Rectangle aCellRect;
1837 impl_getCellRect( m_nCurColumn, _nPrevRow, aCellRect );
1838 aInvalidateRect.SetTop( aCellRect.Top() );
1839 impl_getCellRect( m_nCurColumn, _nCurRow, aCellRect );
1840 aInvalidateRect.SetBottom( aCellRect.Bottom() );
1841 }
1842 //if the region is beneath the current row
1843 else
1844 {
1845 tools::Rectangle aCellRect;
1846 impl_getCellRect( m_nCurColumn, _nCurRow, aCellRect );
1847 aInvalidateRect.SetTop( aCellRect.Top() );
1848 impl_getCellRect( m_nCurColumn, _nPrevRow, aCellRect );
1849 aInvalidateRect.SetBottom( aCellRect.Bottom() );
1850 }
1851
1852 invalidateRect(aInvalidateRect);
1853 }
1854
1856 {
1857 m_pDataWindow->Invalidate( rInvalidateRect,
1858 m_pDataWindow->GetControlBackground().IsTransparent() ? InvalidateFlags::Transparent : InvalidateFlags::NONE );
1859 }
1860
1861
1863 {
1864 for (auto const& selectedRow : m_aSelectedRows)
1865 {
1866 invalidateRow(selectedRow);
1867 }
1868 }
1869
1870
1871 void TableControl_Impl::invalidateRowRange( RowPos const i_firstRow, RowPos const i_lastRow )
1872 {
1873 RowPos const firstRow = i_firstRow < m_nTopRow ? m_nTopRow : i_firstRow;
1874 RowPos const lastVisibleRow = m_nTopRow + impl_getVisibleRows( true ) - 1;
1875 RowPos const lastRow = ( ( i_lastRow == ROW_INVALID ) || ( i_lastRow > lastVisibleRow ) ) ? lastVisibleRow : i_lastRow;
1876
1877 tools::Rectangle aInvalidateRect;
1878
1879 tools::Rectangle const aVisibleCellsArea( impl_getAllVisibleCellsArea() );
1880 TableRowGeometry aRow( *this, aVisibleCellsArea, firstRow, true );
1881 while ( aRow.isValid() && ( aRow.getRow() <= lastRow ) )
1882 {
1883 aInvalidateRect.Union( aRow.getRect() );
1884 aRow.moveDown();
1885 }
1886
1887 if ( i_lastRow == ROW_INVALID )
1888 aInvalidateRect.SetBottom( m_pDataWindow->GetOutputSizePixel().Height() );
1889
1890 invalidateRect(aInvalidateRect);
1891 }
1892
1893
1895 {
1896
1897 TableSize nVisibleRows = impl_getVisibleRows(true);
1898 TableSize nVisibleCols = impl_getVisibleColumns(true);
1899 if ( ( m_nTopRow + nVisibleRows > m_nRowCount )
1900 && ( m_nRowCount >= nVisibleRows )
1901 )
1902 {
1903 --m_nTopRow;
1904 }
1905 else
1906 {
1907 m_nTopRow = 0;
1908 }
1909
1910 if ( ( m_nLeftColumn + nVisibleCols > m_nColumnCount )
1911 && ( m_nColumnCount >= nVisibleCols )
1912 )
1913 {
1914 --m_nLeftColumn;
1915 }
1916 else
1917 {
1918 m_nLeftColumn = 0;
1919 }
1920
1921 m_pDataWindow->Invalidate();
1922 }
1923
1924
1925 TableSize TableControl_Impl::impl_getVisibleRows( bool _bAcceptPartialRow ) const
1926 {
1927 DBG_ASSERT( m_pDataWindow, "TableControl_Impl::impl_getVisibleRows: no data window!" );
1928
1929 return lcl_getRowsFittingInto(
1930 m_pDataWindow->GetOutputSizePixel().Height() - m_nColHeaderHeightPixel,
1932 _bAcceptPartialRow
1933 );
1934 }
1935
1936
1938 {
1939 DBG_ASSERT( m_pDataWindow, "TableControl_Impl::impl_getVisibleColumns: no data window!" );
1940
1941 return lcl_getColumnsVisibleWithin(
1942 tools::Rectangle( Point( 0, 0 ), m_pDataWindow->GetOutputSizePixel() ),
1944 *this,
1945 _bAcceptPartialCol
1946 );
1947 }
1948
1949
1950 bool TableControl_Impl::goTo( ColPos _nColumn, RowPos _nRow )
1951 {
1952 // TODO: give veto listeners a chance
1953
1954 if ( ( _nColumn < 0 ) || ( _nColumn >= m_nColumnCount )
1955 || ( _nRow < 0 ) || ( _nRow >= m_nRowCount )
1956 )
1957 {
1958 OSL_ENSURE( false, "TableControl_Impl::goTo: invalid row or column index!" );
1959 return false;
1960 }
1961
1962 SuppressCursor aHideCursor( *this );
1963 m_nCurColumn = _nColumn;
1964 m_nCurRow = _nRow;
1965
1966 // ensure that the new cell is visible
1968 return true;
1969 }
1970
1971
1973 {
1974 DBG_ASSERT( ( _nColumn >= 0 ) && ( _nColumn < m_nColumnCount )
1975 && ( _nRow >= 0 ) && ( _nRow < m_nRowCount ),
1976 "TableControl_Impl::ensureVisible: invalid coordinates!" );
1977
1978 SuppressCursor aHideCursor( *this );
1979
1980 if ( _nColumn < m_nLeftColumn )
1981 impl_scrollColumns( _nColumn - m_nLeftColumn );
1982 else
1983 {
1984 TableSize nVisibleColumns = impl_getVisibleColumns( false/*bAcceptPartialVisibility*/ );
1985 if ( _nColumn > m_nLeftColumn + nVisibleColumns - 1 )
1986 {
1987 impl_scrollColumns( _nColumn - ( m_nLeftColumn + nVisibleColumns - 1 ) );
1988 // TODO: since not all columns have the same width, this might in theory result
1989 // in the column still not being visible.
1990 }
1991 }
1992
1993 if ( _nRow < m_nTopRow )
1994 impl_scrollRows( _nRow - m_nTopRow );
1995 else
1996 {
1997 TableSize nVisibleRows = impl_getVisibleRows( false/*_bAcceptPartialVisibility*/ );
1998 if ( _nRow > m_nTopRow + nVisibleRows - 1 )
1999 impl_scrollRows( _nRow - ( m_nTopRow + nVisibleRows - 1 ) );
2000 }
2001 }
2002
2003
2004 OUString TableControl_Impl::getCellContentAsString( RowPos const i_row, ColPos const i_col )
2005 {
2006 Any aCellValue;
2007 m_pModel->getCellContent( i_col, i_row, aCellValue );
2008
2009 OUString sCellStringContent;
2010 m_pModel->getRenderer()->GetFormattedCellString( aCellValue, sCellStringContent );
2011
2012 return sCellStringContent;
2013 }
2014
2015
2017 {
2018 // compute new top row
2019 RowPos nNewTopRow =
2020 ::std::max(
2021 ::std::min( static_cast<RowPos>( m_nTopRow + _nRowDelta ), static_cast<RowPos>( m_nRowCount - 1 ) ),
2022 RowPos(0)
2023 );
2024
2025 RowPos nOldTopRow = m_nTopRow;
2026 m_nTopRow = nNewTopRow;
2027
2028 // if updates are enabled currently, scroll the viewport
2029 if ( m_nTopRow != nOldTopRow )
2030 {
2031 SuppressCursor aHideCursor( *this );
2032 // TODO: call an onStartScroll at our listener (or better an own onStartScroll,
2033 // which hides the cursor and then calls the listener)
2034 // Same for onEndScroll
2035
2036 // scroll the view port, if possible
2037 tools::Long nPixelDelta = m_nRowHeightPixel * ( m_nTopRow - nOldTopRow );
2038
2039 tools::Rectangle aDataArea( Point( 0, m_nColHeaderHeightPixel ), m_pDataWindow->GetOutputSizePixel() );
2040
2041 if ( m_pDataWindow->GetBackground().IsScrollable()
2042 && std::abs( nPixelDelta ) < aDataArea.GetHeight()
2043 )
2044 {
2045 m_pDataWindow->Scroll( 0, static_cast<tools::Long>(-nPixelDelta), aDataArea, ScrollFlags::Clip | ScrollFlags::Update | ScrollFlags::Children);
2046 }
2047 else
2048 {
2049 m_pDataWindow->Invalidate( InvalidateFlags::Update );
2050 m_pDataWindow->GetParent()->Invalidate( InvalidateFlags::Transparent );
2051 }
2052
2053 // update the position at the vertical scrollbar
2054 if ( m_pVScroll != nullptr )
2055 m_pVScroll->SetThumbPos( m_nTopRow );
2056 }
2057
2058 // The scroll bar availability might change when we scrolled.
2059 // For instance, imagine a view with 10 rows, if which 5 fit into the window, numbered 1 to 10.
2060 // Now let
2061 // - the user scroll to row number 6, so the last 5 rows are visible
2062 // - somebody remove the last 4 rows
2063 // - the user scroll to row number 5 being the top row, so the last two rows are visible
2064 // - somebody remove row number 6
2065 // - the user scroll to row number 1
2066 // => in this case, the need for the scrollbar vanishes immediately.
2067 if ( m_nTopRow == 0 )
2068 m_rAntiImpl.PostUserEvent( LINK( this, TableControl_Impl, OnUpdateScrollbars ) );
2069
2070 return static_cast<TableSize>( m_nTopRow - nOldTopRow );
2071 }
2072
2073
2075 {
2076 return impl_ni_ScrollRows( i_rowDelta );
2077 }
2078
2079
2081 {
2082 // compute new left column
2083 const ColPos nNewLeftColumn =
2084 ::std::max(
2085 ::std::min( static_cast<ColPos>( m_nLeftColumn + _nColumnDelta ), static_cast<ColPos>( m_nColumnCount - 1 ) ),
2086 ColPos(0)
2087 );
2088
2089 const ColPos nOldLeftColumn = m_nLeftColumn;
2090 m_nLeftColumn = nNewLeftColumn;
2091
2092 // if updates are enabled currently, scroll the viewport
2093 if ( m_nLeftColumn != nOldLeftColumn )
2094 {
2095 SuppressCursor aHideCursor( *this );
2096 // TODO: call an onStartScroll at our listener (or better an own onStartScroll,
2097 // which hides the cursor and then calls the listener)
2098 // Same for onEndScroll
2099
2100 // scroll the view port, if possible
2101 const tools::Rectangle aDataArea( Point( m_nRowHeaderWidthPixel, 0 ), m_pDataWindow->GetOutputSizePixel() );
2102
2103 tools::Long nPixelDelta =
2104 m_aColumnWidths[ nOldLeftColumn ].getStart()
2105 - m_aColumnWidths[ m_nLeftColumn ].getStart();
2106
2107 // update our column positions
2108 // Do this *before* scrolling, as ScrollFlags::Update will trigger a paint, which already needs the correct
2109 // information in m_aColumnWidths
2110 for (auto & columnWidth : m_aColumnWidths)
2111 {
2112 columnWidth.move(nPixelDelta);
2113 }
2114
2115 // scroll the window content (if supported and possible), or invalidate the complete window
2116 if ( m_pDataWindow->GetBackground().IsScrollable()
2117 && std::abs( nPixelDelta ) < aDataArea.GetWidth()
2118 )
2119 {
2120 m_pDataWindow->Scroll( nPixelDelta, 0, aDataArea, ScrollFlags::Clip | ScrollFlags::Update );
2121 }
2122 else
2123 {
2124 m_pDataWindow->Invalidate( InvalidateFlags::Update );
2125 m_pDataWindow->GetParent()->Invalidate( InvalidateFlags::Transparent );
2126 }
2127
2128 // update the position at the horizontal scrollbar
2129 if ( m_pHScroll != nullptr )
2130 m_pHScroll->SetThumbPos( m_nLeftColumn );
2131 }
2132
2133 // The scroll bar availability might change when we scrolled. This is because we do not hide
2134 // the scrollbar when it is, in theory, unnecessary, but currently at a position > 0. In this case, it will
2135 // be auto-hidden when it's scrolled back to pos 0.
2136 if ( m_nLeftColumn == 0 )
2137 m_rAntiImpl.PostUserEvent( LINK( this, TableControl_Impl, OnUpdateScrollbars ) );
2138
2139 return static_cast<TableSize>( m_nLeftColumn - nOldLeftColumn );
2140 }
2141
2142
2144 {
2145 return impl_ni_ScrollColumns( i_columnDelta );
2146 }
2147
2148
2150 {
2151 return m_pSelEngine.get();
2152 }
2153
2155 {
2156 return ::std::find( m_aSelectedRows.begin(), m_aSelectedRows.end(), i_row ) != m_aSelectedRows.end();
2157 }
2158
2159
2160 RowPos TableControl_Impl::getSelectedRowIndex( size_t const i_selectionIndex ) const
2161 {
2162 if ( i_selectionIndex < m_aSelectedRows.size() )
2163 return m_aSelectedRows[ i_selectionIndex ];
2164 return ROW_INVALID;
2165 }
2166
2167
2168 int TableControl_Impl::getRowSelectedNumber(const ::std::vector<RowPos>& selectedRows, RowPos current)
2169 {
2170 std::vector<RowPos>::const_iterator it = ::std::find(selectedRows.begin(),selectedRows.end(),current);
2171 if ( it != selectedRows.end() )
2172 {
2173 return it - selectedRows.begin();
2174 }
2175 return -1;
2176 }
2177
2178
2180 {
2181 if ( ( m_aColumnWidths.empty() ) || ( i_ordinate < 0 ) )
2182 return COL_INVALID;
2183
2184 if ( i_ordinate < m_nRowHeaderWidthPixel )
2185 return COL_ROW_HEADERS;
2186
2187 ColumnPositions::const_iterator lowerBound = ::std::lower_bound(
2188 m_aColumnWidths.begin(),
2189 m_aColumnWidths.end(),
2190 MutableColumnMetrics(i_ordinate+1, i_ordinate+1),
2192 );
2193 if ( lowerBound == m_aColumnWidths.end() )
2194 {
2195 // point is *behind* the start of the last column ...
2196 if ( i_ordinate < m_aColumnWidths.rbegin()->getEnd() )
2197 // ... but still before its end
2198 return m_nColumnCount - 1;
2199 return COL_INVALID;
2200 }
2201 return lowerBound - m_aColumnWidths.begin();
2202 }
2203
2204
2206 {
2207 if ( i_abscissa < 0 )
2208 return ROW_INVALID;
2209
2210 if ( i_abscissa < m_nColHeaderHeightPixel )
2211 return ROW_COL_HEADERS;
2212
2213 tools::Long const abscissa = i_abscissa - m_nColHeaderHeightPixel;
2214 tools::Long const row = m_nTopRow + abscissa / m_nRowHeightPixel;
2215 return row < m_pModel->getRowCount() ? row : ROW_INVALID;
2216 }
2217
2218
2220 {
2221 ::std::vector< RowPos >::iterator selPos = ::std::find( m_aSelectedRows.begin(), m_aSelectedRows.end(), i_rowIndex );
2222 if ( selPos == m_aSelectedRows.end() )
2223 return false;
2224
2225 m_aSelectedRows.erase( selPos );
2226 return true;
2227 }
2228
2229
2231 {
2232 if ( isRowSelected( i_rowIndex ) )
2233 return false;
2234
2235 SelectionMode const eSelMode = getSelEngine()->GetSelectionMode();
2236 switch ( eSelMode )
2237 {
2238 case SelectionMode::Single:
2239 if ( !m_aSelectedRows.empty() )
2240 {
2241 OSL_ENSURE( m_aSelectedRows.size() == 1, "TableControl::markRowAsSelected: SingleSelection with more than one selected element?" );
2242 m_aSelectedRows[0] = i_rowIndex;
2243 break;
2244 }
2245 [[fallthrough]];
2246
2247 case SelectionMode::Multiple:
2248 m_aSelectedRows.push_back( i_rowIndex );
2249 break;
2250
2251 default:
2252 OSL_ENSURE( false, "TableControl_Impl::markRowAsSelected: unsupported selection mode!" );
2253 return false;
2254 }
2255
2256 return true;
2257 }
2258
2259
2261 {
2262 if ( m_aSelectedRows.empty() )
2263 return false;
2264
2265 m_aSelectedRows.clear();
2266 return true;
2267 }
2268
2269
2271 {
2272 SelectionMode const eSelMode = getSelEngine()->GetSelectionMode();
2273 ENSURE_OR_RETURN_FALSE( eSelMode == SelectionMode::Multiple, "TableControl_Impl::markAllRowsAsSelected: unsupported selection mode!" );
2274
2275 if ( m_aSelectedRows.size() == size_t( m_pModel->getRowCount() ) )
2276 {
2277 #if OSL_DEBUG_LEVEL > 0
2278 for ( TableSize row = 0; row < m_pModel->getRowCount(); ++row )
2279 {
2280 OSL_ENSURE( isRowSelected( row ), "TableControl_Impl::markAllRowsAsSelected: inconsistency in the selected rows!" );
2281 }
2282 #endif
2283 // already all rows marked as selected
2284 return false;
2285 }
2286
2287 m_aSelectedRows.clear();
2288 for ( RowPos i=0; i < m_pModel->getRowCount(); ++i )
2289 m_aSelectedRows.push_back(i);
2290
2291 return true;
2292 }
2293
2294
2295 void TableControl_Impl::commitAccessibleEvent( sal_Int16 const i_eventID )
2296 {
2297 impl_commitAccessibleEvent( i_eventID, Any() );
2298 }
2299
2300
2301 void TableControl_Impl::commitCellEvent( sal_Int16 const i_eventID, const Any& i_newValue, const Any& i_oldValue )
2302 {
2303 if ( impl_isAccessibleAlive() )
2304 m_pAccessibleTable->commitCellEvent( i_eventID, i_newValue, i_oldValue );
2305 }
2306
2307
2308 void TableControl_Impl::commitTableEvent( sal_Int16 const i_eventID, const Any& i_newValue, const Any& i_oldValue )
2309 {
2310 if ( impl_isAccessibleAlive() )
2311 m_pAccessibleTable->commitTableEvent( i_eventID, i_newValue, i_oldValue );
2312 }
2313
2314
2316 {
2317 tools::Rectangle const aRectTableWithHeaders( impl_getAllVisibleCellsArea() );
2318 Size const aSizeTableWithHeaders( aRectTableWithHeaders.GetSize() );
2319 if ( bColHeader )
2320 return tools::Rectangle( aRectTableWithHeaders.TopLeft(), Size( aSizeTableWithHeaders.Width(), m_nColHeaderHeightPixel ) );
2321 else
2322 return tools::Rectangle( aRectTableWithHeaders.TopLeft(), Size( m_nRowHeaderWidthPixel, aSizeTableWithHeaders.Height() ) );
2323 }
2324
2325
2327 {
2328 tools::Rectangle const aHeaderRect = calcHeaderRect( bColHeader );
2329 TableCellGeometry const aGeometry(
2330 *this, aHeaderRect,
2331 bColHeader ? nPos : COL_ROW_HEADERS,
2332 bColHeader ? ROW_COL_HEADERS : nPos
2333 );
2334 return aGeometry.getRect();
2335 }
2336
2337
2339 {
2341 }
2342
2343
2344 tools::Rectangle TableControl_Impl::calcCellRect( sal_Int32 nRow, sal_Int32 nCol ) const
2345 {
2346 tools::Rectangle aCellRect;
2347 impl_getCellRect( nRow, nCol, aCellRect );
2348 return aCellRect;
2349 }
2350
2351
2352 IMPL_LINK_NOARG( TableControl_Impl, OnUpdateScrollbars, void*, void )
2353 {
2354 // TODO: can't we simply use lcl_updateScrollbar here, so the scrollbars ranges are updated, instead of
2355 // doing a complete re-layout?
2356 impl_ni_relayout();
2357 }
2358
2359
2360 IMPL_LINK( TableControl_Impl, OnScroll, ScrollBar*, _pScrollbar, void )
2361 {
2362 DBG_ASSERT( ( _pScrollbar == m_pVScroll ) || ( _pScrollbar == m_pHScroll ),
2363 "TableControl_Impl::OnScroll: where did this come from?" );
2364
2365 if ( _pScrollbar == m_pVScroll )
2366 impl_ni_ScrollRows( _pScrollbar->GetDelta() );
2367 else
2368 impl_ni_ScrollColumns( _pScrollbar->GetDelta() );
2369 }
2370
2371
2372 Reference< XAccessible > TableControl_Impl::getAccessible( vcl::Window& i_parentWindow )
2373 {
2375 if ( m_pAccessibleTable == nullptr )
2376 {
2377 Reference< XAccessible > const xAccParent = i_parentWindow.GetAccessible();
2378 if ( xAccParent.is() )
2379 {
2380 m_pAccessibleTable = m_aFactoryAccess.getFactory().createAccessibleTableControl(
2381 xAccParent, m_rAntiImpl
2382 );
2383 }
2384 }
2385
2386 Reference< XAccessible > xAccessible;
2387 if ( m_pAccessibleTable )
2388 xAccessible = m_pAccessibleTable->getMyself();
2389 return xAccessible;
2390 }
2391
2392
2394 {
2395 if ( m_pAccessibleTable )
2397 m_pAccessibleTable = nullptr;
2398 }
2399
2400
2402 {
2403 return ( nullptr != m_pAccessibleTable ) && m_pAccessibleTable->isAlive();
2404 }
2405
2406
2407 void TableControl_Impl::impl_commitAccessibleEvent( sal_Int16 const i_eventID, Any const & i_newValue )
2408 {
2409 if ( impl_isAccessibleAlive() )
2410 m_pAccessibleTable->commitEvent( i_eventID, i_newValue );
2411 }
2412
2413
2414 //= TableFunctionSet
2415
2416
2418 :m_pTableControl( _pTableControl)
2419 ,m_nCurrentRow( ROW_INVALID )
2420 {
2421 }
2422
2424 {
2425 }
2426
2428 {
2429 }
2430
2432 {
2434 }
2435
2436
2438 {
2440 }
2441
2442
2443 void TableFunctionSet::SetCursorAtPoint(const Point& rPoint, bool bDontSelectAtCursor)
2444 {
2445 // newRow is the row which includes the point, getCurRow() is the last selected row, before the mouse click
2446 RowPos newRow = m_pTableControl->getRowAtPoint( rPoint );
2447 if ( newRow == ROW_COL_HEADERS )
2448 newRow = m_pTableControl->getTopRow();
2449
2450 ColPos newCol = m_pTableControl->getColAtPoint( rPoint );
2451 if ( newCol == COL_ROW_HEADERS )
2452 newCol = m_pTableControl->getLeftColumn();
2453
2454 if ( ( newRow == ROW_INVALID ) || ( newCol == COL_INVALID ) )
2455 return;
2456
2457 if ( bDontSelectAtCursor )
2458 {
2461 }
2463 {
2464 //selected region lies above the last selection
2465 if( m_pTableControl->getCurRow() >= newRow)
2466 {
2467 //put selected rows in vector
2468 while ( m_pTableControl->getAnchor() >= newRow )
2469 {
2472 }
2474 }
2475 //selected region lies beneath the last selected row
2476 else
2477 {
2478 while ( m_pTableControl->getAnchor() <= newRow )
2479 {
2482 }
2484 }
2486 }
2487 //no region selected
2488 else
2489 {
2492 else
2493 {
2494 if ( m_pTableControl->getSelEngine()->GetSelectionMode() == SelectionMode::Single )
2495 {
2496 DeselectAll();
2498 }
2499 else
2500 {
2502 }
2503 }
2504 if ( m_pTableControl->getSelectedRowCount() > 1 && m_pTableControl->getSelEngine()->GetSelectionMode() != SelectionMode::Single )
2506
2507 m_pTableControl->invalidateRow( newRow );
2508 }
2509 m_pTableControl->goTo( newCol, newRow );
2510 }
2511
2513 {
2516 return false;
2517 else
2518 {
2519 RowPos curRow = m_pTableControl->getRowAtPoint( rPoint );
2521 bool selected = m_pTableControl->isRowSelected( curRow );
2522 m_nCurrentRow = curRow;
2523 return selected;
2524 }
2525 }
2526
2528 {
2531 }
2532
2533
2535 {
2537 {
2538 for ( size_t i=0; i<m_pTableControl->getSelectedRowCount(); ++i )
2539 {
2540 RowPos const rowIndex = m_pTableControl->getSelectedRowIndex(i);
2541 m_pTableControl->invalidateRow( rowIndex );
2542 }
2543
2545 }
2546 }
2547
2548
2549} // namespace svt::table
2550
2551
2552/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
const MouseSettings & GetMouseSettings() const
void SetMouseSettings(const MouseSettings &rSet)
const StyleSettings & GetStyleSettings() const
bool HasControlFocus() const
void SetButtonRepeat(sal_Int32 nRepeat)
const AllSettings & GetSettings() const
constexpr tools::Long Y() const
constexpr tools::Long X() const
SelectionMode GetSelectionMode() const
void AddAlways(bool bOn)
constexpr tools::Long Height() const
constexpr tools::Long Width() const
sal_Int32 GetScrollBarSize() const
void disposeAndClear()
static VclPtr< reference_type > Create(Arg &&... arg)
a helper representing geometry information of a cell
tools::Rectangle getRect() const
bool markAllRowsAsSelected()
marks all rows as selected
void impl_getCellRect(ColPos _nColumn, RowPos _nRow, tools::Rectangle &_rCellRect) const
determines the rectangle occupied by the given cell
RowPos getRowAtPoint(const Point &rPoint) const
virtual void invalidate(TableArea const i_what) override
invalidates the table window
TableSize impl_getVisibleColumns(bool _bAcceptPartialCol) const
returns the number of visible columns
void invalidateRowRange(RowPos const i_firstRow, RowPos const i_lastRow)
invalidates the part of the data window which is covered by the given rows
tools::Long m_nRowHeightPixel
the height of a single row in the table, measured in pixels
tools::Long m_nRowHeaderWidthPixel
the width of the row header column in the table, measured in pixels
TableSize impl_ni_ScrollColumns(TableSize _nColumnDelta)
scrolls the view by the given number of columns
virtual TableCell hitTest(const Point &rPoint) const override
does a hit test for the given pixel coordinates
void commitAccessibleEvent(sal_Int16 const i_eventID)
tools::Rectangle calcHeaderCellRect(bool bColHeader, sal_Int32 nPos)
tools::Rectangle calcCellRect(sal_Int32 nRow, sal_Int32 nCol) const
PTableInputHandler m_pInputHandler
the input handler to use, usually the input handler as provided by ->m_pModel
virtual void columnChanged(ColPos const i_column, ColumnAttributeGroup const i_attributeGroup) override
notifies the listener that attributes of a given column changed
TableSize impl_getVisibleRows(bool _bAcceptPartialRow) const
returns the number of visible rows.
virtual void allColumnsRemoved() override
notifies the listener that all columns have been removed from the model
static int getRowSelectedNumber(const ::std::vector< RowPos > &selectedRows, RowPos current)
returns the position of the current row in the selection vector
virtual void tableMetricsChanged() override
notifies the listener that the metrics of the table changed.
virtual void columnInserted() override
notifies the listener that one or more columns have been inserted into the table
virtual void rowsInserted(RowPos first, RowPos last) override
notifies the listener that one or more rows have been inserted into the table
virtual void columnRemoved() override
notifies the listener that one or more columns have been removed from the table
void invalidateSelectedRows()
invalidates all selected rows
OUString getCellContentAsString(RowPos const i_row, ColPos const i_col)
retrieves the content of the given cell, converted to a string
TableSize impl_ni_ScrollRows(TableSize _nRowDelta)
scrolls the view by the given number of rows
ColumnPositions m_aColumnWidths
info about the widths of our columns
void setModel(const PTableModel &_pModel)
void setAnchor(RowPos const i_anchor)
void invalidateRect(const tools::Rectangle &rInvalidateRect)
ColPos getColAtPoint(const Point &rPoint) const
virtual void showTracking(tools::Rectangle const &i_location, ShowTrackFlags const i_flags) override
shows a tracking rectangle
virtual void setPointer(PointerStyle i_pointer) override
sets a new mouse pointer for the table window
void commitCellEvent(sal_Int16 const i_eventID, const css::uno::Any &i_newValue, const css::uno::Any &i_oldValue)
tools::Long m_nColHeaderHeightPixel
the height of the column header row in the table, measured in pixels
tools::Long appFontWidthToPixel(tools::Long const i_appFontUnits) const
tools::Rectangle impl_getAllVisibleDataCellArea() const
retrieves the area occupied by all (at least partially) visible data cells.
virtual void hideTracking() override
hides a previously shown tracking rectangle
virtual ColPos getCurrentColumn() const override
returns the index of the currently active column
RowPos impl_getRowForAbscissa(tools::Long const i_abscissa) const
retrieves the row which covers the given abscissa
void invalidateSelectedRegion(RowPos _nPrevRow, RowPos _nCurRow)
???
virtual tools::Long pixelWidthToAppFont(tools::Long const i_pixels) const override
calculates a width, given in pixels, into an AppFont-based width
void impl_ni_relayout(ColPos const i_assumeInflexibleColumnsUpToIncluding=COL_INVALID)
does a relayout of the table control
VclPtr< ScrollBar > m_pVScroll
the vertical scrollbar, if any
ColPos impl_getColumnForOrdinate(tools::Long const i_ordinate) const
retrieves the column which covers the given ordinate
std::vector< RowPos > m_aSelectedRows
VclPtr< TableDataWindow > m_pDataWindow
the window to contain all data content, including header bars
css::uno::Reference< css::accessibility::XAccessible > getAccessible(vcl::Window &i_parentWindow)
void doPaintContent(vcl::RenderContext &rRenderContext, const tools::Rectangle &_rUpdateRect)
paints the table control content which intersects with the given rectangle
virtual RowPos getCurrentRow() const override
returns the index of the currently active row
virtual PTableModel getModel() const override
returns the table model
TableControl & m_rAntiImpl
the control whose impl-instance we implement
VclPtr< ScrollBarBox > m_pScrollCorner
tools::Rectangle calcTableRect() const
bool markRowAsDeselected(RowPos const i_rowIndex)
removes the given row index from m_aSelectedRows
std::unique_ptr< SelectionEngine > m_pSelEngine
virtual bool isRowSelected(RowPos i_row) const override
determines whether a given row is selected
virtual void hideCursor() override
hides the cell cursor
void invalidateRow(RowPos const i_row)
invalidates the part of the data window which is covered by the given row
void impl_ni_updateCachedTableMetrics()
updates the cached table metrics (row height etc.)
PTableModel m_pModel
the model of the table control
virtual ::Size getTableSizePixel() const override
retrieves the size of the table window, in pixels
std::unique_ptr< TableFunctionSet > m_pTableFunctionSet
TableSize m_nRowCount
the number of rows in the table control. Cached model value.
TableSize m_nColumnCount
the number of columns in the table control. Cached model value.
virtual void captureMouse() override
captures the mouse to the table window
void impl_commitAccessibleEvent(sal_Int16 const i_eventID, css::uno::Any const &i_newValue)
VclPtr< ScrollBar > m_pHScroll
the horizontal scrollbar, if any
bool markAllRowsAsDeselected()
marks all rows as deselected
void onResize()
to be called when the anti-impl instance has been resized
tools::Rectangle impl_getAllVisibleCellsArea() const
retrieves the area occupied by the totality of (at least partially) visible cells
virtual void rowsRemoved(RowPos first, RowPos last) override
notifies the listener that one or more rows have been removed from the table
virtual void showCursor() override
shows the cell cursor
void impl_ni_positionChildWindows(tools::Rectangle const &i_dataCellPlayground, bool const i_verticalScrollbar, bool const i_horizontalScrollbar)
positions all child windows, e.g.
vcl::AccessibleFactoryAccess m_aFactoryAccess
virtual void releaseMouse() override
releases the mouse, after it had previously been captured
void commitTableEvent(sal_Int16 const i_eventID, const css::uno::Any &i_newValue, const css::uno::Any &i_oldValue)
TableControl_Impl(TableControl &_rAntiImpl)
void impl_ni_doSwitchCursor(bool _bOn)
toggles the cursor visibility
tools::Rectangle calcHeaderRect(bool bColHeader)
bool markRowAsSelected(RowPos const i_rowIndex)
marks the given row as selected, by putting it into m_aSelectedRows
virtual SelectionEngine * getSelEngine() override
returns selection engine
TableSize impl_scrollColumns(TableSize const i_columnDelta)
equivalent to impl_ni_ScrollColumns, but checks the instances invariants beforehand (in a non-product...
void impl_invalidateColumn(ColPos const i_column)
invalidates the window area occupied by the given column
RowPos getSelectedRowIndex(size_t const i_selectionIndex) const
virtual void activateCell(ColPos const i_col, RowPos const i_row) override
activates the given cell
virtual bool dispatchAction(TableControlAction _eAction) override
dispatches an action to the table control
tools::Long impl_ni_calculateColumnWidths(ColPos const i_assumeInflexibleColumnsUpToIncluding, bool const i_assumeVerticalScrollbar, ::std::vector< tools::Long > &o_newColWidthsPixel) const
calculates the new width of our columns, taking into account their min and max widths,...
virtual void cellsUpdated(RowPos const i_firstRow, RowPos const i_lastRow) override
notifies the listener that a rectangular cell range in the table has been updated
TableSize impl_scrollRows(TableSize const i_rowDelta)
equivalent to impl_ni_ScrollRows, but checks the instances invariants beforehand (in a non-product bu...
vcl::table::IAccessibleTableControl * m_pAccessibleTable
void impl_ni_updateCachedModelValues()
updates all cached model values
bool goTo(ColPos _nColumn, RowPos _nRow)
moves the cursor to the cell with the given coordinates
virtual ColumnMetrics getColumnMetrics(ColPos const i_column) const override
retrieves the metrics for a given column
void ensureVisible(ColPos _nColumn, RowPos _nRow)
ensures that the given coordinate is visible
a basic control which manages table-like data, i.e.
the window containing the content area (including headers) of a table control
virtual void SetCursorAtPoint(const Point &rPoint, bool bDontSelectAtCursor=false) override
virtual void DeselectAtPoint(const Point &rPoint) override
virtual void DestroyAnchor() override
virtual bool IsSelectionAtPoint(const Point &rPoint) override
virtual void DeselectAll() override
virtual void CreateAnchor() override
virtual void BeginDrag() override
TableControl_Impl * m_pTableControl
TableFunctionSet(TableControl_Impl *_pTableControl)
const tools::Rectangle & getRect() const
constexpr tools::Long GetWidth() const
constexpr void SetLeft(tools::Long v)
constexpr void SetTop(tools::Long v)
tools::Rectangle GetIntersection(const tools::Rectangle &rRect) const
constexpr tools::Long Top() const
constexpr Point TopLeft() const
constexpr void SetRight(tools::Long v)
constexpr Size GetSize() const
constexpr tools::Long Right() const
tools::Long AdjustRight(tools::Long nHorzMoveDelta)
constexpr void SetBottom(tools::Long v)
constexpr Point BottomRight() const
constexpr tools::Long GetHeight() const
tools::Rectangle & Union(const tools::Rectangle &rRect)
tools::Long AdjustBottom(tools::Long nVertMoveDelta)
constexpr tools::Long Left() const
constexpr tools::Long Bottom() const
constexpr bool IsEmpty() const
Point LogicToPixel(const Point &rLogicPt) const
ImplSVEvent * PostUserEvent(const Link< void *, void > &rLink, void *pCaller=nullptr, bool bReferenceLink=false)
const AllSettings & GetSettings() const
void SetSettings(const AllSettings &rSettings)
css::uno::Reference< css::accessibility::XAccessible > GetAccessible(bool bCreate=true)
Size GetOutputSizePixel() const
void Invalidate(InvalidateFlags nFlags=InvalidateFlags::NONE)
virtual css::uno::Reference< css::accessibility::XAccessible > getMyself()=0
virtual void commitCellEvent(sal_Int16 nEventId, const css::uno::Any &rNewValue, const css::uno::Any &rOldValue)=0
virtual bool isAlive() const=0
virtual void commitEvent(sal_Int16 nEventId, const css::uno::Any &rNewValue)=0
virtual void commitTableEvent(sal_Int16 nEventId, const css::uno::Any &rNewValue, const css::uno::Any &rOldValue)=0
#define DBG_ASSERT(sCon, aError)
#define DBG_TESTSOLARMUTEX()
#define ENSURE_OR_RETURN_FALSE(c, m)
#define ENSURE_OR_THROW(c, m)
#define ENSURE_OR_RETURN(c, m, r)
#define ENSURE_OR_RETURN_VOID(c, m)
sal_uInt16 nPos
virtual DECL_LISTENERMULTIPLEXER_END void SAL_CALL selectionChanged(const css::lang::EventObject &aEvent) override
void SAL_CALL first(const css::awt::SpinEvent &rEvent) override
RttiCompleteObjectLocator col
int i
void Create(SwFormatVertOrient &rItem, SvStream &rStrm, sal_uInt16 nVersionAbusedAsSize)
std::shared_ptr< T > make_shared(Args &&... args)
IMPL_LINK(TableControl_Impl, OnScroll, ScrollBar *, _pScrollbar, void)
IMPL_LINK_NOARG(TableControl, ImplSelectHdl, LinkParamNone *, void)
sal_Int32 TableSize
a value denoting the size of a table
Definition: tabletypes.hxx:29
sal_Int32 RowPos
a value denoting a row position within a table
Definition: tabletypes.hxx:34
std::shared_ptr< ITableModel > PTableModel
Definition: tablemodel.hxx:448
std::shared_ptr< ITableRenderer > PTableRenderer
@ ScrollbarShowNever
enumeration value denoting that a scrollbar should never be visible, even if needed normally
Definition: tablemodel.hxx:65
@ ScrollbarShowAlways
enumeration value denoting that a scrollbar should always be visible, even if not needed normally
Definition: tablemodel.hxx:72
sal_Int32 ColPos
a value denoting a column position within a table
Definition: tabletypes.hxx:32
std::shared_ptr< ITableInputHandler > PTableInputHandler
sal_Int32 TableMetrics
Definition: tabletypes.hxx:36
@ cursorToFirstLine
moves the cursor to the first row, keeping the current column
@ cursorToLineStart
moves the cursor to the beginning of the current line
@ cursorLeft
moves the cursor in the table control one column to the left, if possible, by keeping the current row
@ cursorPageDown
moves the cursor one page down, keeping the current column
@ cursorSelectRowUp
selects the rows, above the actual cursor is
@ cursorBottomRight
moves the cursor to the bottom-most, right-most cell
@ cursorDown
moves the cursor in the table control one row down, if possible, by keeping the current column
@ cursorPageUp
moves the cursor one page up, keeping the current column
@ cursorSelectRowDown
selects the row, beneath the actual cursor is
@ cursorUp
moves the cursor in the table control one row up, if possible, by keeping the current column
@ cursorToLineEnd
moves the cursor to the end of the current line
@ cursorSelectRow
selects the row, where the actual cursor is
@ cursorTopLeft
moves the cursor to the top-most, left-most cell
@ cursorSelectRowAreaBottom
selects the row, from the actual cursor till bottom
@ cursorRight
moves the cursor in the table control one column to the right, if possible, by keeping the current ro...
@ cursorSelectRowAreaTop
selects the row, from the actual cursor till top
@ cursorToLastLine
moves the cursor to the last row, keeping the current column
std::shared_ptr< IColumnModel > PColumnModel
Definition: tablemodel.hxx:234
std::shared_ptr< ITableModelListener > PTableModelListener
Definition: tablemodel.hxx:150
long Long
sal_Int32 RowPos
PointerStyle
#define MIN_COLUMN_WIDTH_PIXEL
ITableControl & m_rTable
ColumnAttributeGroup
Definition: tablemodel.hxx:42
@ APPEARANCE
denotes column attributes related to the appearance of the column, i.e. those relevant for rendering
@ WIDTH
denotes column attributes related to the width of the column
@ ALL
denotes the entirety of column attributes
#define COL_ROW_HEADERS
denotes the column containing the row headers
Definition: tabletypes.hxx:39
#define ROW_INVALID
denotes an invalid row index
Definition: tabletypes.hxx:46
#define COL_INVALID
denotes an invalid column index
Definition: tabletypes.hxx:44
#define ROW_COL_HEADERS
denotes the row containing the column headers
Definition: tabletypes.hxx:41
SelectionMode
ShowTrackFlags
size_t pos