Coverage Report

Created: 2025-07-07 10:01

/src/libreoffice/toolkit/source/controls/table/mousefunction.cxx
Line
Count
Source (jump to first uncovered line)
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
#include <controls/table/mousefunction.hxx>
21
#include <controls/table/tablecontrolinterface.hxx>
22
#include <controls/table/tablesort.hxx>
23
24
#include <comphelper/diagnose_ex.hxx>
25
#include <vcl/ptrstyle.hxx>
26
27
namespace svt::table
28
{
29
30
31
    //= ColumnResize
32
33
34
    FunctionResult ColumnResize::handleMouseMove( ITableControl& i_tableControl, MouseEvent const & i_event )
35
0
    {
36
0
        Point const aPoint = i_event.GetPosPixel();
37
38
0
        if ( m_nResizingColumn == COL_INVALID )
39
0
        {
40
            // if we hit a column divider, change the mouse pointer accordingly
41
0
            PointerStyle aNewPointer( PointerStyle::Arrow );
42
0
            TableCell const tableCell = i_tableControl.hitTest( aPoint );
43
0
            if ( ( tableCell.nRow == ROW_COL_HEADERS ) && ( tableCell.eArea == ColumnDivider ) )
44
0
            {
45
0
                aNewPointer = PointerStyle::HSplit;
46
0
            }
47
0
            i_tableControl.setPointer( aNewPointer );
48
49
0
            return SkipFunction;    // TODO: is this correct?
50
0
        }
51
52
0
        ::Size const tableSize = i_tableControl.getTableSizePixel();
53
54
        // set proper pointer
55
0
        PointerStyle aNewPointer( PointerStyle::Arrow );
56
0
        ColumnMetrics const columnMetrics( i_tableControl.getColumnMetrics( m_nResizingColumn ) );
57
0
        if  (   ( aPoint.X() > tableSize.Width() )
58
0
            ||  ( aPoint.X() < columnMetrics.nStartPixel )
59
0
            )
60
0
        {
61
0
            aNewPointer = PointerStyle::NotAllowed;
62
0
        }
63
0
        else
64
0
        {
65
0
            aNewPointer = PointerStyle::HSplit;
66
0
        }
67
0
        i_tableControl.setPointer( aNewPointer );
68
69
        // show tracking line
70
0
        i_tableControl.hideTracking();
71
0
        i_tableControl.showTracking(
72
0
            tools::Rectangle(
73
0
                Point( aPoint.X(), 0 ),
74
0
                Size( 1, tableSize.Height() )
75
0
            ),
76
0
            ShowTrackFlags::Split | ShowTrackFlags::TrackWindow
77
0
        );
78
79
0
        return ContinueFunction;
80
0
    }
81
82
83
    FunctionResult ColumnResize::handleMouseDown( ITableControl& i_tableControl, MouseEvent const & i_event )
84
0
    {
85
0
        if ( m_nResizingColumn != COL_INVALID )
86
0
        {
87
0
            OSL_ENSURE( false, "ColumnResize::handleMouseDown: suspicious: MouseButtonDown while still tracking?" );
88
0
            return ContinueFunction;
89
0
        }
90
91
0
        TableCell const tableCell( i_tableControl.hitTest( i_event.GetPosPixel() ) );
92
0
        if ( tableCell.nRow == ROW_COL_HEADERS )
93
0
        {
94
0
            if  (   ( tableCell.nColumn != COL_INVALID )
95
0
                &&  ( tableCell.eArea == ColumnDivider )
96
0
                )
97
0
            {
98
0
                m_nResizingColumn = tableCell.nColumn;
99
0
                i_tableControl.captureMouse();
100
0
                return ActivateFunction;
101
0
            }
102
0
        }
103
104
0
        return SkipFunction;
105
0
    }
106
107
108
    FunctionResult ColumnResize::handleMouseUp( ITableControl& i_tableControl, MouseEvent const & i_event )
109
0
    {
110
0
        if ( m_nResizingColumn == COL_INVALID )
111
0
            return SkipFunction;
112
113
0
        Point const aPoint = i_event.GetPosPixel();
114
115
0
        i_tableControl.hideTracking();
116
0
        PColumnModel const pColumn = i_tableControl.getModel()->getColumnModel( m_nResizingColumn );
117
0
        tools::Long const maxWidthLogical = pColumn->getMaxWidth();
118
0
        tools::Long const minWidthLogical = pColumn->getMinWidth();
119
120
        // new position of mouse
121
0
        tools::Long const requestedEnd = aPoint.X();
122
123
        // old position of right border
124
0
        tools::Long const oldEnd = i_tableControl.getColumnMetrics( m_nResizingColumn ).nEndPixel;
125
126
        // position of left border if cursor in the to-be-resized column
127
0
        tools::Long const columnStart = i_tableControl.getColumnMetrics( m_nResizingColumn ).nStartPixel;
128
0
        tools::Long const requestedWidth = requestedEnd - columnStart;
129
            // TODO: this is not correct, strictly: It assumes that the mouse was pressed exactly on the "end" pos,
130
            // but for a while now, we have relaxed this, and allow clicking a few pixels aside, too
131
132
0
        if ( requestedEnd >= columnStart )
133
0
        {
134
0
            tools::Long requestedWidthLogical = i_tableControl.pixelWidthToAppFont( requestedWidth );
135
            // respect column width limits
136
0
            if ( oldEnd > requestedEnd )
137
0
            {
138
                // column has become smaller, check against minimum width
139
0
                if ( ( minWidthLogical != 0 ) && ( requestedWidthLogical < minWidthLogical ) )
140
0
                    requestedWidthLogical = minWidthLogical;
141
0
            }
142
0
            else if ( oldEnd < requestedEnd )
143
0
            {
144
                // column has become larger, check against max width
145
0
                if ( ( maxWidthLogical != 0 ) && ( requestedWidthLogical >= maxWidthLogical ) )
146
0
                    requestedWidthLogical = maxWidthLogical;
147
0
            }
148
0
            pColumn->setWidth( requestedWidthLogical );
149
0
            i_tableControl.invalidate( TableArea::All );
150
0
        }
151
152
0
        i_tableControl.setPointer( PointerStyle::Arrow );
153
0
        i_tableControl.releaseMouse();
154
155
0
        m_nResizingColumn = COL_INVALID;
156
0
        return DeactivateFunction;
157
0
    }
158
159
160
    //= RowSelection
161
162
163
    FunctionResult RowSelection::handleMouseMove( ITableControl&, MouseEvent const & )
164
0
    {
165
0
        return SkipFunction;
166
0
    }
167
168
169
    FunctionResult RowSelection::handleMouseDown( ITableControl& i_tableControl, MouseEvent const & i_event )
170
0
    {
171
0
        bool handled = false;
172
173
0
        TableCell const tableCell( i_tableControl.hitTest( i_event.GetPosPixel() ) );
174
0
        if ( tableCell.nRow >= 0 )
175
0
        {
176
0
            if ( i_tableControl.getSelEngine()->GetSelectionMode() == SelectionMode::NONE )
177
0
            {
178
0
                i_tableControl.activateCell( tableCell.nColumn, tableCell.nRow );
179
0
                handled = true;
180
0
            }
181
0
            else
182
0
            {
183
0
                handled = i_tableControl.getSelEngine()->SelMouseButtonDown( i_event );
184
0
            }
185
0
        }
186
187
0
        if ( handled )
188
0
            m_bActive = true;
189
0
        return handled ? ActivateFunction : SkipFunction;
190
0
    }
191
192
193
    FunctionResult RowSelection::handleMouseUp( ITableControl& i_tableControl, MouseEvent const & i_event )
194
0
    {
195
0
        TableCell const tableCell = i_tableControl.hitTest( i_event.GetPosPixel() );
196
0
        if ( tableCell.nRow >= 0 )
197
0
        {
198
0
            if ( i_tableControl.getSelEngine()->GetSelectionMode() != SelectionMode::NONE )
199
0
            {
200
0
                i_tableControl.getSelEngine()->SelMouseButtonUp( i_event );
201
0
            }
202
0
        }
203
0
        if ( m_bActive )
204
0
        {
205
0
            m_bActive = false;
206
0
            return DeactivateFunction;
207
0
        }
208
0
        return SkipFunction;
209
0
    }
210
211
212
    //= ColumnSortHandler
213
214
215
    FunctionResult ColumnSortHandler::handleMouseMove( ITableControl&, MouseEvent const & )
216
0
    {
217
0
        return SkipFunction;
218
0
    }
219
220
221
    FunctionResult ColumnSortHandler::handleMouseDown( ITableControl& i_tableControl, MouseEvent const & i_event )
222
0
    {
223
0
        if ( m_nActiveColumn != COL_INVALID )
224
0
        {
225
0
            OSL_ENSURE( false, "ColumnSortHandler::handleMouseDown: called while already active - suspicious!" );
226
0
            return ContinueFunction;
227
0
        }
228
229
0
        if ( i_tableControl.getModel()->getSortAdapter() == nullptr )
230
            // no sorting support at the model
231
0
            return SkipFunction;
232
233
0
        TableCell const tableCell( i_tableControl.hitTest( i_event.GetPosPixel() ) );
234
0
        if ( ( tableCell.nRow != ROW_COL_HEADERS ) || ( tableCell.nColumn < 0 ) )
235
0
            return SkipFunction;
236
237
        // TODO: ensure the column header is rendered in some special way, indicating its current state
238
239
0
        m_nActiveColumn = tableCell.nColumn;
240
0
        return ActivateFunction;
241
0
    }
242
243
244
    FunctionResult ColumnSortHandler::handleMouseUp( ITableControl& i_tableControl, MouseEvent const & i_event )
245
0
    {
246
0
        if ( m_nActiveColumn == COL_INVALID )
247
0
            return SkipFunction;
248
249
0
        TableCell const tableCell( i_tableControl.hitTest( i_event.GetPosPixel() ) );
250
0
        if ( ( tableCell.nRow == ROW_COL_HEADERS ) && ( tableCell.nColumn == m_nActiveColumn ) )
251
0
        {
252
0
            ITableDataSort* pSort = i_tableControl.getModel()->getSortAdapter();
253
0
            ENSURE_OR_RETURN( pSort != nullptr, "ColumnSortHandler::handleMouseUp: somebody is mocking with us!", DeactivateFunction );
254
                // in handleMousButtonDown, the model claimed to have sort support ...
255
256
0
            ColumnSortDirection eSortDirection = ColumnSortAscending;
257
0
            ColumnSort const aCurrentSort = pSort->getCurrentSortOrder();
258
0
            if ( aCurrentSort.nColumnPos == m_nActiveColumn )
259
                // invert existing sort order
260
0
                eSortDirection = ( aCurrentSort.eSortDirection == ColumnSortAscending ) ? ColumnSortDescending : ColumnSortAscending;
261
262
0
            pSort->sortByColumn( m_nActiveColumn, eSortDirection );
263
0
        }
264
265
0
        m_nActiveColumn = COL_INVALID;
266
0
        return DeactivateFunction;
267
0
    }
268
269
270
} // namespace svt::table
271
272
273
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */