Files
Michael Weghorn e4f1a48967 toolkit: Rename TableControl header/source to match new class name
Following

    Change-Id: I54e578098684a027c9bf2f192a5af3fecb4e65d7
    Author: Michael Weghorn <m.weghorn@posteo.de>
    Date:   Fri Sep 18 15:44:17 2026 +0200

        toolkit: Rename TableControl_Impl to TableControl

, also rename the header and source file to match
with the new class name.
(See that commit's commit message for more background.)

Change-Id: I5d7c4251902689718b9b2f3217671cad5b071201
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/211163
Tested-by: Jenkins
Reviewed-by: Michael Weghorn <m.weghorn@posteo.de>
2026-09-19 16:19:11 +02:00

566 lines
21 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#pragma once
#include <controls/table/AccessibleTableControlObjType.hxx>
#include <controls/table/tabledatawindow.hxx>
#include <controls/table/tablemodel.hxx>
#include <controls/table/tablecontrolinterface.hxx>
#include <com/sun/star/accessibility/XAccessible.hpp>
#include <vcl/ctrl.hxx>
#include <vcl/seleng.hxx>
#include <vector>
class ScrollBar;
class ScrollBarBox;
namespace accessibility
{
class AccessibleGridControl;
}
namespace svt::table
{
struct MutableColumnMetrics : public ColumnMetrics
{
MutableColumnMetrics()
: ColumnMetrics()
{
}
MutableColumnMetrics(tools::Long const i_startPixel, tools::Long const i_endPixel)
: ColumnMetrics(i_startPixel, i_endPixel)
{
}
tools::Long getStart() const { return nStartPixel; }
tools::Long getEnd() const { return nEndPixel; }
void move(tools::Long const i_offset)
{
nStartPixel += i_offset;
nEndPixel += i_offset;
}
tools::Long getWidth() const { return nEndPixel - nStartPixel; }
};
struct ColumnInfoPositionLess
{
bool operator()(MutableColumnMetrics const& i_lhs, MutableColumnMetrics const& i_rhs)
{
return i_lhs.getEnd() < i_rhs.getStart();
}
};
typedef ::std::vector<MutableColumnMetrics> ColumnPositions;
class TableFunctionSet;
/** a basic control which manages table-like data, i.e. a number of cells
organized in <code>m</code> rows and <code>n</code> columns.
The control itself does not do any assumptions about the concrete data
it displays, this is encapsulated in an instance supporting the
->ITableModel interface.
Also, the control does not do any assumptions about how the model's
content is rendered. This is the responsibility of a component
supporting the ->ITableRenderer interface (the renderer is obtained from
the model).
The control supports the concept of a <em>current</em> (or <em>active</em>
cell).
*/
class TableControl final : public Control, public ITableModelListener
{
friend class TableGeometry;
friend class TableRowGeometry;
friend class TableColumnGeometry;
friend class SuspendInvariants;
private:
/// the model of the table control
PTableModel m_pModel;
/// the input handler to use, usually the input handler as provided by ->m_pModel
PTableInputHandler m_pInputHandler;
/// info about the widths of our columns
ColumnPositions m_aColumnWidths;
/// the height of a single row in the table, measured in pixels
tools::Long m_nRowHeightPixel;
/// the height of the column header row in the table, measured in pixels
tools::Long m_nColHeaderHeightPixel;
/// the width of the row header column in the table, measured in pixels
tools::Long m_nRowHeaderWidthPixel;
/// the number of columns in the table control. Cached model value.
TableSize m_nColumnCount;
/// the number of rows in the table control. Cached model value.
TableSize m_nRowCount;
sal_Int32 m_nCurColumn;
sal_Int32 m_nCurRow;
sal_Int32 m_nLeftColumn;
sal_Int32 m_nTopRow;
sal_Int32 m_nCursorHidden;
/** the window to contain all data content, including header bars
The window's upper left corner is at position (0,0), relative to the
table control, which is the direct parent of the data window.
*/
VclPtr<TableDataWindow> m_pDataWindow;
/// the vertical scrollbar, if any
VclPtr<ScrollBar> m_pVScroll;
/// the horizontal scrollbar, if any
VclPtr<ScrollBar> m_pHScroll;
VclPtr<ScrollBarBox> m_pScrollCorner;
//selection engine - for determining selection range, e.g. single, multiple
std::unique_ptr<SelectionEngine> m_pSelEngine;
//vector which contains the selected rows
std::vector<sal_Int32> m_aSelectedRows;
//part of selection engine
std::unique_ptr<TableFunctionSet> m_pTableFunctionSet;
//part of selection engine
sal_Int32 m_nAnchor;
bool m_bUpdatingColWidths;
rtl::Reference<accessibility::AccessibleGridControl> m_xAccessibleTable;
public:
/// sets a new table model
void SetModel(const PTableModel& _pModel);
const PTableInputHandler& getInputHandler() const { return m_pInputHandler; }
sal_Int32 getCurRow() const { return m_nCurRow; }
sal_Int32 getAnchor() const { return m_nAnchor; }
void setAnchor(sal_Int32 const i_anchor) { m_nAnchor = i_anchor; }
sal_Int32 getTopRow() const { return m_nTopRow; }
sal_Int32 getLeftColumn() const { return m_nLeftColumn; }
public:
explicit TableControl(vcl::Window* pParent, WinBits nStyle);
virtual ~TableControl() override;
virtual void dispose() override;
// Window overridables
virtual void GetFocus() override;
virtual void LoseFocus() override;
virtual void KeyInput(const KeyEvent& rKEvt) override;
virtual void StateChanged(StateChangedType i_nStateChange) override;
virtual void Resize() override;
/** Creates and returns the accessible object of the whole GridControl. */
virtual rtl::Reference<comphelper::OAccessible> CreateAccessible() override;
/** to be called when the anti-impl instance has been resized
*/
void onResize();
void Select();
/** paints the table control content which intersects with the given rectangle */
void doPaintContent(vcl::RenderContext& rRenderContext, const tools::Rectangle& _rUpdateRect);
/** moves the cursor to the cell with the given coordinates
To ease the caller's code, the coordinates must not necessarily denote a
valid position. If they don't, <FALSE/> will be returned.
*/
bool GoToCell(sal_Int32 _nColumn, sal_Int32 _nRow);
/** ensures that the given coordinate is visible
@param _nColumn
the column position which should be visible. Must be non-negative, and smaller
than the column count.
@param _nRow
the row position which should be visibleMust be non-negative, and smaller
than the row count.
*/
void ensureVisible(sal_Int32 _nColumn, sal_Int32 _nRow);
/** retrieves the content of the given cell, converted to a string
*/
OUString GetAccessibleCellText(sal_Int32 const i_row, sal_Int32 const i_col);
/** returns the position of the current row in the selection vector */
static int getRowSelectedNumber(const ::std::vector<sal_Int32>& selectedRows,
sal_Int32 current);
void invalidateRect(const tools::Rectangle& rInvalidateRect);
/** ??? */
void invalidateSelectedRegion(sal_Int32 _nPrevRow, sal_Int32 _nCurRow);
/** invalidates the part of the data window which is covered by the given rows
@param i_firstRow
the index of the first row to include in the invalidation
@param i_lastRow
the index of the last row to include in the invalidation, or ROW_INVALID if the invalidation
should happen down to the bottom of the data window.
*/
void invalidateRowRange(sal_Int32 const i_firstRow, sal_Int32 const i_lastRow);
/** invalidates the part of the data window which is covered by the given row */
void invalidateRow(sal_Int32 const i_row) { invalidateRowRange(i_row, i_row); }
/** invalidates all selected rows */
void invalidateSelectedRows();
void checkCursorPosition();
sal_Int32 GetRowCount() const;
sal_Int32 GetColumnCount() const;
OUString GetRowName(sal_Int32 nIndex) const;
OUString GetColumnName(sal_Int32 nIndex) const;
bool HasRowHeader();
bool HasColumnHeader();
bool hasRowSelection() const { return !m_aSelectedRows.empty(); }
size_t GetSelectedRowCount() const { return m_aSelectedRows.size(); }
sal_Int32 GetSelectedRowIndex(size_t const i_selectionIndex) const;
/** removes the given row index from m_aSelectedRows
@return
<TRUE/> if and only if the row was previously marked as selected
*/
bool markRowAsDeselected(sal_Int32 const i_rowIndex);
/** marks the given row as selected, by putting it into m_aSelectedRows
@return
<TRUE/> if and only if the row was previously <em>not</em> marked as selected
*/
bool markRowAsSelected(sal_Int32 const i_rowIndex);
/** marks all rows as deselected
@return
<TRUE/> if and only if the selection actually changed by this operation
*/
bool markAllRowsAsDeselected();
/** marks all rows as selected
@return
<FALSE/> if and only if all rows were selected already.
*/
bool markAllRowsAsSelected();
void commitAccessibleEvent(sal_Int16 const i_eventID);
// temporary methods
// Those do not really belong into the public API - they're intended for firing A11Y-related events. However,
// firing those events should be an implementation internal to the TableControl,
// instead of something triggered externally.
void commitCellEvent(sal_Int16 const i_eventID, const css::uno::Any& i_newValue,
const css::uno::Any& i_oldValue);
void commitTableEvent(sal_Int16 const i_eventID, const css::uno::Any& i_newValue,
const css::uno::Any& i_oldValue);
/** hides the cell cursor
The method cares for successive calls, that is, for every call to
->hideCursor(), you need one call to ->showCursor. Only if the number
of both calls matches, the cursor is really shown.
@see showCursor
*/
void hideCursor();
/** shows the cell cursor
@see hideCursor
*/
void showCursor();
/** dispatches an action to the table control
@return
<TRUE/> if the action could be dispatched successfully, <FALSE/> otherwise. Usual
failure conditions include some other instance vetoing the action, or impossibility
to execute the action at all (for instance moving up one row when already positioned
on the very first row).
@see TableControlAction
*/
bool dispatchAction(TableControlAction _eAction);
/** returns selection engine*/
SelectionEngine* getSelEngine();
/** returns the table model
The returned model is guaranteed to not be <NULL/>.
*/
PTableModel GetModel() const;
/** retrieves the current column
The current col is the one which contains the active cell.
@return
the column index of the active cell, or ->COL_INVALID
if there is no active cell, e.g. because the table does
not contain any rows or columns.
*/
sal_Int32 GetCurrentColumn() const;
/** retrieves the current row
The current row is the one which contains the active cell.
@return
the row index of the active cell, or ->ROW_INVALID
if there is no active cell, e.g. because the table does
not contain any rows or columns.
*/
sal_Int32 GetCurrentRow() const;
/// activates the given cell
void activateCell(sal_Int32 const i_col, sal_Int32 const i_row);
/// retrieves the size of the table window, in pixels
Size getTableSizePixel() const;
/// sets a new mouse pointer for the table window
void setPointer(PointerStyle i_pointer);
/// captures the mouse to the table window
void captureMouse();
/// releases the mouse, after it had previously been captured
void releaseMouse();
/// invalidates the table window
void invalidate(TableArea const i_what);
/// calculates a width, given in pixels, into an AppFont-based width
tools::Long pixelWidthToAppFont(tools::Long const i_pixels) const;
/// hides a previously shown tracking rectangle
void hideTracking();
/// shows a tracking rectangle
void showTracking(tools::Rectangle const& i_location, ShowTrackFlags const i_flags);
sal_Int32 getRowAtPoint(const Point& rPoint) const;
sal_Int32 getColAtPoint(const Point& rPoint) const;
/// does a hit test for the given pixel coordinates
TableCell hitTest(const Point& rPoint) const;
/// retrieves the metrics for a given column
ColumnMetrics getColumnMetrics(sal_Int32 const i_column) const;
/// determines whether a given row is selected
bool IsRowSelected(sal_Int32 i_row) const;
void SelectRow(sal_Int32 nRowIndex, bool bSelect);
void SelectAllRows(bool bSelect);
tools::Long appFontWidthToPixel(tools::Long const i_appFontUnits) const;
TableDataWindow& getDataWindow() { return *m_pDataWindow; }
const TableDataWindow& getDataWindow() const { return *m_pDataWindow; }
ScrollBar* getHorzScrollbar() { return m_pHScroll; }
ScrollBar* getVertScrollbar() { return m_pVScroll; }
bool ConvertPointToCellAddress(sal_Int32& rRow, sal_Int32& rColPos, const Point& rPoint);
tools::Rectangle calcHeaderRect(bool bColHeader);
tools::Rectangle calcHeaderCellRect(bool bColHeader, sal_Int32 nPos);
tools::Rectangle calcTableRect() const;
tools::Rectangle calcCellRect(sal_Int32 nRow, sal_Int32 nCol) const;
// A11Y
const rtl::Reference<accessibility::AccessibleGridControl>&
getAccessible(const rtl::Reference<comphelper::OAccessible>& rpParent);
void disposeAccessible();
sal_Int32 GetAccessibleControlCount() const;
OUString GetAccessibleObjectName(AccessibleTableControlObjType eObjType, sal_Int32 nRow,
sal_Int32 nCol) const;
OUString GetAccessibleObjectDescription(AccessibleTableControlObjType eObjType) const;
void FillAccessibleStateSet(sal_Int64& rStateSet, AccessibleTableControlObjType eObjType) const;
void FillAccessibleStateSetForCell(sal_Int64& rStateSet, sal_Int32 nRow,
sal_uInt16 nColumnPos) const;
// ITableModelListener
virtual void rowsInserted(sal_Int32 first, sal_Int32 last) override;
virtual void rowsRemoved(sal_Int32 first, sal_Int32 last) override;
virtual void columnInserted() override;
virtual void columnRemoved() override;
virtual void allColumnsRemoved() override;
virtual void cellsUpdated(sal_Int32 const i_firstRow, sal_Int32 const i_lastRow) override;
virtual void columnChanged(sal_Int32 const i_column,
ColumnAttributeGroup const i_attributeGroup) override;
virtual void tableMetricsChanged() override;
private:
void impl_commitAccessibleEvent(sal_Int16 const i_eventID, css::uno::Any const& i_newValue);
/** toggles the cursor visibility
The method is not bound to the classes public invariants, as it's used in
situations where the they must not necessarily be fulfilled.
*/
void impl_ni_doSwitchCursor(bool _bOn);
/** returns the number of visible rows.
@param _bAcceptPartialRow
specifies whether a possible only partially visible last row is
counted, too.
*/
TableSize impl_getVisibleRows(bool _bAcceptPartialRow) const;
/** returns the number of visible columns
The value may change with different horizontal scroll positions, as
different columns have different widths. For instance, if your control is
100 pixels wide, and has three columns of width 50, 50, 100, respectively,
then this method will return either "2" or "1", depending on which column
is the first visible one.
@param _bAcceptPartialRow
specifies whether a possible only partially visible last row is
counted, too.
*/
TableSize impl_getVisibleColumns(bool _bAcceptPartialCol) const;
/** updates all cached model values
The method is not bound to the classes public invariants, as it's used in
situations where the they must not necessarily be fulfilled.
*/
void impl_ni_updateCachedModelValues();
/** updates the cached table metrics (row height etc.) */
void impl_ni_updateCachedTableMetrics();
/** does a relayout of the table control
Column widths, and consequently the availability of the vertical and horizontal scrollbar, are updated
with a call to this method.
@param i_assumeInflexibleColumnsUpToIncluding
the index of a column up to which all columns should be considered as inflexible, or
<code>COL_INVALID</code>.
*/
void impl_ni_relayout(sal_Int32 const i_assumeInflexibleColumnsUpToIncluding = COL_INVALID);
/** calculates the new width of our columns, taking into account their min and max widths, and their relative
flexibility.
@param i_assumeInflexibleColumnsUpToIncluding
the index of a column up to which all columns should be considered as inflexible, or
<code>COL_INVALID</code>.
@param i_assumeVerticalScrollbar
controls whether or not we should assume the presence of a vertical scrollbar. If <true/>, and
if the model has a VerticalScrollbarVisibility != ScrollbarShowNever, the method will leave
space for a vertical scrollbar.
@return
the overall width of the grid, which is available for columns
*/
tools::Long
impl_ni_calculateColumnWidths(sal_Int32 const i_assumeInflexibleColumnsUpToIncluding,
bool const i_assumeVerticalScrollbar,
::std::vector<tools::Long>& o_newColWidthsPixel) const;
/** positions all child windows, e.g. the both scrollbars, the corner window, and the data window */
void impl_ni_positionChildWindows(tools::Rectangle const& i_dataCellPlayground,
bool const i_verticalScrollbar,
bool const i_horizontalScrollbar);
/** scrolls the view by the given number of rows
The method is not bound to the classes public invariants, as it's used in
situations where the they must not necessarily be fulfilled.
@return
the number of rows by which the viewport was scrolled. This may differ
from the given numbers to scroll in case the begin or the end of the
row range were reached.
*/
TableSize impl_ni_ScrollRows(TableSize _nRowDelta);
/** equivalent to impl_ni_ScrollRows, but checks the instances invariants beforehand (in a non-product build only) */
TableSize impl_scrollRows(TableSize const i_rowDelta);
/** scrolls the view by the given number of columns
The method is not bound to the classes public invariants, as it's used in
situations where the they must not necessarily be fulfilled.
@return
the number of columns by which the viewport was scrolled. This may differ
from the given numbers to scroll in case the begin or the end of the
column range were reached.
*/
TableSize impl_ni_ScrollColumns(TableSize _nColumnDelta);
/** equivalent to impl_ni_ScrollColumns, but checks the instances invariants beforehand (in a non-product build only) */
TableSize impl_scrollColumns(TableSize const i_columnDelta);
/** retrieves the area occupied by the totality of (at least partially) visible cells
The returned area includes row and column headers. Also, it takes into
account the fact that there might be less columns than would normally
find room in the control.
As a result of respecting the partial visibility of rows and columns,
the returned area might be larger than the data window's output size.
*/
tools::Rectangle impl_getAllVisibleCellsArea() const;
/** retrieves the area occupied by all (at least partially) visible data cells.
Effectively, the returned area is the same as returned by ->impl_getAllVisibleCellsArea,
minus the row and column header areas.
*/
tools::Rectangle impl_getAllVisibleDataCellArea() const;
/** retrieves the column which covers the given ordinate */
sal_Int32 impl_getColumnForOrdinate(tools::Long const i_ordinate) const;
/** retrieves the row which covers the given abscissa
*/
sal_Int32 impl_getRowForAbscissa(tools::Long const i_abscissa) const;
/// invalidates the window area occupied by the given column
void impl_invalidateColumn(sal_Int32 const i_column);
DECL_LINK(OnScroll, ScrollBar*, void);
DECL_LINK(OnUpdateScrollbars, void*, void);
DECL_LINK(ImplSelectHdl, LinkParamNone*, void);
};
} // namespace svt::table
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */