Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
wine-mirror
GitHub Repository: wine-mirror/wine
Path: blob/master/dlls/comctl32/pager.c
5968 views
1
/*
2
* Pager control
3
*
4
* Copyright 1998, 1999 Eric Kohl
5
*
6
* This library is free software; you can redistribute it and/or
7
* modify it under the terms of the GNU Lesser General Public
8
* License as published by the Free Software Foundation; either
9
* version 2.1 of the License, or (at your option) any later version.
10
*
11
* This library is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
* Lesser General Public License for more details.
15
*
16
* You should have received a copy of the GNU Lesser General Public
17
* License along with this library; if not, write to the Free Software
18
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19
*
20
* NOTES
21
*
22
* This code was audited for completeness against the documented features
23
* of Comctl32.dll version 6.0 on Sep. 18, 2004, by Robert Shearman.
24
*
25
* Unless otherwise noted, we believe this code to be complete, as per
26
* the specification mentioned above.
27
* If you discover missing features or bugs please note them below.
28
*
29
* TODO:
30
* Implement repetitive button press.
31
* Adjust arrow size relative to size of button.
32
* Allow border size changes.
33
* Styles:
34
* PGS_DRAGNDROP
35
* Notifications:
36
* PGN_HOTITEMCHANGE
37
* Messages:
38
* WM_PRINT and/or WM_PRINTCLIENT
39
*
40
* TESTING:
41
* Tested primarily with the controlspy Pager application.
42
* Susan Farley ([email protected])
43
*
44
* IMPLEMENTATION NOTES:
45
* This control uses WM_NCPAINT instead of WM_PAINT to paint itself
46
* as we need to scroll a child window. In order to do this we move
47
* the child window in the control's client area, using the clipping
48
* region that is automatically set around the client area. As the
49
* entire client area now consists of the child window, we must
50
* allocate space (WM_NCCALCSIZE) for the buttons and draw them as
51
* a non-client area (WM_NCPAINT).
52
* Robert Shearman <[email protected]>
53
*/
54
55
#include <stdarg.h>
56
#include <stdlib.h>
57
#include <string.h>
58
#include "windef.h"
59
#include "winbase.h"
60
#include "wingdi.h"
61
#include "winuser.h"
62
#include "winnls.h"
63
#include "commctrl.h"
64
#include "comctl32.h"
65
#include "wine/debug.h"
66
67
WINE_DEFAULT_DEBUG_CHANNEL(pager);
68
69
typedef struct
70
{
71
HWND hwndSelf; /* handle of the control wnd */
72
HWND hwndChild; /* handle of the contained wnd */
73
HWND hwndNotify; /* handle of the parent wnd */
74
BOOL bUnicode; /* send notifications in Unicode */
75
DWORD dwStyle; /* styles for this control */
76
COLORREF clrBk; /* background color */
77
INT nBorder; /* border size for the control */
78
INT nButtonSize;/* size of the pager btns */
79
INT nPos; /* scroll position */
80
INT nWidth; /* from child wnd's response to PGN_CALCSIZE */
81
INT nHeight; /* from child wnd's response to PGN_CALCSIZE */
82
BOOL bForward; /* forward WM_MOUSEMOVE msgs to the contained wnd */
83
BOOL bCapture; /* we have captured the mouse */
84
INT TLbtnState; /* state of top or left btn */
85
INT BRbtnState; /* state of bottom or right btn */
86
INT direction; /* direction of the scroll, (e.g. PGF_SCROLLUP) */
87
WCHAR *pwszBuffer;/* text buffer for converted notifications */
88
DWORD nBufferSize;/* size of the above buffer measured in bytes */
89
} PAGER_INFO;
90
91
#define TIMERID1 1
92
#define TIMERID2 2
93
#define INITIAL_DELAY 500
94
#define REPEAT_DELAY 50
95
96
static void
97
PAGER_GetButtonRects(const PAGER_INFO* infoPtr, RECT* prcTopLeft, RECT* prcBottomRight, BOOL bClientCoords)
98
{
99
RECT rcWindow;
100
GetWindowRect (infoPtr->hwndSelf, &rcWindow);
101
102
if (bClientCoords)
103
MapWindowPoints( 0, infoPtr->hwndSelf, (POINT *)&rcWindow, 2 );
104
else
105
OffsetRect(&rcWindow, -rcWindow.left, -rcWindow.top);
106
107
*prcTopLeft = *prcBottomRight = rcWindow;
108
if (infoPtr->dwStyle & PGS_HORZ)
109
{
110
prcTopLeft->right = prcTopLeft->left + infoPtr->nButtonSize;
111
prcBottomRight->left = prcBottomRight->right - infoPtr->nButtonSize;
112
}
113
else
114
{
115
prcTopLeft->bottom = prcTopLeft->top + infoPtr->nButtonSize;
116
prcBottomRight->top = prcBottomRight->bottom - infoPtr->nButtonSize;
117
}
118
}
119
120
static void
121
PAGER_DrawButton(HDC hdc, COLORREF clrBk, RECT rc,
122
BOOL horz, BOOL topLeft, INT btnState)
123
{
124
UINT flags;
125
126
TRACE("rc = %s, btnState = %d\n", wine_dbgstr_rect(&rc), btnState);
127
128
if (btnState == PGF_INVISIBLE)
129
return;
130
131
if ((rc.right - rc.left <= 0) || (rc.bottom - rc.top <= 0))
132
return;
133
134
if (horz)
135
flags = topLeft ? DFCS_SCROLLLEFT : DFCS_SCROLLRIGHT;
136
else
137
flags = topLeft ? DFCS_SCROLLUP : DFCS_SCROLLDOWN;
138
139
switch (btnState)
140
{
141
case PGF_HOT:
142
break;
143
case PGF_NORMAL:
144
flags |= DFCS_FLAT;
145
break;
146
case PGF_DEPRESSED:
147
flags |= DFCS_PUSHED;
148
break;
149
case PGF_GRAYED:
150
flags |= DFCS_INACTIVE | DFCS_FLAT;
151
break;
152
}
153
DrawFrameControl( hdc, &rc, DFC_SCROLL, flags );
154
}
155
156
/* << PAGER_GetDropTarget >> */
157
158
static inline LRESULT
159
PAGER_ForwardMouse (PAGER_INFO* infoPtr, BOOL bFwd)
160
{
161
TRACE("[%p]\n", infoPtr->hwndSelf);
162
163
infoPtr->bForward = bFwd;
164
165
return 0;
166
}
167
168
static inline LRESULT
169
PAGER_GetButtonState (const PAGER_INFO* infoPtr, INT btn)
170
{
171
LRESULT btnState = PGF_INVISIBLE;
172
TRACE("[%p]\n", infoPtr->hwndSelf);
173
174
if (btn == PGB_TOPORLEFT)
175
btnState = infoPtr->TLbtnState;
176
else if (btn == PGB_BOTTOMORRIGHT)
177
btnState = infoPtr->BRbtnState;
178
179
return btnState;
180
}
181
182
183
static inline INT
184
PAGER_GetPos(const PAGER_INFO *infoPtr)
185
{
186
TRACE("[%p] returns %d\n", infoPtr->hwndSelf, infoPtr->nPos);
187
return infoPtr->nPos;
188
}
189
190
static inline INT
191
PAGER_GetButtonSize(const PAGER_INFO *infoPtr)
192
{
193
TRACE("[%p] returns %d\n", infoPtr->hwndSelf, infoPtr->nButtonSize);
194
return infoPtr->nButtonSize;
195
}
196
197
static inline INT
198
PAGER_GetBorder(const PAGER_INFO *infoPtr)
199
{
200
TRACE("[%p] returns %d\n", infoPtr->hwndSelf, infoPtr->nBorder);
201
return infoPtr->nBorder;
202
}
203
204
static inline COLORREF
205
PAGER_GetBkColor(const PAGER_INFO *infoPtr)
206
{
207
TRACE("[%p] returns %#lx\n", infoPtr->hwndSelf, infoPtr->clrBk);
208
return infoPtr->clrBk;
209
}
210
211
static void
212
PAGER_CalcSize( PAGER_INFO *infoPtr )
213
{
214
NMPGCALCSIZE nmpgcs;
215
ZeroMemory (&nmpgcs, sizeof (NMPGCALCSIZE));
216
nmpgcs.hdr.hwndFrom = infoPtr->hwndSelf;
217
nmpgcs.hdr.idFrom = GetWindowLongPtrW (infoPtr->hwndSelf, GWLP_ID);
218
nmpgcs.hdr.code = PGN_CALCSIZE;
219
nmpgcs.dwFlag = (infoPtr->dwStyle & PGS_HORZ) ? PGF_CALCWIDTH : PGF_CALCHEIGHT;
220
nmpgcs.iWidth = infoPtr->nWidth;
221
nmpgcs.iHeight = infoPtr->nHeight;
222
SendMessageW (infoPtr->hwndNotify, WM_NOTIFY, nmpgcs.hdr.idFrom, (LPARAM)&nmpgcs);
223
224
if (infoPtr->dwStyle & PGS_HORZ)
225
infoPtr->nWidth = nmpgcs.iWidth;
226
else
227
infoPtr->nHeight = nmpgcs.iHeight;
228
229
TRACE("[%p] PGN_CALCSIZE returns %dx%d\n", infoPtr->hwndSelf, nmpgcs.iWidth, nmpgcs.iHeight );
230
}
231
232
static void
233
PAGER_PositionChildWnd(PAGER_INFO* infoPtr)
234
{
235
if (infoPtr->hwndChild)
236
{
237
RECT rcClient;
238
int nPos = infoPtr->nPos;
239
240
/* compensate for a grayed btn, which will soon become invisible */
241
if (infoPtr->TLbtnState == PGF_GRAYED)
242
nPos += infoPtr->nButtonSize;
243
244
GetClientRect(infoPtr->hwndSelf, &rcClient);
245
246
if (infoPtr->dwStyle & PGS_HORZ)
247
{
248
int wndSize = max(0, rcClient.right - rcClient.left);
249
if (infoPtr->nWidth < wndSize)
250
infoPtr->nWidth = wndSize;
251
252
TRACE("[%p] SWP %dx%d at (%d,%d)\n", infoPtr->hwndSelf,
253
infoPtr->nWidth, infoPtr->nHeight,
254
-nPos, 0);
255
SetWindowPos(infoPtr->hwndChild, HWND_TOP,
256
-nPos, 0,
257
infoPtr->nWidth, infoPtr->nHeight, 0);
258
}
259
else
260
{
261
int wndSize = max(0, rcClient.bottom - rcClient.top);
262
if (infoPtr->nHeight < wndSize)
263
infoPtr->nHeight = wndSize;
264
265
TRACE("[%p] SWP %dx%d at (%d,%d)\n", infoPtr->hwndSelf,
266
infoPtr->nWidth, infoPtr->nHeight,
267
0, -nPos);
268
SetWindowPos(infoPtr->hwndChild, HWND_TOP,
269
0, -nPos,
270
infoPtr->nWidth, infoPtr->nHeight, 0);
271
}
272
273
InvalidateRect(infoPtr->hwndChild, NULL, TRUE);
274
}
275
}
276
277
static INT
278
PAGER_GetScrollRange(PAGER_INFO* infoPtr, BOOL calc_size)
279
{
280
INT scrollRange = 0;
281
282
if (infoPtr->hwndChild)
283
{
284
INT wndSize, childSize;
285
RECT wndRect;
286
GetWindowRect(infoPtr->hwndSelf, &wndRect);
287
288
if (calc_size)
289
PAGER_CalcSize(infoPtr);
290
if (infoPtr->dwStyle & PGS_HORZ)
291
{
292
wndSize = wndRect.right - wndRect.left;
293
childSize = infoPtr->nWidth;
294
}
295
else
296
{
297
wndSize = wndRect.bottom - wndRect.top;
298
childSize = infoPtr->nHeight;
299
}
300
301
TRACE("childSize = %d, wndSize = %d\n", childSize, wndSize);
302
if (childSize > wndSize)
303
scrollRange = childSize - wndSize + infoPtr->nButtonSize;
304
}
305
306
TRACE("[%p] returns %d\n", infoPtr->hwndSelf, scrollRange);
307
return scrollRange;
308
}
309
310
static void
311
PAGER_UpdateBtns(PAGER_INFO *infoPtr, INT scrollRange, BOOL hideGrayBtns)
312
{
313
BOOL resizeClient;
314
BOOL repaintBtns;
315
INT oldTLbtnState = infoPtr->TLbtnState;
316
INT oldBRbtnState = infoPtr->BRbtnState;
317
POINT pt;
318
RECT rcTopLeft, rcBottomRight;
319
320
/* get button rects */
321
PAGER_GetButtonRects(infoPtr, &rcTopLeft, &rcBottomRight, TRUE);
322
323
GetCursorPos(&pt);
324
ScreenToClient( infoPtr->hwndSelf, &pt );
325
326
/* update states based on scroll position */
327
if (infoPtr->nPos > 0)
328
{
329
if (infoPtr->TLbtnState == PGF_INVISIBLE || infoPtr->TLbtnState == PGF_GRAYED)
330
infoPtr->TLbtnState = PGF_NORMAL;
331
}
332
else if (!hideGrayBtns && PtInRect(&rcTopLeft, pt))
333
infoPtr->TLbtnState = PGF_GRAYED;
334
else
335
infoPtr->TLbtnState = PGF_INVISIBLE;
336
337
if (scrollRange <= 0)
338
{
339
infoPtr->TLbtnState = PGF_INVISIBLE;
340
infoPtr->BRbtnState = PGF_INVISIBLE;
341
}
342
else if (infoPtr->nPos < scrollRange)
343
{
344
if (infoPtr->BRbtnState == PGF_INVISIBLE || infoPtr->BRbtnState == PGF_GRAYED)
345
infoPtr->BRbtnState = PGF_NORMAL;
346
}
347
else if (!hideGrayBtns && PtInRect(&rcBottomRight, pt))
348
infoPtr->BRbtnState = PGF_GRAYED;
349
else
350
infoPtr->BRbtnState = PGF_INVISIBLE;
351
352
/* only need to resize when entering or leaving PGF_INVISIBLE state */
353
resizeClient =
354
((oldTLbtnState == PGF_INVISIBLE) != (infoPtr->TLbtnState == PGF_INVISIBLE)) ||
355
((oldBRbtnState == PGF_INVISIBLE) != (infoPtr->BRbtnState == PGF_INVISIBLE));
356
/* initiate NCCalcSize to resize client wnd if necessary */
357
if (resizeClient)
358
SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0,
359
SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE |
360
SWP_NOZORDER | SWP_NOACTIVATE);
361
362
/* repaint when changing any state */
363
repaintBtns = (oldTLbtnState != infoPtr->TLbtnState) ||
364
(oldBRbtnState != infoPtr->BRbtnState);
365
if (repaintBtns)
366
SendMessageW(infoPtr->hwndSelf, WM_NCPAINT, 0, 0);
367
}
368
369
static LRESULT
370
PAGER_SetPos(PAGER_INFO* infoPtr, INT newPos, BOOL fromBtnPress, BOOL calc_size)
371
{
372
INT scrollRange = PAGER_GetScrollRange(infoPtr, calc_size);
373
INT oldPos = infoPtr->nPos;
374
375
if ((scrollRange <= 0) || (newPos < 0))
376
infoPtr->nPos = 0;
377
else if (newPos > scrollRange)
378
infoPtr->nPos = scrollRange;
379
else
380
infoPtr->nPos = newPos;
381
382
TRACE("[%p] pos=%d, oldpos=%d\n", infoPtr->hwndSelf, infoPtr->nPos, oldPos);
383
384
if (infoPtr->nPos != oldPos)
385
{
386
/* gray and restore btns, and if from WM_SETPOS, hide the gray btns */
387
PAGER_UpdateBtns(infoPtr, scrollRange, !fromBtnPress);
388
PAGER_PositionChildWnd(infoPtr);
389
}
390
391
return 0;
392
}
393
394
/******************************************************************
395
* For the PGM_RECALCSIZE message (but not the other uses in *
396
* this module), the native control does only the following: *
397
* *
398
* if (some condition) *
399
* PostMessageW(hwnd, EM_FMTLINES, 0, 0); *
400
* return DefWindowProcW(hwnd, PGM_RECALCSIZE, 0, 0); *
401
* *
402
* When we figure out what the "some condition" is we will *
403
* implement that for the message processing. *
404
******************************************************************/
405
406
static LRESULT
407
PAGER_RecalcSize(PAGER_INFO *infoPtr)
408
{
409
TRACE("[%p]\n", infoPtr->hwndSelf);
410
411
if (infoPtr->hwndChild)
412
{
413
INT scrollRange = PAGER_GetScrollRange(infoPtr, TRUE);
414
415
if (scrollRange <= 0)
416
{
417
infoPtr->nPos = -1;
418
PAGER_SetPos(infoPtr, 0, FALSE, TRUE);
419
}
420
else
421
PAGER_PositionChildWnd(infoPtr);
422
}
423
424
return 1;
425
}
426
427
428
static COLORREF
429
PAGER_SetBkColor (PAGER_INFO* infoPtr, COLORREF clrBk)
430
{
431
COLORREF clrTemp = infoPtr->clrBk;
432
433
infoPtr->clrBk = clrBk;
434
TRACE("[%p] %#lx\n", infoPtr->hwndSelf, infoPtr->clrBk);
435
436
/* the native control seems to do things this way */
437
SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0,
438
SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE |
439
SWP_NOZORDER | SWP_NOACTIVATE);
440
441
RedrawWindow(infoPtr->hwndSelf, 0, 0, RDW_ERASE | RDW_INVALIDATE);
442
443
return clrTemp;
444
}
445
446
447
static INT
448
PAGER_SetBorder (PAGER_INFO* infoPtr, INT iBorder)
449
{
450
INT nTemp = infoPtr->nBorder;
451
452
infoPtr->nBorder = iBorder;
453
TRACE("[%p] %d\n", infoPtr->hwndSelf, infoPtr->nBorder);
454
455
PAGER_RecalcSize(infoPtr);
456
457
return nTemp;
458
}
459
460
461
static INT
462
PAGER_SetButtonSize (PAGER_INFO* infoPtr, INT iButtonSize)
463
{
464
INT nTemp = infoPtr->nButtonSize;
465
466
infoPtr->nButtonSize = iButtonSize;
467
TRACE("[%p] %d\n", infoPtr->hwndSelf, infoPtr->nButtonSize);
468
469
PAGER_RecalcSize(infoPtr);
470
471
return nTemp;
472
}
473
474
475
static LRESULT
476
PAGER_SetChild (PAGER_INFO* infoPtr, HWND hwndChild)
477
{
478
infoPtr->hwndChild = IsWindow (hwndChild) ? hwndChild : 0;
479
480
if (infoPtr->hwndChild)
481
{
482
TRACE("[%p] hwndChild=%p\n", infoPtr->hwndSelf, infoPtr->hwndChild);
483
484
SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0,
485
SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
486
487
infoPtr->nPos = -1;
488
PAGER_SetPos(infoPtr, 0, FALSE, FALSE);
489
}
490
491
return 0;
492
}
493
494
static void
495
PAGER_Scroll(PAGER_INFO* infoPtr, INT dir)
496
{
497
NMPGSCROLL nmpgScroll;
498
RECT rcWnd;
499
500
if (infoPtr->hwndChild)
501
{
502
ZeroMemory (&nmpgScroll, sizeof (NMPGSCROLL));
503
nmpgScroll.hdr.hwndFrom = infoPtr->hwndSelf;
504
nmpgScroll.hdr.idFrom = GetWindowLongPtrW (infoPtr->hwndSelf, GWLP_ID);
505
nmpgScroll.hdr.code = PGN_SCROLL;
506
507
GetWindowRect(infoPtr->hwndSelf, &rcWnd);
508
GetClientRect(infoPtr->hwndSelf, &nmpgScroll.rcParent);
509
nmpgScroll.iXpos = nmpgScroll.iYpos = 0;
510
nmpgScroll.iDir = dir;
511
512
if (infoPtr->dwStyle & PGS_HORZ)
513
{
514
nmpgScroll.iScroll = rcWnd.right - rcWnd.left;
515
nmpgScroll.iXpos = infoPtr->nPos;
516
}
517
else
518
{
519
nmpgScroll.iScroll = rcWnd.bottom - rcWnd.top;
520
nmpgScroll.iYpos = infoPtr->nPos;
521
}
522
nmpgScroll.iScroll -= 2*infoPtr->nButtonSize;
523
524
SendMessageW (infoPtr->hwndNotify, WM_NOTIFY, nmpgScroll.hdr.idFrom, (LPARAM)&nmpgScroll);
525
526
TRACE("[%p] PGN_SCROLL returns iScroll=%d\n", infoPtr->hwndSelf, nmpgScroll.iScroll);
527
528
if (nmpgScroll.iScroll > 0)
529
{
530
infoPtr->direction = dir;
531
532
if (dir == PGF_SCROLLLEFT || dir == PGF_SCROLLUP)
533
PAGER_SetPos(infoPtr, infoPtr->nPos - nmpgScroll.iScroll, TRUE, TRUE);
534
else
535
PAGER_SetPos(infoPtr, infoPtr->nPos + nmpgScroll.iScroll, TRUE, TRUE);
536
}
537
else
538
infoPtr->direction = -1;
539
}
540
}
541
542
static LRESULT
543
PAGER_FmtLines(const PAGER_INFO *infoPtr)
544
{
545
/* initiate NCCalcSize to resize client wnd and get size */
546
SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0,
547
SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE |
548
SWP_NOZORDER | SWP_NOACTIVATE);
549
550
SetWindowPos(infoPtr->hwndChild, 0,
551
0,0,infoPtr->nWidth,infoPtr->nHeight,
552
0);
553
554
return DefWindowProcW (infoPtr->hwndSelf, EM_FMTLINES, 0, 0);
555
}
556
557
static LRESULT
558
PAGER_Create (HWND hwnd, const CREATESTRUCTW *lpcs)
559
{
560
PAGER_INFO *infoPtr;
561
INT ret;
562
563
/* allocate memory for info structure */
564
infoPtr = Alloc(sizeof(*infoPtr));
565
if (!infoPtr) return -1;
566
SetWindowLongPtrW (hwnd, 0, (DWORD_PTR)infoPtr);
567
568
/* set default settings */
569
infoPtr->hwndSelf = hwnd;
570
infoPtr->hwndChild = NULL;
571
infoPtr->hwndNotify = lpcs->hwndParent;
572
infoPtr->dwStyle = lpcs->style;
573
infoPtr->clrBk = GetSysColor(COLOR_BTNFACE);
574
infoPtr->nBorder = 0;
575
infoPtr->nButtonSize = 12;
576
infoPtr->nPos = 0;
577
infoPtr->nWidth = 0;
578
infoPtr->nHeight = 0;
579
infoPtr->bForward = FALSE;
580
infoPtr->bCapture = FALSE;
581
infoPtr->TLbtnState = PGF_INVISIBLE;
582
infoPtr->BRbtnState = PGF_INVISIBLE;
583
infoPtr->direction = -1;
584
585
if (infoPtr->dwStyle & PGS_DRAGNDROP)
586
FIXME("[%p] Drag and Drop style is not implemented yet.\n", infoPtr->hwndSelf);
587
588
ret = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
589
infoPtr->bUnicode = (ret == NFR_UNICODE);
590
591
return 0;
592
}
593
594
595
static LRESULT
596
PAGER_Destroy (PAGER_INFO *infoPtr)
597
{
598
SetWindowLongPtrW (infoPtr->hwndSelf, 0, 0);
599
Free (infoPtr->pwszBuffer);
600
Free (infoPtr);
601
return 0;
602
}
603
604
static LRESULT
605
PAGER_NCCalcSize(PAGER_INFO* infoPtr, WPARAM wParam, LPRECT lpRect)
606
{
607
RECT rcChild, rcWindow;
608
609
/*
610
* lpRect points to a RECT struct. On entry, the struct
611
* contains the proposed wnd rectangle for the window.
612
* On exit, the struct should contain the screen
613
* coordinates of the corresponding window's client area.
614
*/
615
616
DefWindowProcW (infoPtr->hwndSelf, WM_NCCALCSIZE, wParam, (LPARAM)lpRect);
617
618
TRACE("orig rect=%s\n", wine_dbgstr_rect(lpRect));
619
620
GetWindowRect (infoPtr->hwndChild, &rcChild);
621
MapWindowPoints (0, infoPtr->hwndSelf, (LPPOINT)&rcChild, 2); /* FIXME: RECT != 2 POINTS */
622
GetWindowRect (infoPtr->hwndSelf, &rcWindow);
623
624
infoPtr->nWidth = lpRect->right - lpRect->left;
625
infoPtr->nHeight = lpRect->bottom - lpRect->top;
626
PAGER_CalcSize( infoPtr );
627
628
if (infoPtr->dwStyle & PGS_HORZ)
629
{
630
if (infoPtr->TLbtnState && (lpRect->left + infoPtr->nButtonSize < lpRect->right))
631
lpRect->left += infoPtr->nButtonSize;
632
if (infoPtr->BRbtnState && (lpRect->right - infoPtr->nButtonSize > lpRect->left))
633
lpRect->right -= infoPtr->nButtonSize;
634
}
635
else
636
{
637
if (infoPtr->TLbtnState && (lpRect->top + infoPtr->nButtonSize < lpRect->bottom))
638
lpRect->top += infoPtr->nButtonSize;
639
if (infoPtr->BRbtnState && (lpRect->bottom - infoPtr->nButtonSize > lpRect->top))
640
lpRect->bottom -= infoPtr->nButtonSize;
641
}
642
643
TRACE("nPos=%d, nHeight=%d, window=%s\n", infoPtr->nPos, infoPtr->nHeight, wine_dbgstr_rect(&rcWindow));
644
TRACE("[%p] client rect set to %s BtnState[%d,%d]\n", infoPtr->hwndSelf, wine_dbgstr_rect(lpRect),
645
infoPtr->TLbtnState, infoPtr->BRbtnState);
646
647
return 0;
648
}
649
650
static LRESULT
651
PAGER_NCPaint (const PAGER_INFO* infoPtr, HRGN hRgn)
652
{
653
RECT rcBottomRight, rcTopLeft;
654
HDC hdc;
655
656
if (infoPtr->dwStyle & WS_MINIMIZE)
657
return 0;
658
659
DefWindowProcW (infoPtr->hwndSelf, WM_NCPAINT, (WPARAM)hRgn, 0);
660
661
if (!(hdc = GetDCEx (infoPtr->hwndSelf, 0, DCX_USESTYLE | DCX_WINDOW)))
662
return 0;
663
664
PAGER_GetButtonRects(infoPtr, &rcTopLeft, &rcBottomRight, FALSE);
665
666
PAGER_DrawButton(hdc, infoPtr->clrBk, rcTopLeft,
667
infoPtr->dwStyle & PGS_HORZ, TRUE, infoPtr->TLbtnState);
668
PAGER_DrawButton(hdc, infoPtr->clrBk, rcBottomRight,
669
infoPtr->dwStyle & PGS_HORZ, FALSE, infoPtr->BRbtnState);
670
671
ReleaseDC( infoPtr->hwndSelf, hdc );
672
return 0;
673
}
674
675
static INT
676
PAGER_HitTest (const PAGER_INFO* infoPtr, const POINT * pt)
677
{
678
RECT clientRect, rcTopLeft, rcBottomRight;
679
POINT ptWindow;
680
681
GetClientRect (infoPtr->hwndSelf, &clientRect);
682
683
if (PtInRect(&clientRect, *pt))
684
{
685
TRACE("child\n");
686
return -1;
687
}
688
689
ptWindow = *pt;
690
PAGER_GetButtonRects(infoPtr, &rcTopLeft, &rcBottomRight, TRUE);
691
692
if ((infoPtr->TLbtnState != PGF_INVISIBLE) && PtInRect(&rcTopLeft, ptWindow))
693
{
694
TRACE("PGB_TOPORLEFT\n");
695
return PGB_TOPORLEFT;
696
}
697
else if ((infoPtr->BRbtnState != PGF_INVISIBLE) && PtInRect(&rcBottomRight, ptWindow))
698
{
699
TRACE("PGB_BOTTOMORRIGHT\n");
700
return PGB_BOTTOMORRIGHT;
701
}
702
703
TRACE("nowhere\n");
704
return -1;
705
}
706
707
static LRESULT
708
PAGER_NCHitTest (const PAGER_INFO* infoPtr, INT x, INT y)
709
{
710
POINT pt;
711
INT nHit;
712
713
pt.x = x;
714
pt.y = y;
715
716
ScreenToClient (infoPtr->hwndSelf, &pt);
717
nHit = PAGER_HitTest(infoPtr, &pt);
718
719
return (nHit < 0) ? HTTRANSPARENT : HTCLIENT;
720
}
721
722
static LRESULT
723
PAGER_MouseMove (PAGER_INFO* infoPtr, INT keys, INT x, INT y)
724
{
725
POINT clpt, pt;
726
RECT wnrect;
727
BOOL topLeft = FALSE;
728
INT btnstate = 0;
729
INT hit;
730
HDC hdc;
731
732
pt.x = x;
733
pt.y = y;
734
735
TRACE("[%p] to (%d,%d)\n", infoPtr->hwndSelf, x, y);
736
ClientToScreen(infoPtr->hwndSelf, &pt);
737
GetWindowRect(infoPtr->hwndSelf, &wnrect);
738
if (PtInRect(&wnrect, pt)) {
739
RECT topleft, bottomright, *rect = NULL;
740
741
PAGER_GetButtonRects(infoPtr, &topleft, &bottomright, FALSE);
742
743
clpt = pt;
744
MapWindowPoints(0, infoPtr->hwndSelf, &clpt, 1);
745
hit = PAGER_HitTest(infoPtr, &clpt);
746
if ((hit == PGB_TOPORLEFT) && (infoPtr->TLbtnState == PGF_NORMAL))
747
{
748
topLeft = TRUE;
749
rect = &topleft;
750
infoPtr->TLbtnState = PGF_HOT;
751
btnstate = infoPtr->TLbtnState;
752
}
753
else if ((hit == PGB_BOTTOMORRIGHT) && (infoPtr->BRbtnState == PGF_NORMAL))
754
{
755
topLeft = FALSE;
756
rect = &bottomright;
757
infoPtr->BRbtnState = PGF_HOT;
758
btnstate = infoPtr->BRbtnState;
759
}
760
761
/* If in one of the buttons the capture and draw buttons */
762
if (rect)
763
{
764
TRACE("[%p] draw btn (%s), Capture %s, style %#lx\n",
765
infoPtr->hwndSelf, wine_dbgstr_rect(rect),
766
(infoPtr->bCapture) ? "TRUE" : "FALSE",
767
infoPtr->dwStyle);
768
if (!infoPtr->bCapture)
769
{
770
TRACE("[%p] SetCapture\n", infoPtr->hwndSelf);
771
SetCapture(infoPtr->hwndSelf);
772
infoPtr->bCapture = TRUE;
773
}
774
if (infoPtr->dwStyle & PGS_AUTOSCROLL)
775
SetTimer(infoPtr->hwndSelf, TIMERID1, 0x3e, 0);
776
hdc = GetWindowDC(infoPtr->hwndSelf);
777
/* OffsetRect(wnrect, 0 | 1, 0 | 1) */
778
PAGER_DrawButton(hdc, infoPtr->clrBk, *rect,
779
infoPtr->dwStyle & PGS_HORZ, topLeft, btnstate);
780
ReleaseDC(infoPtr->hwndSelf, hdc);
781
return 0;
782
}
783
}
784
785
/* If we think we are captured, then do release */
786
if (infoPtr->bCapture && (WindowFromPoint(pt) != infoPtr->hwndSelf))
787
{
788
NMHDR nmhdr;
789
790
infoPtr->bCapture = FALSE;
791
792
if (GetCapture() == infoPtr->hwndSelf)
793
{
794
ReleaseCapture();
795
796
if (infoPtr->TLbtnState == PGF_GRAYED)
797
{
798
infoPtr->TLbtnState = PGF_INVISIBLE;
799
SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0,
800
SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE |
801
SWP_NOZORDER | SWP_NOACTIVATE);
802
}
803
else if (infoPtr->TLbtnState == PGF_HOT)
804
{
805
infoPtr->TLbtnState = PGF_NORMAL;
806
/* FIXME: just invalidate button rect */
807
RedrawWindow(infoPtr->hwndSelf, NULL, NULL, RDW_FRAME | RDW_INVALIDATE);
808
}
809
810
if (infoPtr->BRbtnState == PGF_GRAYED)
811
{
812
infoPtr->BRbtnState = PGF_INVISIBLE;
813
SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0,
814
SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE |
815
SWP_NOZORDER | SWP_NOACTIVATE);
816
}
817
else if (infoPtr->BRbtnState == PGF_HOT)
818
{
819
infoPtr->BRbtnState = PGF_NORMAL;
820
/* FIXME: just invalidate button rect */
821
RedrawWindow(infoPtr->hwndSelf, NULL, NULL, RDW_FRAME | RDW_INVALIDATE);
822
}
823
824
/* Notify parent of released mouse capture */
825
memset(&nmhdr, 0, sizeof(NMHDR));
826
nmhdr.hwndFrom = infoPtr->hwndSelf;
827
nmhdr.idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
828
nmhdr.code = NM_RELEASEDCAPTURE;
829
SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, nmhdr.idFrom, (LPARAM)&nmhdr);
830
}
831
if (IsWindow(infoPtr->hwndSelf))
832
KillTimer(infoPtr->hwndSelf, TIMERID1);
833
}
834
return 0;
835
}
836
837
static LRESULT
838
PAGER_LButtonDown (PAGER_INFO* infoPtr, INT keys, INT x, INT y)
839
{
840
BOOL repaintBtns = FALSE;
841
POINT pt;
842
INT hit;
843
844
pt.x = x;
845
pt.y = y;
846
847
TRACE("[%p] at (%d,%d)\n", infoPtr->hwndSelf, x, y);
848
849
hit = PAGER_HitTest(infoPtr, &pt);
850
851
/* put btn in DEPRESSED state */
852
if (hit == PGB_TOPORLEFT)
853
{
854
repaintBtns = infoPtr->TLbtnState != PGF_DEPRESSED;
855
infoPtr->TLbtnState = PGF_DEPRESSED;
856
SetTimer(infoPtr->hwndSelf, TIMERID1, INITIAL_DELAY, 0);
857
}
858
else if (hit == PGB_BOTTOMORRIGHT)
859
{
860
repaintBtns = infoPtr->BRbtnState != PGF_DEPRESSED;
861
infoPtr->BRbtnState = PGF_DEPRESSED;
862
SetTimer(infoPtr->hwndSelf, TIMERID1, INITIAL_DELAY, 0);
863
}
864
865
if (repaintBtns)
866
SendMessageW(infoPtr->hwndSelf, WM_NCPAINT, 0, 0);
867
868
switch(hit)
869
{
870
case PGB_TOPORLEFT:
871
if (infoPtr->dwStyle & PGS_HORZ)
872
{
873
TRACE("[%p] PGF_SCROLLLEFT\n", infoPtr->hwndSelf);
874
PAGER_Scroll(infoPtr, PGF_SCROLLLEFT);
875
}
876
else
877
{
878
TRACE("[%p] PGF_SCROLLUP\n", infoPtr->hwndSelf);
879
PAGER_Scroll(infoPtr, PGF_SCROLLUP);
880
}
881
break;
882
case PGB_BOTTOMORRIGHT:
883
if (infoPtr->dwStyle & PGS_HORZ)
884
{
885
TRACE("[%p] PGF_SCROLLRIGHT\n", infoPtr->hwndSelf);
886
PAGER_Scroll(infoPtr, PGF_SCROLLRIGHT);
887
}
888
else
889
{
890
TRACE("[%p] PGF_SCROLLDOWN\n", infoPtr->hwndSelf);
891
PAGER_Scroll(infoPtr, PGF_SCROLLDOWN);
892
}
893
break;
894
default:
895
break;
896
}
897
898
return 0;
899
}
900
901
static LRESULT
902
PAGER_LButtonUp (PAGER_INFO* infoPtr, INT keys, INT x, INT y)
903
{
904
TRACE("[%p]\n", infoPtr->hwndSelf);
905
906
KillTimer (infoPtr->hwndSelf, TIMERID1);
907
KillTimer (infoPtr->hwndSelf, TIMERID2);
908
909
/* make PRESSED btns NORMAL but don't hide gray btns */
910
if (infoPtr->TLbtnState & (PGF_HOT | PGF_DEPRESSED))
911
infoPtr->TLbtnState = PGF_NORMAL;
912
if (infoPtr->BRbtnState & (PGF_HOT | PGF_DEPRESSED))
913
infoPtr->BRbtnState = PGF_NORMAL;
914
915
return 0;
916
}
917
918
static LRESULT
919
PAGER_Timer (PAGER_INFO* infoPtr, INT nTimerId)
920
{
921
INT dir;
922
923
/* if initial timer, kill it and start the repeat timer */
924
if (nTimerId == TIMERID1) {
925
if (infoPtr->TLbtnState == PGF_HOT)
926
dir = (infoPtr->dwStyle & PGS_HORZ) ?
927
PGF_SCROLLLEFT : PGF_SCROLLUP;
928
else
929
dir = (infoPtr->dwStyle & PGS_HORZ) ?
930
PGF_SCROLLRIGHT : PGF_SCROLLDOWN;
931
TRACE("[%p] TIMERID1: style=%#lx, dir=%d\n",
932
infoPtr->hwndSelf, infoPtr->dwStyle, dir);
933
KillTimer(infoPtr->hwndSelf, TIMERID1);
934
SetTimer(infoPtr->hwndSelf, TIMERID1, REPEAT_DELAY, 0);
935
if (infoPtr->dwStyle & PGS_AUTOSCROLL) {
936
PAGER_Scroll(infoPtr, dir);
937
SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0,
938
SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE |
939
SWP_NOZORDER | SWP_NOACTIVATE);
940
}
941
return 0;
942
943
}
944
945
TRACE("[%p] TIMERID2: dir=%d\n", infoPtr->hwndSelf, infoPtr->direction);
946
KillTimer(infoPtr->hwndSelf, TIMERID2);
947
if (infoPtr->direction > 0) {
948
PAGER_Scroll(infoPtr, infoPtr->direction);
949
SetTimer(infoPtr->hwndSelf, TIMERID2, REPEAT_DELAY, 0);
950
}
951
return 0;
952
}
953
954
static LRESULT PAGER_ThemeChanged (const PAGER_INFO* infoPtr)
955
{
956
InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
957
return 0;
958
}
959
960
static LRESULT
961
PAGER_EraseBackground (const PAGER_INFO* infoPtr, HDC hdc)
962
{
963
POINT pt, ptorig;
964
HWND parent;
965
LRESULT ret;
966
967
pt.x = 0;
968
pt.y = 0;
969
parent = GetParent(infoPtr->hwndSelf);
970
MapWindowPoints(infoPtr->hwndSelf, parent, &pt, 1);
971
OffsetWindowOrgEx (hdc, pt.x, pt.y, &ptorig);
972
ret = SendMessageW (parent, WM_ERASEBKGND, (WPARAM)hdc, 0);
973
SetWindowOrgEx (hdc, ptorig.x, ptorig.y, 0);
974
975
return ret;
976
}
977
978
979
static LRESULT
980
PAGER_Size (PAGER_INFO* infoPtr, INT type, INT x, INT y)
981
{
982
/* note that WM_SIZE is sent whenever NCCalcSize resizes the client wnd */
983
984
TRACE("[%p] %d,%d\n", infoPtr->hwndSelf, x, y);
985
986
if (infoPtr->dwStyle & PGS_HORZ)
987
infoPtr->nHeight = y;
988
else
989
infoPtr->nWidth = x;
990
991
return PAGER_RecalcSize(infoPtr);
992
}
993
994
995
static LRESULT
996
PAGER_StyleChanged(PAGER_INFO *infoPtr, WPARAM wStyleType, const STYLESTRUCT *lpss)
997
{
998
DWORD oldStyle = infoPtr->dwStyle;
999
1000
TRACE("styletype %Ix, styleOld %#lx, styleNew %#lx\n", wStyleType, lpss->styleOld, lpss->styleNew);
1001
1002
if (wStyleType != GWL_STYLE) return 0;
1003
1004
infoPtr->dwStyle = lpss->styleNew;
1005
1006
if ((oldStyle ^ lpss->styleNew) & (PGS_HORZ | PGS_VERT))
1007
{
1008
PAGER_RecalcSize(infoPtr);
1009
}
1010
1011
return 0;
1012
}
1013
1014
static LRESULT PAGER_NotifyFormat(PAGER_INFO *infoPtr, INT command)
1015
{
1016
INT ret;
1017
switch (command)
1018
{
1019
case NF_REQUERY:
1020
ret = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
1021
infoPtr->bUnicode = (ret == NFR_UNICODE);
1022
return ret;
1023
case NF_QUERY:
1024
/* Pager always wants Unicode notifications from children */
1025
return NFR_UNICODE;
1026
default:
1027
return 0;
1028
}
1029
}
1030
1031
static LRESULT PAGER_Notify(PAGER_INFO *infoPtr, NMHDR *hdr)
1032
{
1033
if (infoPtr->bUnicode)
1034
return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, hdr->idFrom, (LPARAM)hdr);
1035
return COMCTL32_forward_notify_to_ansi_window(infoPtr->hwndNotify, hdr, &infoPtr->pwszBuffer, &infoPtr->nBufferSize);
1036
}
1037
1038
static LRESULT WINAPI
1039
PAGER_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1040
{
1041
PAGER_INFO *infoPtr = (PAGER_INFO *)GetWindowLongPtrW(hwnd, 0);
1042
1043
TRACE("%p, %#x, %#Ix, %#Ix\n", hwnd, uMsg, wParam, lParam);
1044
1045
if (!infoPtr && (uMsg != WM_CREATE))
1046
return DefWindowProcW (hwnd, uMsg, wParam, lParam);
1047
1048
switch (uMsg)
1049
{
1050
case EM_FMTLINES:
1051
return PAGER_FmtLines(infoPtr);
1052
1053
case PGM_FORWARDMOUSE:
1054
return PAGER_ForwardMouse (infoPtr, (BOOL)wParam);
1055
1056
case PGM_GETBKCOLOR:
1057
return PAGER_GetBkColor(infoPtr);
1058
1059
case PGM_GETBORDER:
1060
return PAGER_GetBorder(infoPtr);
1061
1062
case PGM_GETBUTTONSIZE:
1063
return PAGER_GetButtonSize(infoPtr);
1064
1065
case PGM_GETPOS:
1066
return PAGER_GetPos(infoPtr);
1067
1068
case PGM_GETBUTTONSTATE:
1069
return PAGER_GetButtonState (infoPtr, (INT)lParam);
1070
1071
/* case PGM_GETDROPTARGET: */
1072
1073
case PGM_RECALCSIZE:
1074
return PAGER_RecalcSize(infoPtr);
1075
1076
case PGM_SETBKCOLOR:
1077
return PAGER_SetBkColor (infoPtr, (COLORREF)lParam);
1078
1079
case PGM_SETBORDER:
1080
return PAGER_SetBorder (infoPtr, (INT)lParam);
1081
1082
case PGM_SETBUTTONSIZE:
1083
return PAGER_SetButtonSize (infoPtr, (INT)lParam);
1084
1085
case PGM_SETCHILD:
1086
return PAGER_SetChild (infoPtr, (HWND)lParam);
1087
1088
case PGM_SETPOS:
1089
return PAGER_SetPos(infoPtr, (INT)lParam, FALSE, TRUE);
1090
1091
case WM_CREATE:
1092
return PAGER_Create (hwnd, (LPCREATESTRUCTW)lParam);
1093
1094
case WM_DESTROY:
1095
return PAGER_Destroy (infoPtr);
1096
1097
case WM_SIZE:
1098
return PAGER_Size (infoPtr, (INT)wParam, (short)LOWORD(lParam), (short)HIWORD(lParam));
1099
1100
case WM_NCPAINT:
1101
return PAGER_NCPaint (infoPtr, (HRGN)wParam);
1102
1103
case WM_STYLECHANGED:
1104
return PAGER_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
1105
1106
case WM_NCCALCSIZE:
1107
return PAGER_NCCalcSize (infoPtr, wParam, (LPRECT)lParam);
1108
1109
case WM_NCHITTEST:
1110
return PAGER_NCHitTest (infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
1111
1112
case WM_MOUSEMOVE:
1113
if (infoPtr->bForward && infoPtr->hwndChild)
1114
PostMessageW(infoPtr->hwndChild, WM_MOUSEMOVE, wParam, lParam);
1115
return PAGER_MouseMove (infoPtr, (INT)wParam, (short)LOWORD(lParam), (short)HIWORD(lParam));
1116
1117
case WM_LBUTTONDOWN:
1118
return PAGER_LButtonDown (infoPtr, (INT)wParam, (short)LOWORD(lParam), (short)HIWORD(lParam));
1119
1120
case WM_LBUTTONUP:
1121
return PAGER_LButtonUp (infoPtr, (INT)wParam, (short)LOWORD(lParam), (short)HIWORD(lParam));
1122
1123
case WM_ERASEBKGND:
1124
return PAGER_EraseBackground (infoPtr, (HDC)wParam);
1125
1126
case WM_TIMER:
1127
return PAGER_Timer (infoPtr, (INT)wParam);
1128
1129
case WM_NOTIFYFORMAT:
1130
return PAGER_NotifyFormat (infoPtr, lParam);
1131
1132
case WM_NOTIFY:
1133
return PAGER_Notify (infoPtr, (NMHDR *)lParam);
1134
1135
case WM_COMMAND:
1136
return SendMessageW (infoPtr->hwndNotify, uMsg, wParam, lParam);
1137
1138
case WM_THEMECHANGED:
1139
return PAGER_ThemeChanged (infoPtr);
1140
1141
default:
1142
return DefWindowProcW (hwnd, uMsg, wParam, lParam);
1143
}
1144
}
1145
1146
1147
VOID
1148
PAGER_Register (void)
1149
{
1150
WNDCLASSW wndClass;
1151
1152
ZeroMemory (&wndClass, sizeof(WNDCLASSW));
1153
wndClass.style = CS_GLOBALCLASS;
1154
wndClass.lpfnWndProc = PAGER_WindowProc;
1155
wndClass.cbClsExtra = 0;
1156
wndClass.cbWndExtra = sizeof(PAGER_INFO *);
1157
wndClass.hCursor = LoadCursorW (0, (LPWSTR)IDC_ARROW);
1158
wndClass.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
1159
wndClass.lpszClassName = WC_PAGESCROLLERW;
1160
1161
RegisterClassW (&wndClass);
1162
}
1163
1164
1165
VOID
1166
PAGER_Unregister (void)
1167
{
1168
UnregisterClassW (WC_PAGESCROLLERW, NULL);
1169
}
1170
1171