1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 #include "precompiled_svtools.hxx"
25 
26 #include "mousefunction.hxx"
27 #include "svtools/table/tablecontrolinterface.hxx"
28 
29 #include <tools/diagnose_ex.h>
30 #include <vcl/window.hxx>
31 
32 //......................................................................................................................
33 namespace svt { namespace table
34 {
35 //......................................................................................................................
36 
37 	//==================================================================================================================
38 	//= MouseFunction
39 	//==================================================================================================================
40 	//------------------------------------------------------------------------------------------------------------------
acquire()41     oslInterlockedCount MouseFunction::acquire()
42     {
43         return osl_incrementInterlockedCount( &m_refCount );
44     }
45 
46 	//------------------------------------------------------------------------------------------------------------------
release()47     oslInterlockedCount MouseFunction::release()
48     {
49         oslInterlockedCount newCount = osl_decrementInterlockedCount( &m_refCount );
50         if ( newCount == 0 )
51         {
52             delete this;
53             return 0;
54         }
55         return newCount;
56     }
57 
58 	//==================================================================================================================
59 	//= ColumnResize
60 	//==================================================================================================================
61 	//------------------------------------------------------------------------------------------------------------------
handleMouseMove(ITableControl & i_tableControl,MouseEvent const & i_event)62     FunctionResult ColumnResize::handleMouseMove( ITableControl& i_tableControl, MouseEvent const & i_event )
63     {
64 	    Point const aPoint = i_event.GetPosPixel();
65 
66         if ( m_nResizingColumn == COL_INVALID )
67         {
68             // if we hit a column divider, change the mosue pointer accordingly
69             Pointer aNewPointer( POINTER_ARROW );
70             TableCell const tableCell = i_tableControl.hitTest( aPoint );
71             if ( ( tableCell.nRow == ROW_COL_HEADERS ) && ( tableCell.eArea == ColumnDivider ) )
72             {
73                 aNewPointer = Pointer( POINTER_HSPLIT );
74             }
75             i_tableControl.setPointer( aNewPointer );
76 
77             return SkipFunction;    // TODO: is this correct?
78         }
79 
80         ::Size const tableSize = i_tableControl.getTableSizePixel();
81 
82         // set proper pointer
83         Pointer aNewPointer( POINTER_ARROW );
84         ColumnMetrics const & columnMetrics( i_tableControl.getColumnMetrics( m_nResizingColumn ) );
85         if  (   ( aPoint.X() > tableSize.Width() )
86             ||  ( aPoint.X() < columnMetrics.nStartPixel )
87             )
88         {
89 	        aNewPointer = Pointer( POINTER_NOTALLOWED );
90         }
91         else
92         {
93 	        aNewPointer = Pointer( POINTER_HSPLIT );
94         }
95         i_tableControl.setPointer( aNewPointer );
96 
97         // show tracking line
98         i_tableControl.hideTracking();
99         i_tableControl.showTracking(
100             Rectangle(
101                 Point( aPoint.X(), 0 ),
102                 Size( 1, tableSize.Height() )
103             ),
104 		    SHOWTRACK_SPLIT | SHOWTRACK_WINDOW
105         );
106 
107         (void)i_event;
108         return ContinueFunction;
109     }
110 
111 	//------------------------------------------------------------------------------------------------------------------
handleMouseDown(ITableControl & i_tableControl,MouseEvent const & i_event)112     FunctionResult ColumnResize::handleMouseDown( ITableControl& i_tableControl, MouseEvent const & i_event )
113     {
114         if ( m_nResizingColumn != COL_INVALID )
115         {
116             OSL_ENSURE( false, "ColumnResize::handleMouseDown: suspicious: MouseButtonDown while still tracking?" );
117             return ContinueFunction;
118         }
119 
120         TableCell const tableCell( i_tableControl.hitTest( i_event.GetPosPixel() ) );
121         if ( tableCell.nRow == ROW_COL_HEADERS )
122         {
123             if  (   ( tableCell.nColumn != COL_INVALID )
124                 &&  ( tableCell.eArea == ColumnDivider )
125                 )
126             {
127                 m_nResizingColumn = tableCell.nColumn;
128                 i_tableControl.captureMouse();
129                 return ActivateFunction;
130             }
131         }
132 
133         return SkipFunction;
134     }
135 
136 	//------------------------------------------------------------------------------------------------------------------
handleMouseUp(ITableControl & i_tableControl,MouseEvent const & i_event)137     FunctionResult ColumnResize::handleMouseUp( ITableControl& i_tableControl, MouseEvent const & i_event )
138     {
139         if ( m_nResizingColumn == COL_INVALID )
140             return SkipFunction;
141 
142         Point const aPoint = i_event.GetPosPixel();
143 
144         i_tableControl.hideTracking();
145         PColumnModel const pColumn = i_tableControl.getModel()->getColumnModel( m_nResizingColumn );
146         long const maxWidthLogical = pColumn->getMaxWidth();
147         long const minWidthLogical = pColumn->getMinWidth();
148 
149         // new position of mouse
150         long const requestedEnd = aPoint.X();
151 
152         // old position of right border
153         long const oldEnd = i_tableControl.getColumnMetrics( m_nResizingColumn ).nEndPixel;
154 
155         // position of left border if cursor in the to-be-resized column
156         long const columnStart = i_tableControl.getColumnMetrics( m_nResizingColumn ).nStartPixel;
157         long const requestedWidth = requestedEnd - columnStart;
158             // TODO: this is not correct, strictly: It assumes that the mouse was pressed exactly on the "end" pos,
159             // but for a while now, we have relaxed this, and allow clicking a few pixels aside, too
160 
161         if ( requestedEnd >= columnStart )
162         {
163             long requestedWidthLogical = i_tableControl.pixelWidthToAppFont( requestedWidth );
164             // respect column width limits
165             if ( oldEnd > requestedEnd )
166             {
167                 // column has become smaller, check against minimum width
168                 if ( ( minWidthLogical != 0 ) && ( requestedWidthLogical < minWidthLogical ) )
169                     requestedWidthLogical = minWidthLogical;
170             }
171             else if ( oldEnd < requestedEnd )
172             {
173                 // column has become larger, check against max width
174                 if ( ( maxWidthLogical != 0 ) && ( requestedWidthLogical >= maxWidthLogical ) )
175                     requestedWidthLogical = maxWidthLogical;
176             }
177             pColumn->setWidth( requestedWidthLogical );
178             i_tableControl.invalidate( TableAreaAll );
179         }
180 
181         i_tableControl.setPointer( Pointer() );
182         i_tableControl.releaseMouse();
183 
184         m_nResizingColumn = COL_INVALID;
185 	    return DeactivateFunction;
186     }
187 
188 	//==================================================================================================================
189 	//= RowSelection
190 	//==================================================================================================================
191 	//------------------------------------------------------------------------------------------------------------------
handleMouseMove(ITableControl & i_tableControl,MouseEvent const & i_event)192     FunctionResult RowSelection::handleMouseMove( ITableControl& i_tableControl, MouseEvent const & i_event )
193     {
194         OSL_UNUSED( i_tableControl );
195         OSL_UNUSED( i_event );
196         return SkipFunction;
197     }
198 
199 	//------------------------------------------------------------------------------------------------------------------
handleMouseDown(ITableControl & i_tableControl,MouseEvent const & i_event)200     FunctionResult RowSelection::handleMouseDown( ITableControl& i_tableControl, MouseEvent const & i_event )
201     {
202         bool handled = false;
203 
204         TableCell const tableCell( i_tableControl.hitTest( i_event.GetPosPixel() ) );
205 	    if ( tableCell.nRow >= 0 )
206 	    {
207 		    if ( i_tableControl.getSelEngine()->GetSelectionMode() == NO_SELECTION )
208 		    {
209 		        i_tableControl.activateCell( tableCell.nColumn, tableCell.nRow );
210 				handled = true;
211 		    }
212 		    else
213 			{
214 				handled = i_tableControl.getSelEngine()->SelMouseButtonDown( i_event );
215             }
216 	    }
217 
218         if ( handled )
219             m_bActive = true;
220         return handled ? ActivateFunction : SkipFunction;
221     }
222 
223 	//------------------------------------------------------------------------------------------------------------------
handleMouseUp(ITableControl & i_tableControl,MouseEvent const & i_event)224     FunctionResult RowSelection::handleMouseUp( ITableControl& i_tableControl, MouseEvent const & i_event )
225     {
226         TableCell const tableCell = i_tableControl.hitTest( i_event.GetPosPixel() );
227         if ( tableCell.nRow >= 0 )
228 	    {
229 		    if ( i_tableControl.getSelEngine()->GetSelectionMode() != NO_SELECTION )
230 			{
231 				i_tableControl.getSelEngine()->SelMouseButtonUp( i_event );
232 			}
233 	    }
234         if ( m_bActive )
235         {
236             m_bActive = false;
237             return DeactivateFunction;
238         }
239         return SkipFunction;
240     }
241 
242 	//==================================================================================================================
243 	//= ColumnSortHandler
244 	//==================================================================================================================
245 	//------------------------------------------------------------------------------------------------------------------
handleMouseMove(ITableControl & i_tableControl,MouseEvent const & i_event)246     FunctionResult ColumnSortHandler::handleMouseMove( ITableControl& i_tableControl, MouseEvent const & i_event )
247     {
248         OSL_UNUSED( i_tableControl );
249         OSL_UNUSED( i_event );
250         return SkipFunction;
251     }
252 
253 	//------------------------------------------------------------------------------------------------------------------
handleMouseDown(ITableControl & i_tableControl,MouseEvent const & i_event)254     FunctionResult ColumnSortHandler::handleMouseDown( ITableControl& i_tableControl, MouseEvent const & i_event )
255     {
256         if ( m_nActiveColumn != COL_INVALID )
257         {
258             OSL_ENSURE( false, "ColumnSortHandler::handleMouseDown: called while already active - suspicious!" );
259             return ContinueFunction;
260         }
261 
262         if ( i_tableControl.getModel()->getSortAdapter() == NULL )
263             // no sorting support at the model
264             return SkipFunction;
265 
266         TableCell const tableCell( i_tableControl.hitTest( i_event.GetPosPixel() ) );
267         if ( ( tableCell.nRow != ROW_COL_HEADERS ) || ( tableCell.nColumn < 0 ) )
268             return SkipFunction;
269 
270         // TODO: ensure the column header is rendered in some special way, indicating its current state
271 
272         m_nActiveColumn = tableCell.nColumn;
273         return ActivateFunction;
274     }
275 
276 	//------------------------------------------------------------------------------------------------------------------
handleMouseUp(ITableControl & i_tableControl,MouseEvent const & i_event)277     FunctionResult ColumnSortHandler::handleMouseUp( ITableControl& i_tableControl, MouseEvent const & i_event )
278     {
279         if ( m_nActiveColumn == COL_INVALID )
280             return SkipFunction;
281 
282         TableCell const tableCell( i_tableControl.hitTest( i_event.GetPosPixel() ) );
283         if ( ( tableCell.nRow == ROW_COL_HEADERS ) && ( tableCell.nColumn == m_nActiveColumn ) )
284         {
285             ITableDataSort* pSort = i_tableControl.getModel()->getSortAdapter();
286             ENSURE_OR_RETURN( pSort != NULL, "ColumnSortHandler::handleMouseUp: somebody is mocking with us!", DeactivateFunction );
287                 // in handleMousButtonDown, the model claimed to have sort support ...
288 
289             ColumnSortDirection eSortDirection = ColumnSortAscending;
290             ColumnSort const aCurrentSort = pSort->getCurrentSortOrder();
291             if ( aCurrentSort.nColumnPos == m_nActiveColumn )
292                 // invert existing sort order
293                 eSortDirection = ( aCurrentSort.eSortDirection == ColumnSortAscending ) ? ColumnSortDescending : ColumnSortAscending;
294 
295             pSort->sortByColumn( m_nActiveColumn, eSortDirection );
296         }
297 
298         m_nActiveColumn = COL_INVALID;
299         return DeactivateFunction;
300     }
301 
302 //......................................................................................................................
303 } } // namespace svt::table
304 //......................................................................................................................
305