Bug Summary

File:home/maarten/src/libreoffice/core/include/rtl/ref.hxx
Warning:line 192, column 9
Use of memory after it is freed

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name tablecontrol_impl.cxx -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -mframe-pointer=all -fmath-errno -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib64/clang/11.0.0 -D BOOST_ERROR_CODE_HEADER_ONLY -D BOOST_SYSTEM_NO_DEPRECATED -D CPPU_ENV=gcc3 -D LINUX -D OSL_DEBUG_LEVEL=1 -D SAL_LOG_INFO -D SAL_LOG_WARN -D UNIX -D UNX -D X86_64 -D _PTHREADS -D _REENTRANT -D SVT_DLLIMPLEMENTATION -D EXCEPTIONS_ON -D LIBO_INTERNAL_ONLY -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/icu/source -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/icu/source/i18n -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/icu/source/common -I /home/maarten/src/libreoffice/core/external/boost/include -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/boost -I /home/maarten/src/libreoffice/core/include -I /usr/lib/jvm/java-11-openjdk-11.0.9.10-0.0.ea.fc33.x86_64/include -I /usr/lib/jvm/java-11-openjdk-11.0.9.10-0.0.ea.fc33.x86_64/include/linux -I /home/maarten/src/libreoffice/core/config_host -I /home/maarten/src/libreoffice/core/workdir/CustomTarget/officecfg/registry -I /home/maarten/src/libreoffice/core/workdir/UnoApiHeadersTarget/udkapi/normal -I /home/maarten/src/libreoffice/core/workdir/UnoApiHeadersTarget/offapi/normal -I /home/maarten/src/libreoffice/core/svtools/source/inc -I /home/maarten/src/libreoffice/core/svtools/inc -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/10/../../../../include/c++/10 -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/10/../../../../include/c++/10/x86_64-redhat-linux -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/10/../../../../include/c++/10/backward -internal-isystem /usr/local/include -internal-isystem /usr/lib64/clang/11.0.0/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -O0 -Wno-missing-braces -std=c++17 -fdeprecated-macro -fdebug-compilation-dir /home/maarten/src/libreoffice/core -ferror-limit 19 -fvisibility hidden -fvisibility-inlines-hidden -stack-protector 2 -fgnuc-version=4.2.1 -fcxx-exceptions -fexceptions -debug-info-kind=constructor -analyzer-output=html -faddrsig -o /home/maarten/tmp/wis/scan-build-libreoffice/output/report/2020-10-07-141433-9725-1 -x c++ /home/maarten/src/libreoffice/core/svtools/source/table/tablecontrol_impl.cxx

/home/maarten/src/libreoffice/core/svtools/source/table/tablecontrol_impl.cxx

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

/home/maarten/src/libreoffice/core/include/vcl/vclptr.hxx

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#ifndef INCLUDED_VCL_PTR_HXX
21#define INCLUDED_VCL_PTR_HXX
22
23#include <sal/config.h>
24
25#include <rtl/ref.hxx>
26
27#include <utility>
28#include <type_traits>
29
30#ifdef DBG_UTIL
31#ifndef _WIN32
32#include <vcl/vclmain.hxx>
33#endif
34#endif
35
36class VclReferenceBase;
37
38namespace vcl::detail {
39
40template<typename>
41constexpr bool isIncompleteOrDerivedFromVclReferenceBase(...) { return true; }
42
43template<typename T> constexpr bool isIncompleteOrDerivedFromVclReferenceBase(
44 int (*)[sizeof(T)])
45{ return std::is_base_of<VclReferenceBase, T>::value; }
46
47} // namespace vcl::detail
48
49/**
50 * A thin wrapper around rtl::Reference to implement the acquire and dispose semantics we want for references to vcl::Window subclasses.
51 *
52 * For more details on the design please see vcl/README.lifecycle
53 *
54 * @param reference_type must be a subclass of vcl::Window
55 */
56template <class reference_type>
57class VclPtr
58{
59 static_assert(
60 vcl::detail::isIncompleteOrDerivedFromVclReferenceBase<reference_type>(
61 nullptr),
62 "template argument type must be derived from VclReferenceBase");
63
64 ::rtl::Reference<reference_type> m_rInnerRef;
65
66public:
67 /** Constructor...
68 */
69 VclPtr()
70 : m_rInnerRef()
71 {}
72
73 /** Constructor...
74 */
75 VclPtr (reference_type * pBody)
76 : m_rInnerRef(pBody)
77 {}
78
79 /** Constructor... that doesn't take a ref.
80 */
81 VclPtr (reference_type * pBody, __sal_NoAcquire)
82 : m_rInnerRef(pBody, SAL_NO_ACQUIRE)
83 {}
84
85 /** Up-casting conversion constructor: Copies interface reference.
86
87 Does not work for up-casts to ambiguous bases. For the special case of
88 up-casting to Reference< XInterface >, see the corresponding conversion
89 operator.
90
91 @param rRef another reference
92 */
93 template< class derived_type >
94 VclPtr(
95 const VclPtr< derived_type > & rRef,
96 typename std::enable_if<
97 std::is_base_of<reference_type, derived_type>::value, int>::type
98 = 0 )
99 : m_rInnerRef( static_cast<reference_type*>(rRef) )
100 {
101 }
102
103#if defined(DBG_UTIL) && !defined(_WIN32)
104 virtual ~VclPtr()
105 {
106 assert(m_rInnerRef.get() == nullptr || vclmain::isAlive())(static_cast <bool> (m_rInnerRef.get() == nullptr || vclmain
::isAlive()) ? void (0) : __assert_fail ("m_rInnerRef.get() == nullptr || vclmain::isAlive()"
, "/home/maarten/src/libreoffice/core/include/vcl/vclptr.hxx"
, 106, __extension__ __PRETTY_FUNCTION__))
;
107 // We can be one of the intermediate counts, but if we are the last
108 // VclPtr keeping this object alive, then something forgot to call dispose().
109 assert((!m_rInnerRef.get() || m_rInnerRef->isDisposed() || m_rInnerRef->getRefCount() > 1)(static_cast <bool> ((!m_rInnerRef.get() || m_rInnerRef
->isDisposed() || m_rInnerRef->getRefCount() > 1) &&
"someone forgot to call dispose()") ? void (0) : __assert_fail
("(!m_rInnerRef.get() || m_rInnerRef->isDisposed() || m_rInnerRef->getRefCount() > 1) && \"someone forgot to call dispose()\""
, "/home/maarten/src/libreoffice/core/include/vcl/vclptr.hxx"
, 110, __extension__ __PRETTY_FUNCTION__))
110 && "someone forgot to call dispose()")(static_cast <bool> ((!m_rInnerRef.get() || m_rInnerRef
->isDisposed() || m_rInnerRef->getRefCount() > 1) &&
"someone forgot to call dispose()") ? void (0) : __assert_fail
("(!m_rInnerRef.get() || m_rInnerRef->isDisposed() || m_rInnerRef->getRefCount() > 1) && \"someone forgot to call dispose()\""
, "/home/maarten/src/libreoffice/core/include/vcl/vclptr.hxx"
, 110, __extension__ __PRETTY_FUNCTION__))
;
111 }
112 VclPtr(VclPtr const &) = default;
113 VclPtr(VclPtr &&) = default;
114 VclPtr & operator =(VclPtr const &) = default;
115 VclPtr & operator =(VclPtr &&) = default;
116#endif
117
118 /**
119 * A construction helper for VclPtr. Since VclPtr types are created
120 * with a reference-count of one - to help fit into the existing
121 * code-flow; this helps us to construct them easily.
122 *
123 * For more details on the design please see vcl/README.lifecycle
124 *
125 * @tparam reference_type must be a subclass of vcl::Window
126 */
127 template<typename... Arg> [[nodiscard]] static VclPtr< reference_type > Create(Arg &&... arg)
128 {
129 return VclPtr< reference_type >( new reference_type(std::forward<Arg>(arg)...), SAL_NO_ACQUIRE );
130 }
131
132 /** Probably most common used: handle->someBodyOp().
133 */
134 reference_type * operator->() const
135 {
136 return m_rInnerRef.get();
137 }
138
139 /** Get the body. Can be used instead of operator->().
140 I.e. handle->someBodyOp() and handle.get()->someBodyOp()
141 are the same.
142 */
143 reference_type * get() const
144 {
145 return m_rInnerRef.get();
146 }
147
148 void set(reference_type *pBody)
149 {
150 m_rInnerRef.set(pBody);
151 }
152
153 void reset(reference_type *pBody)
154 {
155 m_rInnerRef.set(pBody);
156 }
157
158 /** Up-casting copy assignment operator.
159
160 Does not work for up-casts to ambiguous bases.
161
162 @param rRef another reference
163 */
164 template<typename derived_type>
165 typename std::enable_if<
166 std::is_base_of<reference_type, derived_type>::value,
167 VclPtr &>::type
168 operator =(VclPtr<derived_type> const & rRef)
169 {
170 m_rInnerRef.set(rRef.get());
171 return *this;
172 }
173
174 VclPtr & operator =(reference_type * pBody)
175 {
176 m_rInnerRef.set(pBody);
177 return *this;
178 }
179
180 operator reference_type * () const
181 {
182 return m_rInnerRef.get();
183 }
184
185 explicit operator bool () const
186 {
187 return m_rInnerRef.get() != nullptr;
188 }
189
190 void clear()
191 {
192 m_rInnerRef.clear();
193 }
194
195 void reset()
196 {
197 m_rInnerRef.clear();
198 }
199
200 void disposeAndClear()
201 {
202 // hold it alive for the lifetime of this method
203 ::rtl::Reference<reference_type> aTmp(m_rInnerRef);
2
Calling copy constructor for 'Reference<svt::table::TableDataWindow>'
5
Returning from copy constructor for 'Reference<svt::table::TableDataWindow>'
204 m_rInnerRef.clear(); // we should use some 'swap' method ideally ;-)
6
Calling 'Reference::clear'
13
Returning; memory was released
205 if (aTmp.get()) {
14
Calling 'Reference::get'
206 aTmp->disposeOnce();
207 }
208 }
209
210 /** Needed to place VclPtr's into STL collection.
211 */
212 bool operator< (const VclPtr<reference_type> & handle) const
213 {
214 return (m_rInnerRef < handle.m_rInnerRef);
215 }
216}; // class VclPtr
217
218template<typename T1, typename T2>
219inline bool operator ==(VclPtr<T1> const & p1, VclPtr<T2> const & p2) {
220 return p1.get() == p2.get();
221}
222
223template<typename T> inline bool operator ==(VclPtr<T> const & p1, T const * p2)
224{
225 return p1.get() == p2;
226}
227
228template<typename T> inline bool operator ==(VclPtr<T> const & p1, T * p2) {
229 return p1.get() == p2;
230}
231
232template<typename T> inline bool operator ==(T const * p1, VclPtr<T> const & p2)
233{
234 return p1 == p2.get();
235}
236
237template<typename T> inline bool operator ==(T * p1, VclPtr<T> const & p2) {
238 return p1 == p2.get();
239}
240
241template<typename T1, typename T2>
242inline bool operator !=(VclPtr<T1> const & p1, VclPtr<T2> const & p2) {
243 return !(p1 == p2);
244}
245
246template<typename T> inline bool operator !=(VclPtr<T> const & p1, T const * p2)
247{
248 return !(p1 == p2);
249}
250
251template<typename T> inline bool operator !=(VclPtr<T> const & p1, T * p2) {
252 return !(p1 == p2);
253}
254
255template<typename T> inline bool operator !=(T const * p1, VclPtr<T> const & p2)
256{
257 return !(p1 == p2);
258}
259
260template<typename T> inline bool operator !=(T * p1, VclPtr<T> const & p2) {
261 return !(p1 == p2);
262}
263
264/**
265 * A construction helper for a temporary VclPtr. Since VclPtr types
266 * are created with a reference-count of one - to help fit into
267 * the existing code-flow; this helps us to construct them easily.
268 * see also VclPtr::Create and ScopedVclPtr
269 *
270 * For more details on the design please see vcl/README.lifecycle
271 *
272 * @param reference_type must be a subclass of vcl::Window
273 */
274template <class reference_type>
275class SAL_WARN_UNUSED__attribute__((warn_unused)) VclPtrInstance final : public VclPtr<reference_type>
276{
277public:
278 template<typename... Arg> VclPtrInstance(Arg &&... arg)
279 : VclPtr<reference_type>( new reference_type(std::forward<Arg>(arg)...), SAL_NO_ACQUIRE )
280 {
281 }
282
283 /**
284 * Override and disallow this, to prevent people accidentally calling it and actually
285 * getting VclPtr::Create and getting a naked VclPtr<> instance
286 */
287 template<typename... Arg> static VclPtrInstance< reference_type > Create(Arg &&... ) = delete;
288};
289
290template <class reference_type>
291class ScopedVclPtr : public VclPtr<reference_type>
292{
293public:
294 /** Constructor...
295 */
296 ScopedVclPtr()
297 : VclPtr<reference_type>()
298 {}
299
300 /** Constructor
301 */
302 ScopedVclPtr (reference_type * pBody)
303 : VclPtr<reference_type>(pBody)
304 {}
305
306 /** Copy constructor...
307 */
308 ScopedVclPtr (const VclPtr<reference_type> & handle)
309 : VclPtr<reference_type>(handle)
310 {}
311
312 /**
313 Assignment that releases the last reference.
314 */
315 void disposeAndReset(reference_type *pBody)
316 {
317 if (pBody != this->get()) {
318 VclPtr<reference_type>::disposeAndClear();
319 VclPtr<reference_type>::set(pBody);
320 }
321 }
322
323 /**
324 Assignment that releases the last reference.
325 */
326 ScopedVclPtr<reference_type>& operator = (reference_type * pBody)
327 {
328 disposeAndReset(pBody);
329 return *this;
330 }
331
332 /** Up-casting conversion constructor: Copies interface reference.
333
334 Does not work for up-casts to ambiguous bases. For the special case of
335 up-casting to Reference< XInterface >, see the corresponding conversion
336 operator.
337
338 @param rRef another reference
339 */
340 template< class derived_type >
341 ScopedVclPtr(
342 const VclPtr< derived_type > & rRef,
343 typename std::enable_if<
344 std::is_base_of<reference_type, derived_type>::value, int>::type
345 = 0 )
346 : VclPtr<reference_type>( rRef )
347 {
348 }
349
350 /** Up-casting assignment operator.
351
352 Does not work for up-casts to ambiguous bases.
353
354 @param rRef another VclPtr
355 */
356 template<typename derived_type>
357 typename std::enable_if<
358 std::is_base_of<reference_type, derived_type>::value,
359 ScopedVclPtr &>::type
360 operator =(VclPtr<derived_type> const & rRef)
361 {
362 disposeAndReset(rRef.get());
363 return *this;
364 }
365
366 /**
367 * Override and disallow this, to prevent people accidentally calling it and actually
368 * getting VclPtr::Create and getting a naked VclPtr<> instance
369 */
370 template<typename... Arg> static ScopedVclPtr< reference_type > Create(Arg &&... ) = delete;
371
372 ~ScopedVclPtr()
373 {
374 VclPtr<reference_type>::disposeAndClear();
375 assert(VclPtr<reference_type>::get() == nullptr)(static_cast <bool> (VclPtr<reference_type>::get(
) == nullptr) ? void (0) : __assert_fail ("VclPtr<reference_type>::get() == nullptr"
, "/home/maarten/src/libreoffice/core/include/vcl/vclptr.hxx"
, 375, __extension__ __PRETTY_FUNCTION__))
; // make sure there are no lingering references
376 }
377
378private:
379 // Most likely we don't want this default copy-constructor.
380 ScopedVclPtr (const ScopedVclPtr<reference_type> &) = delete;
381 // And certainly we don't want a default assignment operator.
382 ScopedVclPtr<reference_type>& operator = (const ScopedVclPtr<reference_type> &) = delete;
383 // And disallow reset as that doesn't call disposeAndClear on the original reference
384 void reset() = delete;
385 void reset(reference_type *pBody) = delete;
386
387protected:
388 ScopedVclPtr (reference_type * pBody, __sal_NoAcquire)
389 : VclPtr<reference_type>(pBody, SAL_NO_ACQUIRE)
390 {}
391};
392
393/**
394 * A construction helper for ScopedVclPtr. Since VclPtr types are created
395 * with a reference-count of one - to help fit into the existing
396 * code-flow; this helps us to construct them easily.
397 *
398 * For more details on the design please see vcl/README.lifecycle
399 *
400 * @param reference_type must be a subclass of vcl::Window
401 */
402#if defined _MSC_VER
403#pragma warning(push)
404#pragma warning(disable: 4521) // " multiple copy constructors specified"
405#endif
406template <class reference_type>
407class SAL_WARN_UNUSED__attribute__((warn_unused)) ScopedVclPtrInstance final : public ScopedVclPtr<reference_type>
408{
409public:
410 template<typename... Arg> ScopedVclPtrInstance(Arg &&... arg)
411 : ScopedVclPtr<reference_type>( new reference_type(std::forward<Arg>(arg)...), SAL_NO_ACQUIRE )
412 {
413 }
414
415 /**
416 * Override and disallow this, to prevent people accidentally calling it and actually
417 * getting VclPtr::Create and getting a naked VclPtr<> instance
418 */
419 template<typename... Arg> static ScopedVclPtrInstance< reference_type > Create(Arg &&...) = delete;
420
421private:
422 // Prevent the above perfect forwarding ctor from hijacking (accidental)
423 // attempts at ScopedVclPtrInstance copy construction (where the hijacking
424 // would typically lead to somewhat obscure error messages); both non-const
425 // and const variants are needed here, as the ScopedVclPtr base class has a
426 // const--variant copy ctor, so the implicitly declared copy ctor for
427 // ScopedVclPtrInstance would also be the const variant, so non-const copy
428 // construction attempts would be hijacked by the perfect forwarding ctor;
429 // but if we only declared a non-const variant here, the const variant would
430 // no longer be implicitly declared (as there would already be an explicitly
431 // declared copy ctor), so const copy construction attempts would then be
432 // hijacked by the perfect forwarding ctor:
433 ScopedVclPtrInstance(ScopedVclPtrInstance &) = delete;
434 ScopedVclPtrInstance(ScopedVclPtrInstance const &) = delete;
435};
436#if defined _MSC_VER
437#pragma warning(pop)
438#endif
439
440#endif // INCLUDED_VCL_PTR_HXX
441
442/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

/home/maarten/src/libreoffice/core/include/rtl/ref.hxx

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#ifndef INCLUDED_RTL_REF_HXX
21#define INCLUDED_RTL_REF_HXX
22
23#include "sal/config.h"
24
25#include <cassert>
26#include <cstddef>
27#include <functional>
28#ifdef LIBO_INTERNAL_ONLY1
29#include <type_traits>
30#endif
31
32#include "sal/types.h"
33
34namespace rtl
35{
36
37/** Template reference class for reference type.
38*/
39template <class reference_type>
40class Reference
41{
42 /** The <b>reference_type</b> body pointer.
43 */
44 reference_type * m_pBody;
45
46
47public:
48 /** Constructor...
49 */
50 Reference()
51 : m_pBody (NULL__null)
52 {}
53
54
55 /** Constructor...
56 */
57 Reference (reference_type * pBody, __sal_NoAcquire)
58 : m_pBody (pBody)
59 {
60 }
61
62 /** Constructor...
63 */
64 Reference (reference_type * pBody)
65 : m_pBody (pBody)
66 {
67 if (m_pBody)
68 m_pBody->acquire();
69 }
70
71 /** Copy constructor...
72 */
73 Reference (const Reference<reference_type> & handle)
74 : m_pBody (handle.m_pBody)
75 {
76 if (m_pBody)
3
Assuming field 'm_pBody' is non-null
4
Taking true branch
77 m_pBody->acquire();
78 }
79
80#ifdef LIBO_INTERNAL_ONLY1
81 /** Move constructor...
82 */
83 Reference (Reference<reference_type> && handle) noexcept
84 : m_pBody (handle.m_pBody)
85 {
86 handle.m_pBody = nullptr;
87 }
88#endif
89
90#if defined LIBO_INTERNAL_ONLY1
91 /** Up-casting conversion constructor: Copies interface reference.
92
93 Does not work for up-casts to ambiguous bases.
94
95 @param rRef another reference
96 */
97 template< class derived_type >
98 inline Reference(
99 const Reference< derived_type > & rRef,
100 std::enable_if_t<std::is_base_of_v<reference_type, derived_type>, int> = 0 )
101 : m_pBody (rRef.get())
102 {
103 if (m_pBody)
104 m_pBody->acquire();
105 }
106#endif
107
108 /** Destructor...
109 */
110 ~Reference() COVERITY_NOEXCEPT_FALSE
111 {
112 if (m_pBody)
113 m_pBody->release();
114 }
115
116 /** Set...
117 Similar to assignment.
118 */
119 Reference<reference_type> &
120 SAL_CALL set (reference_type * pBody)
121 {
122 if (pBody)
123 pBody->acquire();
124 reference_type * const pOld = m_pBody;
125 m_pBody = pBody;
126 if (pOld)
127 pOld->release();
128 return *this;
129 }
130
131 /** Assignment.
132 Unbinds this instance from its body (if bound) and
133 bind it to the body represented by the handle.
134 */
135 Reference<reference_type> &
136 SAL_CALL operator= (const Reference<reference_type> & handle)
137 {
138 return set( handle.m_pBody );
139 }
140
141#ifdef LIBO_INTERNAL_ONLY1
142 /** Assignment.
143 * Unbinds this instance from its body (if bound),
144 * bind it to the body represented by the handle, and
145 * set the body represented by the handle to nullptr.
146 */
147 Reference<reference_type> &
148 operator= (Reference<reference_type> && handle)
149 {
150 // self-movement guts ourself
151 if (m_pBody)
152 m_pBody->release();
153 m_pBody = handle.m_pBody;
154 handle.m_pBody = nullptr;
155 return *this;
156 }
157#endif
158
159 /** Assignment...
160 */
161 Reference<reference_type> &
162 SAL_CALL operator= (reference_type * pBody)
163 {
164 return set( pBody );
165 }
166
167 /** Unbind the body from this handle.
168 Note that for a handle representing a large body,
169 "handle.clear().set(new body());" _might_
170 perform a little bit better than "handle.set(new body());",
171 since in the second case two large objects exist in memory
172 (the old body and the new body).
173 */
174 Reference<reference_type> & SAL_CALL clear()
175 {
176 if (m_pBody
6.1
Field 'm_pBody' is non-null
6.1
Field 'm_pBody' is non-null
6.1
Field 'm_pBody' is non-null
6.1
Field 'm_pBody' is non-null
)
7
Taking true branch
177 {
178 reference_type * const pOld = m_pBody;
179 m_pBody = NULL__null;
180 pOld->release();
8
Calling 'VclReferenceBase::release'
12
Returning; memory was released
181 }
182 return *this;
183 }
184
185
186 /** Get the body. Can be used instead of operator->().
187 I.e. handle->someBodyOp() and handle.get()->someBodyOp()
188 are the same.
189 */
190 reference_type * SAL_CALL get() const
191 {
192 return m_pBody;
15
Use of memory after it is freed
193 }
194
195
196 /** Probably most common used: handle->someBodyOp().
197 */
198 reference_type * SAL_CALL operator->() const
199 {
200 assert(m_pBody != NULL)(static_cast <bool> (m_pBody != __null) ? void (0) : __assert_fail
("m_pBody != NULL", "/home/maarten/src/libreoffice/core/include/rtl/ref.hxx"
, 200, __extension__ __PRETTY_FUNCTION__))
;
201 return m_pBody;
202 }
203
204
205 /** Allows (*handle).someBodyOp().
206 */
207 reference_type & SAL_CALL operator*() const
208 {
209 assert(m_pBody != NULL)(static_cast <bool> (m_pBody != __null) ? void (0) : __assert_fail
("m_pBody != NULL", "/home/maarten/src/libreoffice/core/include/rtl/ref.hxx"
, 209, __extension__ __PRETTY_FUNCTION__))
;
210 return *m_pBody;
211 }
212
213
214 /** Returns True if the handle does point to a valid body.
215 */
216 bool SAL_CALL is() const
217 {
218 return (m_pBody != NULL__null);
219 }
220
221#if defined LIBO_INTERNAL_ONLY1
222 /** Returns True if the handle does point to a valid body.
223 */
224 explicit operator bool() const
225 {
226 return is();
227 }
228#endif
229
230 /** Returns True if this points to pBody.
231 */
232 bool SAL_CALL operator== (const reference_type * pBody) const
233 {
234 return (m_pBody == pBody);
235 }
236
237
238 /** Returns True if handle points to the same body.
239 */
240 bool
241 SAL_CALL operator== (const Reference<reference_type> & handle) const
242 {
243 return (m_pBody == handle.m_pBody);
244 }
245
246
247 /** Needed to place References into STL collection.
248 */
249 bool
250 SAL_CALL operator!= (const Reference<reference_type> & handle) const
251 {
252 return (m_pBody != handle.m_pBody);
253 }
254
255
256 /** Needed to place References into STL collection.
257 */
258 bool
259 SAL_CALL operator< (const Reference<reference_type> & handle) const
260 {
261 return (m_pBody < handle.m_pBody);
262 }
263
264
265 /** Needed to place References into STL collection.
266 */
267 bool
268 SAL_CALL operator> (const Reference<reference_type> & handle) const
269 {
270 return (m_pBody > handle.m_pBody);
271 }
272};
273
274} // namespace rtl
275
276#if defined LIBO_INTERNAL_ONLY1
277namespace std
278{
279
280/// @cond INTERNAL
281/**
282 Make rtl::Reference hashable by default for use in STL containers.
283
284 @since LibreOffice 6.3
285*/
286template<typename T>
287struct hash<::rtl::Reference<T>>
288{
289 std::size_t operator()(::rtl::Reference<T> const & s) const
290 { return std::size_t(s.get()); }
291};
292/// @endcond
293
294}
295
296#endif
297
298#endif /* ! INCLUDED_RTL_REF_HXX */
299
300/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

