2 * Smart "snapping" for window moving and sizing
\r
4 * Author: Alessandro Scotti (Dec 2005)
\r
6 * Copyright 2005 Alessandro Scotti
\r
8 * ------------------------------------------------------------------------
\r
10 * GNU XBoard is free software: you can redistribute it and/or modify
\r
11 * it under the terms of the GNU General Public License as published by
\r
12 * the Free Software Foundation, either version 3 of the License, or (at
\r
13 * your option) any later version.
\r
15 * GNU XBoard is distributed in the hope that it will be useful, but
\r
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
\r
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
\r
18 * General Public License for more details.
\r
20 * You should have received a copy of the GNU General Public License
\r
21 * along with this program. If not, see http://www.gnu.org/licenses/. *
\r
23 *------------------------------------------------------------------------
\r
24 ** See the file ChangeLog for a revision history. */
\r
28 /* Imports from winboard.c */
\r
29 extern HINSTANCE hInst;
\r
31 extern HWND hwndMain;
\r
32 extern HWND moveHistoryDialog;
\r
33 extern HWND evalGraphDialog;
\r
34 extern HWND engineOutputDialog;
\r
35 extern HWND gameListDialog;
\r
37 static BOOL SnappingEnabled = TRUE;
\r
39 static void AddSnapPoint( int * grid, int * grid_len, int value )
\r
41 int len = *grid_len;
\r
43 if( len < MAX_SNAP_POINTS ) {
\r
46 for( i=0; i<len; i++ ) {
\r
47 if( grid[i] == value ) {
\r
52 grid[ len++ ] = value;
\r
58 static void AddSnapRectangle( SnapData * sd, RECT * rc )
\r
60 AddSnapPoint( sd->x_grid, &sd->x_grid_len, rc->left );
\r
61 AddSnapPoint( sd->x_grid, &sd->x_grid_len, rc->right );
\r
63 AddSnapPoint( sd->y_grid, &sd->y_grid_len, rc->top );
\r
64 AddSnapPoint( sd->y_grid, &sd->y_grid_len, rc->bottom );
\r
67 static RECT activeRect, mainRect;
\r
68 static int side, loc; // code for edge we were dragging, and its latest coordinate
\r
70 static void AddSnapWindow( HWND hWndCaller, SnapData * sd, HWND hWndSnapWindow )
\r
72 if( hWndSnapWindow != NULL && IsWindowVisible(hWndSnapWindow) ) {
\r
75 GetWindowRect( hWndSnapWindow, &rc );
\r
76 if(hWndSnapWindow == hwndMain) mainRect = rc;
\r
78 if(hWndCaller != hWndSnapWindow) {
\r
79 AddSnapRectangle( sd, &rc );
\r
81 activeRect = rc; // [HGM] glue: remember original geometry of dragged window
\r
86 static BOOL AdjustToSnapPoint( int * grid, int grid_len, int value, int * snap_size, int * delta )
\r
88 BOOL result = FALSE;
\r
91 for( i=0; i<grid_len; i++ ) {
\r
92 int distance = value - grid[i];
\r
94 if( distance < 0 ) distance = -distance;
\r
96 if( distance < *snap_size ) {
\r
98 *snap_size = distance;
\r
99 *delta = grid[i] - value;
\r
106 LRESULT OnEnterSizeMove( SnapData * snapData, HWND hWnd, WPARAM wParam, LPARAM lParam )
\r
110 snapData->x_grid_len = 0;
\r
111 snapData->y_grid_len = 0;
\r
114 /* Add desktop area */
\r
115 if( SystemParametersInfo( SPI_GETWORKAREA, 0, &rc, 0 ) ) {
\r
116 AddSnapRectangle( snapData, &rc );
\r
119 if( hWnd != hwndMain ) {
\r
120 /* Add other windows */
\r
121 AddSnapWindow( hWnd, snapData, hwndMain );
\r
122 AddSnapWindow( hWnd, snapData, moveHistoryDialog );
\r
123 AddSnapWindow( hWnd, snapData, evalGraphDialog );
\r
124 AddSnapWindow( hWnd, snapData, engineOutputDialog );
\r
125 AddSnapWindow( hWnd, snapData, gameListDialog );
\r
131 LRESULT OnMoving( SnapData * snapData, HWND hWnd, WPARAM wParam, LPARAM lParam )
\r
133 LPRECT lprc = (LPRECT) lParam;
\r
136 int snap_size_x = SNAP_DISTANCE;
\r
137 int snap_size_y = SNAP_DISTANCE;
\r
139 if( ! SnappingEnabled ) {
\r
143 AdjustToSnapPoint( snapData->x_grid, snapData->x_grid_len, lprc->left, &snap_size_x, &delta_x );
\r
144 AdjustToSnapPoint( snapData->x_grid, snapData->x_grid_len, lprc->right, &snap_size_x, &delta_x );
\r
146 AdjustToSnapPoint( snapData->y_grid, snapData->y_grid_len, lprc->top, &snap_size_y, &delta_y );
\r
147 AdjustToSnapPoint( snapData->y_grid, snapData->y_grid_len, lprc->bottom, &snap_size_y, &delta_y );
\r
149 OffsetRect( lprc, delta_x, delta_y );
\r
154 LRESULT OnSizing( SnapData * snapData, HWND hWnd, WPARAM wParam, LPARAM lParam )
\r
156 LPRECT lprc = (LPRECT) lParam;
\r
159 int snap_size_x = SNAP_DISTANCE;
\r
160 int snap_size_y = SNAP_DISTANCE;
\r
162 if( ! SnappingEnabled ) {
\r
168 AdjustToSnapPoint( snapData->y_grid, snapData->y_grid_len, lprc->bottom, &snap_size_y, &delta_y );
\r
169 lprc->bottom += delta_y; side = 4; loc = lprc->bottom;
\r
171 case WMSZ_BOTTOMLEFT:
\r
172 AdjustToSnapPoint( snapData->y_grid, snapData->y_grid_len, lprc->bottom, &snap_size_y, &delta_y );
\r
173 lprc->bottom += delta_y;
\r
174 AdjustToSnapPoint( snapData->x_grid, snapData->x_grid_len, lprc->left, &snap_size_x, &delta_x );
\r
175 lprc->left += delta_x;
\r
177 case WMSZ_BOTTOMRIGHT:
\r
178 AdjustToSnapPoint( snapData->y_grid, snapData->y_grid_len, lprc->bottom, &snap_size_y, &delta_y );
\r
179 lprc->bottom += delta_y;
\r
180 AdjustToSnapPoint( snapData->x_grid, snapData->x_grid_len, lprc->right, &snap_size_x, &delta_x );
\r
181 lprc->right += delta_x;
\r
184 AdjustToSnapPoint( snapData->x_grid, snapData->x_grid_len, lprc->left, &snap_size_x, &delta_x );
\r
185 lprc->left += delta_x; side = 1; loc = lprc->left;
\r
188 AdjustToSnapPoint( snapData->x_grid, snapData->x_grid_len, lprc->right, &snap_size_x, &delta_x );
\r
189 lprc->right += delta_x; side = 2; loc = lprc->right;
\r
192 AdjustToSnapPoint( snapData->y_grid, snapData->y_grid_len, lprc->top, &snap_size_y, &delta_y );
\r
193 lprc->top += delta_y; side = 3; loc = lprc->top;
\r
196 AdjustToSnapPoint( snapData->y_grid, snapData->y_grid_len, lprc->top, &snap_size_y, &delta_y );
\r
197 lprc->top += delta_y;
\r
198 AdjustToSnapPoint( snapData->x_grid, snapData->x_grid_len, lprc->left, &snap_size_x, &delta_x );
\r
199 lprc->left += delta_x;
\r
201 case WMSZ_TOPRIGHT:
\r
202 AdjustToSnapPoint( snapData->y_grid, snapData->y_grid_len, lprc->top, &snap_size_y, &delta_y );
\r
203 lprc->top += delta_y;
\r
204 AdjustToSnapPoint( snapData->x_grid, snapData->x_grid_len, lprc->right, &snap_size_x, &delta_x );
\r
205 lprc->right += delta_x;
\r
214 static int Adjust( LONG *data, int new, int old , int vertical)
\r
216 // protect edges that also touch main window
\r
217 if(!vertical && (old == mainRect.left || old == mainRect.right)) return 0;
\r
218 if( vertical && (old == mainRect.top || old == mainRect.bottom)) return 0;
\r
219 // if the coordinate was the same as the old, now make it the same as the new edge position
\r
220 if(*data == old) { *data = new; return 1; }
\r
224 static void KeepTouching( int side, int new, int old, HWND hWnd )
\r
225 { // if the mentioned window was touching on the moved edge, move its touching edge too
\r
226 if( IsWindowVisible(hWnd) ) {
\r
230 GetWindowRect( hWnd, &rc );
\r
232 switch(side) { // figure out which edge we might need to drag along (if any)
\r
233 case 1: i = Adjust(&rc.right, new, old, 0); break;
\r
234 case 2: i = Adjust(&rc.left, new, old, 0); break;
\r
235 case 3: i = Adjust(&rc.bottom, new, old, 1); break;
\r
236 case 4: i = Adjust(&rc.top, new, old, 1); break;
\r
239 if(i) { // the correct edge was touching, and is adjusted
\r
240 SetWindowPos(hWnd, HWND_TOP, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOZORDER );
\r
245 LRESULT OnExitSizeMove( SnapData * snapData, HWND hWnd, WPARAM wParam, LPARAM lParam )
\r
247 if(side && hWnd != hwndMain) { // [HGM] glue: we have been sizing, by dragging an edge
\r
248 int *grid = (side > 2 ? snapData->y_grid : snapData->x_grid);
\r
249 int i, pos = -1, len = (side > 2 ? snapData->y_grid_len : snapData->x_grid_len);
\r
252 case 1: pos = activeRect.left; break;
\r
253 case 2: pos = activeRect.right; break;
\r
254 case 3: pos = activeRect.top; break;
\r
255 case 4: pos = activeRect.bottom; break;
\r
258 for(i=0; i<len; i++) {
\r
259 if(grid[i] == pos) break; // the dragged side originally touched another auxiliary window
\r
262 if(i < len) { // we were touching another sticky window: figure out how, and adapt it if needed
\r
263 KeepTouching(side, loc, pos, moveHistoryDialog);
\r
264 KeepTouching(side, loc, pos, evalGraphDialog);
\r
265 KeepTouching(side, loc, pos, engineOutputDialog);
\r
266 KeepTouching(side, loc, pos, gameListDialog);
\r