e6f6151d991268e49a48130ee78019f2c8374a99
[xboard.git] / winboard / wsnap.c
1 /*
2  * Smart "snapping" for window moving and sizing
3  *
4  * Author: Alessandro Scotti (Dec 2005)
5  *
6  * ------------------------------------------------------------------------
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20  * ------------------------------------------------------------------------
21  */
22 #include "wsnap.h"
23
24 /* Imports from winboard.c */
25 extern HINSTANCE hInst;
26
27 extern HWND hwndMain;
28 extern HWND moveHistoryDialog;
29 extern HWND evalGraphDialog;
30 extern HWND engineOutputDialog;
31 extern HWND gameListDialog;
32
33 static BOOL SnappingEnabled = TRUE;
34
35 static void AddSnapPoint( int * grid, int * grid_len, int value )
36 {
37     int len = *grid_len;
38
39     if( len < MAX_SNAP_POINTS ) {
40         int i;
41
42         for( i=0; i<len; i++ ) {
43             if( grid[i] == value ) {
44                 return;
45             }
46         }
47
48         grid[ len++ ] = value;
49
50         *grid_len = len;
51     }
52 }
53
54 static void AddSnapRectangle( SnapData * sd, RECT * rc )
55 {
56     AddSnapPoint( sd->x_grid, &sd->x_grid_len, rc->left );
57     AddSnapPoint( sd->x_grid, &sd->x_grid_len, rc->right );
58
59     AddSnapPoint( sd->y_grid, &sd->y_grid_len, rc->top );
60     AddSnapPoint( sd->y_grid, &sd->y_grid_len, rc->bottom );
61 }
62
63 static void AddSnapWindow( HWND hWndCaller, SnapData * sd, HWND hWndSnapWindow )
64 {
65     if( hWndSnapWindow != NULL && hWndCaller != hWndSnapWindow && IsWindowVisible(hWndSnapWindow) ) {
66         RECT rc;
67
68         GetWindowRect( hWndSnapWindow, &rc );
69
70         AddSnapRectangle( sd, &rc );
71     }
72 }
73
74 static BOOL AdjustToSnapPoint( int * grid, int grid_len, int value, int * snap_size, int * delta )
75 {
76     BOOL result = FALSE;
77     int i;
78
79     for( i=0; i<grid_len; i++ ) {
80         int distance = value - grid[i];
81
82         if( distance < 0 ) distance = -distance;
83
84         if( distance < *snap_size ) {
85             result = TRUE;
86             *snap_size = distance;
87             *delta = grid[i] - value;
88         }
89     }
90
91     return result;
92 }
93
94 LRESULT OnEnterSizeMove( SnapData * snapData, HWND hWnd, WPARAM wParam, LPARAM lParam )
95 {
96     RECT rc;
97
98     snapData->x_grid_len = 0;
99     snapData->y_grid_len = 0;
100
101     /* Add desktop area */
102     if( SystemParametersInfo( SPI_GETWORKAREA, 0, &rc, 0 ) ) {
103         AddSnapRectangle( snapData, &rc );
104     }
105
106     if( hWnd != hwndMain ) {
107         /* Add other windows */
108         AddSnapWindow( hWnd, snapData, hwndMain );
109         AddSnapWindow( hWnd, snapData, moveHistoryDialog );
110         AddSnapWindow( hWnd, snapData, evalGraphDialog );
111         AddSnapWindow( hWnd, snapData, engineOutputDialog );
112         AddSnapWindow( hWnd, snapData, gameListDialog );
113     }
114
115     return 0;
116 }
117
118 LRESULT OnMoving( SnapData * snapData, HWND hWnd, WPARAM wParam, LPARAM lParam )
119 {
120     LPRECT lprc = (LPRECT) lParam;
121     int delta_x = 0;
122     int delta_y = 0;
123     int snap_size_x = SNAP_DISTANCE;
124     int snap_size_y = SNAP_DISTANCE;
125
126     if( ! SnappingEnabled ) {
127         return FALSE;
128     }
129
130     AdjustToSnapPoint( snapData->x_grid, snapData->x_grid_len, lprc->left, &snap_size_x, &delta_x );
131     AdjustToSnapPoint( snapData->x_grid, snapData->x_grid_len, lprc->right, &snap_size_x, &delta_x );
132
133     AdjustToSnapPoint( snapData->y_grid, snapData->y_grid_len, lprc->top, &snap_size_y, &delta_y );
134     AdjustToSnapPoint( snapData->y_grid, snapData->y_grid_len, lprc->bottom, &snap_size_y, &delta_y );
135
136     OffsetRect( lprc, delta_x, delta_y );
137
138     return TRUE;
139 }
140
141 LRESULT OnSizing( SnapData * snapData, HWND hWnd, WPARAM wParam, LPARAM lParam )
142 {
143     LPRECT lprc = (LPRECT) lParam;
144     int delta_x = 0;
145     int delta_y = 0;
146     int snap_size_x = SNAP_DISTANCE;
147     int snap_size_y = SNAP_DISTANCE;
148
149     if( ! SnappingEnabled ) {
150         return FALSE;
151     }
152
153     switch( wParam ) {
154     case WMSZ_BOTTOM:
155         AdjustToSnapPoint( snapData->y_grid, snapData->y_grid_len, lprc->bottom, &snap_size_y, &delta_y );
156         lprc->bottom += delta_y;
157         break;
158     case WMSZ_BOTTOMLEFT:
159         AdjustToSnapPoint( snapData->y_grid, snapData->y_grid_len, lprc->bottom, &snap_size_y, &delta_y );
160         lprc->bottom += delta_y;
161         AdjustToSnapPoint( snapData->x_grid, snapData->x_grid_len, lprc->left, &snap_size_x, &delta_x );
162         lprc->left += delta_x;
163         break;
164     case WMSZ_BOTTOMRIGHT:
165         AdjustToSnapPoint( snapData->y_grid, snapData->y_grid_len, lprc->bottom, &snap_size_y, &delta_y );
166         lprc->bottom += delta_y;
167         AdjustToSnapPoint( snapData->x_grid, snapData->x_grid_len, lprc->right, &snap_size_x, &delta_x );
168         lprc->right += delta_x;
169         break;
170     case WMSZ_LEFT:
171         AdjustToSnapPoint( snapData->x_grid, snapData->x_grid_len, lprc->left, &snap_size_x, &delta_x );
172         lprc->left += delta_x;
173         break;
174     case WMSZ_RIGHT:
175         AdjustToSnapPoint( snapData->x_grid, snapData->x_grid_len, lprc->right, &snap_size_x, &delta_x );
176         lprc->right += delta_x;
177         break;
178     case WMSZ_TOP:
179         AdjustToSnapPoint( snapData->y_grid, snapData->y_grid_len, lprc->top, &snap_size_y, &delta_y );
180         lprc->top += delta_y;
181         break;
182     case WMSZ_TOPLEFT:
183         AdjustToSnapPoint( snapData->y_grid, snapData->y_grid_len, lprc->top, &snap_size_y, &delta_y );
184         lprc->top += delta_y;
185         AdjustToSnapPoint( snapData->x_grid, snapData->x_grid_len, lprc->left, &snap_size_x, &delta_x );
186         lprc->left += delta_x;
187         break;
188     case WMSZ_TOPRIGHT:
189         AdjustToSnapPoint( snapData->y_grid, snapData->y_grid_len, lprc->top, &snap_size_y, &delta_y );
190         lprc->top += delta_y;
191         AdjustToSnapPoint( snapData->x_grid, snapData->x_grid_len, lprc->right, &snap_size_x, &delta_x );
192         lprc->right += delta_x;
193         break;
194     default:
195         return FALSE;
196     }
197
198     return TRUE;
199 }
200
201 LRESULT OnExitSizeMove( SnapData * snapData, HWND hWnd, WPARAM wParam, LPARAM lParam )
202 {
203     return 0;
204 }