/home/maarten/src/libreoffice/core/include/vcl/vclreferencebase.hxx

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#ifndef INCLUDED_VCL_Reference_HXX
20#define INCLUDED_VCL_Reference_HXX
21
22#include <vcl/dllapi.h>
23#include <osl/interlck.h>
24
25class VCL_DLLPUBLIC__attribute__ ((visibility("default"))) VclReferenceBase
26{
27 mutable oslInterlockedCount mnRefCnt;
28
29 template<typename T> friend class VclPtr;
30
31public:
32 void acquire() const
33 {
34 osl_atomic_increment(&mnRefCnt)__sync_add_and_fetch((&mnRefCnt), 1);
35 }
36
37 void release() const
38 {
39 if (osl_atomic_decrement(&mnRefCnt)__sync_sub_and_fetch((&mnRefCnt), 1) == 0)
9
Assuming the condition is true
10
Taking true branch
40 delete this;
11
Memory is released
41 }
42#ifdef DBG_UTIL
43#ifndef _WIN32
44 sal_Int32 getRefCount() const { return mnRefCnt; }
45#endif
46#endif
47
48
49private:
50 VclReferenceBase(const VclReferenceBase&) = delete;
51 VclReferenceBase& operator=(const VclReferenceBase&) = delete;
52
53 bool mbDisposed : 1;
54
55protected:
56 VclReferenceBase();
57protected:
58 virtual ~VclReferenceBase();
59
60protected:
61 virtual void dispose();
62
63public:
64 void disposeOnce();
65 bool isDisposed() const { return mbDisposed; }
66
67};
68#endif