user: Menu drawing fixes.
[wine] / dlls / user / caret.c
1 /*
2  * Caret functions
3  *
4  * Copyright 1993 David Metcalfe
5  * Copyright 1996 Frans van Dorsselaer
6  * Copyright 2001 Eric Pouech
7  * Copyright 2002 Alexandre Julliard
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library 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 GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  */
23
24 #include "config.h"
25 #include "wine/port.h"
26
27 #include <stdarg.h>
28
29 #include "windef.h"
30 #include "winbase.h"
31 #include "wingdi.h"
32 #include "winuser.h"
33 #include "wine/server.h"
34 #include "wine/debug.h"
35
36 WINE_DEFAULT_DEBUG_CHANNEL(caret);
37
38 typedef struct
39 {
40     HBITMAP  hBmp;
41     UINT     timeout;
42 } CARET;
43
44 static CARET Caret = { 0, 500 };
45
46 #define TIMERID 0xffff  /* system timer id for the caret */
47
48
49 /*****************************************************************
50  *               CARET_DisplayCaret
51  */
52 static void CARET_DisplayCaret( HWND hwnd, const RECT *r )
53 {
54     HDC hdc;
55     HDC hCompDC;
56
57     /* do not use DCX_CACHE here, for x,y,width,height are in logical units */
58     if (!(hdc = GetDCEx( hwnd, 0, DCX_USESTYLE /*| DCX_CACHE*/ ))) return;
59     hCompDC = CreateCompatibleDC(hdc);
60     if (hCompDC)
61     {
62         HBITMAP hPrevBmp;
63
64         hPrevBmp = SelectObject(hCompDC, Caret.hBmp);
65         BitBlt(hdc, r->left, r->top, r->right-r->left, r->bottom-r->top, hCompDC, 0, 0, SRCINVERT);
66         SelectObject(hCompDC, hPrevBmp);
67         DeleteDC(hCompDC);
68     }
69     ReleaseDC( hwnd, hdc );
70 }
71
72
73 /*****************************************************************
74  *               CARET_Callback
75  */
76 static void CALLBACK CARET_Callback( HWND hwnd, UINT msg, UINT_PTR id, DWORD ctime)
77 {
78     BOOL ret;
79     RECT r;
80     int old_state = 0;
81     int hidden = 0;
82
83     SERVER_START_REQ( set_caret_info )
84     {
85         req->flags  = SET_CARET_STATE;
86         req->handle = hwnd;
87         req->x      = 0;
88         req->y      = 0;
89         req->hide   = 0;
90         req->state  = -1;  /* toggle current state */
91         if ((ret = !wine_server_call( req )))
92         {
93             hwnd      = reply->full_handle;
94             r.left    = reply->old_rect.left;
95             r.top     = reply->old_rect.top;
96             r.right   = reply->old_rect.right;
97             r.bottom  = reply->old_rect.bottom;
98             old_state = reply->old_state;
99             hidden    = reply->old_hide;
100         }
101     }
102     SERVER_END_REQ;
103
104     if (ret && !hidden) CARET_DisplayCaret( hwnd, &r );
105 }
106
107
108 /*****************************************************************
109  *              CreateCaret (USER32.@)
110  */
111 BOOL WINAPI CreateCaret( HWND hwnd, HBITMAP bitmap, INT width, INT height )
112 {
113     BOOL ret;
114     RECT r;
115     int old_state = 0;
116     int hidden = 0;
117     HBITMAP hBmp = 0;
118     HWND prev = 0;
119
120     TRACE("hwnd=%p\n", hwnd);
121
122     if (!hwnd) return FALSE;
123
124     if (bitmap && (bitmap != (HBITMAP)1))
125     {
126         BITMAP bmp;
127         if (!GetObjectA( bitmap, sizeof(bmp), &bmp )) return FALSE;
128         width = bmp.bmWidth;
129         height = bmp.bmHeight;
130         bmp.bmBits = NULL;
131         hBmp = CreateBitmapIndirect(&bmp);
132         if (hBmp)
133         {
134             /* copy the bitmap */
135             LPBYTE buf = HeapAlloc(GetProcessHeap(), 0, bmp.bmWidthBytes * bmp.bmHeight);
136             GetBitmapBits(bitmap, bmp.bmWidthBytes * bmp.bmHeight, buf);
137             SetBitmapBits(hBmp, bmp.bmWidthBytes * bmp.bmHeight, buf);
138             HeapFree(GetProcessHeap(), 0, buf);
139         }
140     }
141     else
142     {
143         HDC hdc;
144
145         if (!width) width = GetSystemMetrics(SM_CXBORDER);
146         if (!height) height = GetSystemMetrics(SM_CYBORDER);
147
148         /* create the uniform bitmap on the fly */
149         hdc = GetDC(hwnd);
150         if (hdc)
151         {
152             HDC hMemDC = CreateCompatibleDC(hdc);
153             if (hMemDC)
154             {
155                 if ((hBmp = CreateCompatibleBitmap(hMemDC, width, height )))
156                 {
157                     HBITMAP hPrevBmp = SelectObject(hMemDC, hBmp);
158                     SetRect( &r, 0, 0, width, height );
159                     FillRect(hMemDC, &r, (HBRUSH)((bitmap ? COLOR_GRAYTEXT : COLOR_WINDOW) + 1));
160                     SelectObject(hMemDC, hPrevBmp);
161                 }
162                 DeleteDC(hMemDC);
163             }
164             ReleaseDC(hwnd, hdc);
165         }
166     }
167     if (!hBmp) return FALSE;
168
169     SERVER_START_REQ( set_caret_window )
170     {
171         req->handle = hwnd;
172         req->width  = width;
173         req->height = height;
174         if ((ret = !wine_server_call_err( req )))
175         {
176             prev      = reply->previous;
177             r.left    = reply->old_rect.left;
178             r.top     = reply->old_rect.top;
179             r.right   = reply->old_rect.right;
180             r.bottom  = reply->old_rect.bottom;
181             old_state = reply->old_state;
182             hidden    = reply->old_hide;
183         }
184     }
185     SERVER_END_REQ;
186     if (!ret) return FALSE;
187
188     if (prev && !hidden)  /* hide the previous one */
189     {
190         /* FIXME: won't work if prev belongs to a different process */
191         KillSystemTimer( prev, TIMERID );
192         if (old_state) CARET_DisplayCaret( prev, &r );
193     }
194
195     if (Caret.hBmp) DeleteObject( Caret.hBmp );
196     Caret.hBmp = hBmp;
197     Caret.timeout = GetProfileIntA( "windows", "CursorBlinkRate", 500 );
198     return TRUE;
199 }
200
201
202 /*****************************************************************
203  *              DestroyCaret (USER32.@)
204  */
205 BOOL WINAPI DestroyCaret(void)
206 {
207     BOOL ret;
208     HWND prev = 0;
209     RECT r;
210     int old_state = 0;
211     int hidden = 0;
212
213     SERVER_START_REQ( set_caret_window )
214     {
215         req->handle = 0;
216         req->width  = 0;
217         req->height = 0;
218         if ((ret = !wine_server_call_err( req )))
219         {
220             prev      = reply->previous;
221             r.left    = reply->old_rect.left;
222             r.top     = reply->old_rect.top;
223             r.right   = reply->old_rect.right;
224             r.bottom  = reply->old_rect.bottom;
225             old_state = reply->old_state;
226             hidden    = reply->old_hide;
227         }
228     }
229     SERVER_END_REQ;
230
231     if (ret && prev && !hidden)
232     {
233         /* FIXME: won't work if prev belongs to a different process */
234         KillSystemTimer( prev, TIMERID );
235         if (old_state) CARET_DisplayCaret( prev, &r );
236     }
237     if (Caret.hBmp) DeleteObject( Caret.hBmp );
238     Caret.hBmp = 0;
239     return ret;
240 }
241
242
243 /*****************************************************************
244  *              SetCaretPos (USER32.@)
245  */
246 BOOL WINAPI SetCaretPos( INT x, INT y )
247 {
248     BOOL ret;
249     HWND hwnd = 0;
250     RECT r;
251     int old_state = 0;
252     int hidden = 0;
253
254     SERVER_START_REQ( set_caret_info )
255     {
256         req->flags  = SET_CARET_POS|SET_CARET_STATE;
257         req->handle = 0;
258         req->x      = x;
259         req->y      = y;
260         req->hide   = 0;
261         req->state  = 1;
262         if ((ret = !wine_server_call_err( req )))
263         {
264             hwnd      = reply->full_handle;
265             r.left    = reply->old_rect.left;
266             r.top     = reply->old_rect.top;
267             r.right   = reply->old_rect.right;
268             r.bottom  = reply->old_rect.bottom;
269             old_state = reply->old_state;
270             hidden    = reply->old_hide;
271         }
272     }
273     SERVER_END_REQ;
274     if (ret && !hidden)
275     {
276         if (old_state) CARET_DisplayCaret( hwnd, &r );
277         r.right += x - r.left;
278         r.bottom += y - r.top;
279         r.left = x;
280         r.top = y;
281         CARET_DisplayCaret( hwnd, &r );
282         SetSystemTimer( hwnd, TIMERID, Caret.timeout, CARET_Callback );
283     }
284     return ret;
285 }
286
287
288 /*****************************************************************
289  *              HideCaret (USER32.@)
290  */
291 BOOL WINAPI HideCaret( HWND hwnd )
292 {
293     BOOL ret;
294     RECT r;
295     int old_state = 0;
296     int hidden = 0;
297
298     SERVER_START_REQ( set_caret_info )
299     {
300         req->flags  = SET_CARET_HIDE|SET_CARET_STATE;
301         req->handle = hwnd;
302         req->x      = 0;
303         req->y      = 0;
304         req->hide   = 1;
305         req->state  = 0;
306         if ((ret = !wine_server_call_err( req )))
307         {
308             hwnd      = reply->full_handle;
309             r.left    = reply->old_rect.left;
310             r.top     = reply->old_rect.top;
311             r.right   = reply->old_rect.right;
312             r.bottom  = reply->old_rect.bottom;
313             old_state = reply->old_state;
314             hidden    = reply->old_hide;
315         }
316     }
317     SERVER_END_REQ;
318
319     if (ret && !hidden)
320     {
321         if (old_state) CARET_DisplayCaret( hwnd, &r );
322         KillSystemTimer( hwnd, TIMERID );
323     }
324     return ret;
325 }
326
327
328 /*****************************************************************
329  *              ShowCaret (USER32.@)
330  */
331 BOOL WINAPI ShowCaret( HWND hwnd )
332 {
333     BOOL ret;
334     RECT r;
335     int old_state = 0;
336     int hidden = 0;
337
338     SERVER_START_REQ( set_caret_info )
339     {
340         req->flags  = SET_CARET_HIDE|SET_CARET_STATE;
341         req->handle = hwnd;
342         req->x      = 0;
343         req->y      = 0;
344         req->hide   = -1;
345         req->state  = 1;
346         if ((ret = !wine_server_call_err( req )))
347         {
348             hwnd      = reply->full_handle;
349             r.left    = reply->old_rect.left;
350             r.top     = reply->old_rect.top;
351             r.right   = reply->old_rect.right;
352             r.bottom  = reply->old_rect.bottom;
353             old_state = reply->old_state;
354             hidden    = reply->old_hide;
355         }
356     }
357     SERVER_END_REQ;
358
359     if (ret && (hidden == 1))  /* hidden was 1 so it's now 0 */
360     {
361         CARET_DisplayCaret( hwnd, &r );
362         SetSystemTimer( hwnd, TIMERID, Caret.timeout, CARET_Callback );
363     }
364     return ret;
365 }
366
367
368 /*****************************************************************
369  *              GetCaretPos (USER32.@)
370  */
371 BOOL WINAPI GetCaretPos( LPPOINT pt )
372 {
373     BOOL ret;
374
375     SERVER_START_REQ( set_caret_info )
376     {
377         req->flags  = 0;  /* don't set anything */
378         req->handle = 0;
379         req->x      = 0;
380         req->y      = 0;
381         req->hide   = 0;
382         req->state  = 0;
383         if ((ret = !wine_server_call_err( req )))
384         {
385             pt->x = reply->old_rect.left;
386             pt->y = reply->old_rect.top;
387         }
388     }
389     SERVER_END_REQ;
390     return ret;
391 }
392
393
394 /*****************************************************************
395  *              SetCaretBlinkTime (USER32.@)
396  */
397 BOOL WINAPI SetCaretBlinkTime( UINT msecs )
398 {
399     TRACE("msecs=%d\n", msecs);
400
401     Caret.timeout = msecs;
402 /*    if (Caret.hwnd) CARET_SetTimer(); FIXME */
403     return TRUE;
404 }
405
406
407 /*****************************************************************
408  *              GetCaretBlinkTime (USER32.@)
409  */
410 UINT WINAPI GetCaretBlinkTime(void)
411 {
412     return Caret.timeout;
413 }