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