Fixed DC leak.
[wine] / windows / caret.c
1 /*
2  * Caret functions
3  *
4  * Copyright 1993 David Metcalfe
5  * Copyright 1996 Frans van Dorsselaer
6  * Copyright 2001 Eric Pouech
7  */
8
9 #include "windef.h"
10 #include "winbase.h"
11 #include "wingdi.h"
12 #include "winuser.h"
13 #include "wine/wingdi16.h"
14 #include "wine/winuser16.h"
15 #include "win.h"
16 #include "debugtools.h"
17
18 DEFAULT_DEBUG_CHANNEL(caret);
19
20 typedef struct
21 {
22     HWND     hwnd;
23     UINT     hidden;
24     BOOL     on;
25     INT      x;
26     INT      y;
27     INT      width;
28     INT      height;
29     HBITMAP  hBmp;
30     UINT     timeout;
31     UINT     timerid;
32 } CARET;
33
34 typedef enum
35 {
36     CARET_OFF = 0,
37     CARET_ON,
38     CARET_TOGGLE
39 } DISPLAY_CARET;
40
41 static CARET Caret = { 0, 0, FALSE, 0, 0, 2, 12, 0, 500, 0 };
42
43 /*****************************************************************
44  *              CARET_GetHwnd
45  */
46 HWND CARET_GetHwnd(void)
47 {
48     return Caret.hwnd;
49 }
50
51 /*****************************************************************
52  *              CARET_GetRect
53  */
54 void CARET_GetRect(LPRECT lprc)
55 {
56     lprc->right = (lprc->left = Caret.x) + Caret.width - 1;
57     lprc->bottom = (lprc->top = Caret.y) + Caret.height - 1;
58 }
59
60 /*****************************************************************
61  *               CARET_DisplayCaret
62  */
63 static void CARET_DisplayCaret( DISPLAY_CARET status )
64 {
65     HDC hdc;
66     HDC hCompDC;
67
68     if (Caret.on && (status == CARET_ON)) return;
69     if (!Caret.on && (status == CARET_OFF)) return;
70
71     /* So now it's always a toggle */
72
73     Caret.on = !Caret.on;
74     /* do not use DCX_CACHE here, for x,y,width,height are in logical units */
75     if (!(hdc = GetDCEx( Caret.hwnd, 0, DCX_USESTYLE /*| DCX_CACHE*/ ))) return;
76     hCompDC = CreateCompatibleDC(hdc);
77     if (hCompDC)
78     {
79         HBITMAP hPrevBmp;
80
81         hPrevBmp = SelectObject(hCompDC, Caret.hBmp);
82         BitBlt(hdc, Caret.x, Caret.y, Caret.width, Caret.height, hCompDC, 0, 0, SRCINVERT);
83         SelectObject(hCompDC, hPrevBmp);
84         DeleteDC(hCompDC);
85     }
86     ReleaseDC( Caret.hwnd, hdc );
87 }
88
89   
90 /*****************************************************************
91  *               CARET_Callback
92  */
93 static VOID CALLBACK CARET_Callback( HWND hwnd, UINT msg, UINT id, DWORD ctime)
94 {
95     TRACE("hwnd=%04x, timerid=%d, caret=%d\n",
96                   hwnd, id, Caret.on);
97     CARET_DisplayCaret(CARET_TOGGLE);
98 }
99
100
101 /*****************************************************************
102  *               CARET_SetTimer
103  */
104 static void CARET_SetTimer(void)
105 {
106     if (Caret.timerid) KillSystemTimer( (HWND)0, Caret.timerid );
107     Caret.timerid = SetSystemTimer( (HWND)0, 0, Caret.timeout,
108                                       CARET_Callback );
109 }
110
111
112 /*****************************************************************
113  *               CARET_ResetTimer
114  */
115 static void CARET_ResetTimer(void)
116 {
117     if (Caret.timerid) 
118     {
119         KillSystemTimer( (HWND)0, Caret.timerid );
120         Caret.timerid = SetSystemTimer( (HWND)0, 0, Caret.timeout,
121                                           CARET_Callback );
122     }
123 }
124
125
126 /*****************************************************************
127  *               CARET_KillTimer
128  */
129 static void CARET_KillTimer(void)
130 {
131     if (Caret.timerid) 
132     {
133         KillSystemTimer( (HWND)0, Caret.timerid );
134         Caret.timerid = 0;
135     }
136 }
137
138
139 /*****************************************************************
140  *              CreateCaret (USER32.@)
141  */
142 BOOL WINAPI CreateCaret( HWND hwnd, HBITMAP bitmap,
143                              INT width, INT height )
144 {
145     TRACE("hwnd=%04x\n", hwnd);
146
147     if (!hwnd) return FALSE;
148
149     /* if cursor already exists, destroy it */
150     if (Caret.hwnd) DestroyCaret();
151
152     if (bitmap && (bitmap != 1))
153     {
154         BITMAP bmp;
155         if (!GetObjectA( bitmap, sizeof(bmp), &bmp )) return FALSE;
156         Caret.width = bmp.bmWidth;
157         Caret.height = bmp.bmHeight;
158         bmp.bmBits = NULL;
159         Caret.hBmp = CreateBitmapIndirect(&bmp);
160  
161         if (Caret.hBmp) 
162         {
163             /* copy the bitmap */
164             LPBYTE buf = HeapAlloc(GetProcessHeap(), 0, bmp.bmWidthBytes * bmp.bmHeight);
165             GetBitmapBits(bitmap, bmp.bmWidthBytes * bmp.bmHeight, buf);
166             SetBitmapBits(Caret.hBmp, bmp.bmWidthBytes * bmp.bmHeight, buf);
167             HeapFree(GetProcessHeap(), 0, buf);
168         }
169     }
170     else
171     {
172         HDC     hdc;
173
174         Caret.width = width ? width : GetSystemMetrics(SM_CXBORDER);
175         Caret.height = height ? height : GetSystemMetrics(SM_CYBORDER);
176         Caret.hBmp = 0;
177
178         /* create the uniform bitmap on the fly */
179         hdc = GetDC(hwnd);
180         if (hdc)
181         {
182             HDC         hMemDC = CreateCompatibleDC(hdc);
183
184             if (hMemDC)
185             {
186                 RECT    r;
187                 r.left = r.top = 0;
188                 r.right = Caret.width;
189                 r.bottom = Caret.height;
190                     
191                 if ((Caret.hBmp = CreateCompatibleBitmap(hMemDC, Caret.width, Caret.height)))
192                 {
193                     HBITMAP hPrevBmp = SelectObject(hMemDC, Caret.hBmp);
194                     FillRect(hMemDC, &r, (bitmap ? COLOR_GRAYTEXT : COLOR_WINDOW) + 1);
195                     SelectObject(hMemDC, hPrevBmp);
196                 }
197                 DeleteDC(hMemDC);
198             }
199             ReleaseDC(hwnd, hdc);
200         }
201     }
202
203     Caret.hwnd = WIN_GetFullHandle( hwnd );
204     Caret.hidden = 1;
205     Caret.on = FALSE;
206     Caret.x = 0;
207     Caret.y = 0;
208
209     Caret.timeout = GetProfileIntA( "windows", "CursorBlinkRate", 500 );
210     return TRUE;
211 }
212    
213
214 /*****************************************************************
215  *              DestroyCaret (USER.164)
216  */
217 void WINAPI DestroyCaret16(void)
218 {
219     DestroyCaret();
220 }
221
222
223 /*****************************************************************
224  *              DestroyCaret (USER32.@)
225  */
226 BOOL WINAPI DestroyCaret(void)
227 {
228     if (!Caret.hwnd) return FALSE;
229
230     TRACE("hwnd=%04x, timerid=%d\n",
231                 Caret.hwnd, Caret.timerid);
232
233     CARET_KillTimer();
234     CARET_DisplayCaret(CARET_OFF);
235     DeleteObject( Caret.hBmp );
236     Caret.hwnd = 0;
237     return TRUE;
238 }
239
240
241 /*****************************************************************
242  *              SetCaretPos (USER.165)
243  */
244 void WINAPI SetCaretPos16( INT16 x, INT16 y )
245 {
246     SetCaretPos( x, y );
247 }
248
249
250 /*****************************************************************
251  *              SetCaretPos (USER32.@)
252  */
253 BOOL WINAPI SetCaretPos( INT x, INT y)
254 {
255     if (!Caret.hwnd) return FALSE;
256     if ((x == Caret.x) && (y == Caret.y)) return TRUE;
257
258     TRACE("x=%d, y=%d\n", x, y);
259
260     CARET_KillTimer();
261     CARET_DisplayCaret(CARET_OFF);
262     Caret.x = x;
263     Caret.y = y;
264     if (!Caret.hidden)
265     {
266         CARET_DisplayCaret(CARET_ON);
267         CARET_SetTimer();
268     }
269     return TRUE;
270 }
271
272
273 /*****************************************************************
274  *              HideCaret (USER32.@)
275  */
276 BOOL WINAPI HideCaret( HWND hwnd )
277 {
278     if (!Caret.hwnd) return FALSE;
279     if (hwnd && (Caret.hwnd != WIN_GetFullHandle(hwnd))) return FALSE;
280
281     TRACE("hwnd=%04x, hidden=%d\n",
282                   hwnd, Caret.hidden);
283
284     CARET_KillTimer();
285     CARET_DisplayCaret(CARET_OFF);
286     Caret.hidden++;
287     return TRUE;
288 }
289
290
291 /*****************************************************************
292  *              ShowCaret (USER32.@)
293  */
294 BOOL WINAPI ShowCaret( HWND hwnd )
295 {
296     if (!Caret.hwnd) return FALSE;
297     if (hwnd && (Caret.hwnd != WIN_GetFullHandle(hwnd))) return FALSE;
298
299     TRACE("hwnd=%04x, hidden=%d\n",
300                 hwnd, Caret.hidden);
301
302     if (Caret.hidden)
303     {
304         Caret.hidden--;
305         if (!Caret.hidden)
306         {
307             CARET_DisplayCaret(CARET_ON);
308             CARET_SetTimer();
309         }
310     }
311     return TRUE;
312 }
313
314
315 /*****************************************************************
316  *              SetCaretBlinkTime (USER.168)
317  */
318 void WINAPI SetCaretBlinkTime16( UINT16 msecs )
319 {
320     SetCaretBlinkTime( msecs );
321 }
322
323 /*****************************************************************
324  *              SetCaretBlinkTime (USER32.@)
325  */
326 BOOL WINAPI SetCaretBlinkTime( UINT msecs )
327 {
328     if (!Caret.hwnd) return FALSE;
329
330     TRACE("hwnd=%04x, msecs=%d\n",
331                 Caret.hwnd, msecs);
332
333     Caret.timeout = msecs;
334     CARET_ResetTimer();
335     return TRUE;
336 }
337
338
339 /*****************************************************************
340  *              GetCaretBlinkTime (USER.169)
341  */
342 UINT16 WINAPI GetCaretBlinkTime16(void)
343 {
344     return (UINT16)GetCaretBlinkTime();
345 }
346
347
348 /*****************************************************************
349  *              GetCaretBlinkTime (USER32.@)
350  */
351 UINT WINAPI GetCaretBlinkTime(void)
352 {
353     return Caret.timeout;
354 }
355
356
357 /*****************************************************************
358  *              GetCaretPos (USER.183)
359  */
360 VOID WINAPI GetCaretPos16( LPPOINT16 pt )
361 {
362     if (!Caret.hwnd || !pt) return;
363
364     TRACE("hwnd=%04x, pt=%p, x=%d, y=%d\n",
365                   Caret.hwnd, pt, Caret.x, Caret.y);
366     pt->x = (INT16)Caret.x;
367     pt->y = (INT16)Caret.y;
368 }
369
370
371 /*****************************************************************
372  *              GetCaretPos (USER32.@)
373  */
374 BOOL WINAPI GetCaretPos( LPPOINT pt )
375 {
376     if (!Caret.hwnd || !pt) return FALSE;
377     pt->x = Caret.x;
378     pt->y = Caret.y;
379     return TRUE;
380 }