Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1899 lines
54 KiB

  1. //----------------------------------------------------------
  2. //
  3. // BUGBUG: make sure this stuff really works with the DWORD
  4. // ranges
  5. //
  6. //----------------------------------------------------------
  7. #include "ctlspriv.h"
  8. #include "limits.h"
  9. #include "image.h" // for CreateColorBitmap
  10. #if defined(MAINWIN)
  11. #include <mainwin.h>
  12. #endif
  13. //#define TB_DEBUG
  14. //#define FEATURE_DEBUG // Ctrl+Shift force-enables rare features for debugging
  15. typedef struct {
  16. // standard header information for each control
  17. CONTROLINFO ci;
  18. HDC hdc; // current DC
  19. HBITMAP hbmBuffer; // double buffer
  20. LONG lLogMin; // Logical minimum
  21. LONG lLogMax; // Logical maximum
  22. LONG lLogPos; // Logical position
  23. LONG lSelStart; // Logical selection start
  24. LONG lSelEnd; // Logical selection end
  25. int iThumbWidth; // Width of the thumb
  26. int iThumbHeight; // Height of the thumb
  27. int iSizePhys; // Size of where thumb lives
  28. RECT rc; // track bar rect.
  29. RECT rcThumb; // Rectangle we current thumb
  30. DWORD dwDragPos; // Logical position of mouse while dragging.
  31. int dwDragOffset; // how many pixels off the center did they click
  32. int nTics; // number of ticks.
  33. PDWORD pTics; // the tick marks.
  34. int ticFreq; // the frequency of ticks
  35. LONG lPageSize; // how much to thumb up and down.
  36. LONG lLineSize; // how muhc to scroll up and down on line up/down
  37. HWND hwndToolTips;
  38. // these should probably be word or bytes
  39. UINT wDirtyFlags;
  40. UINT uTipSide; // which side should the tip be on?
  41. UINT Flags; // Flags for our window
  42. UINT Cmd; // The command we're repeating.
  43. #if defined(FE_IME) || !defined(WINNT)
  44. HIMC hPrevImc; // previous input context handle
  45. #endif
  46. HWND hwndBuddyLeft;
  47. HWND hwndBuddyRight;
  48. } TRACKBAR, *PTRACKBAR;
  49. // Trackbar flags
  50. #define TBF_NOTHUMB 0x0001 // No thumb because not wide enough.
  51. #define TBF_SELECTION 0x0002 // a selection has been established (draw the range)
  52. #define MIN_THUMB_HEIGHT (2 * g_cxEdge)
  53. /*
  54. useful constants.
  55. */
  56. #define REPEATTIME 500 // mouse auto repeat 1/2 of a second
  57. #define TIMER_ID 1
  58. /*
  59. Function Prototypes
  60. */
  61. void NEAR PASCAL DoTrack(PTRACKBAR, int, DWORD);
  62. WORD NEAR PASCAL WTrackType(PTRACKBAR, LONG);
  63. void NEAR PASCAL TBTrackInit(PTRACKBAR, LPARAM);
  64. void NEAR PASCAL TBTrackEnd(PTRACKBAR);
  65. void NEAR PASCAL TBTrack(PTRACKBAR, LPARAM);
  66. void NEAR PASCAL DrawThumb(PTRACKBAR, LPRECT, BOOL);
  67. HBRUSH NEAR PASCAL SelectColorObjects(PTRACKBAR, BOOL);
  68. void NEAR PASCAL SetTBCaretPos(PTRACKBAR);
  69. #define TICKHEIGHT 3
  70. #define BORDERSIZE 2
  71. #define ISVERT(tb) (tb->ci.style & TBS_VERT)
  72. #define TBC_TICS 0x1
  73. #define TBC_THUMB 0x2
  74. #define TBC_ALL 0xF
  75. // this is called internally when the trackbar has
  76. // changed and we need to update the double buffer bitmap
  77. // we only set a flag. we do the actual draw
  78. // during WM_PAINT. This prevents wasted efforts drawing.
  79. #define TBChanged(ptb, wFlags) ((ptb)->wDirtyFlags |= (wFlags))
  80. //
  81. // Function Prototypes
  82. //
  83. LPARAM FAR CALLBACK TrackBarWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
  84. void NEAR PASCAL FlushChanges(PTRACKBAR tb);
  85. //--------------------------------------------------------------------------;
  86. //
  87. // LONG MulDiv32(a,b,c) = (a * b + c/2) / c
  88. //
  89. //--------------------------------------------------------------------------;
  90. #ifdef WIN32
  91. #define MulDiv32 MulDiv // use KERNEL32 version (it rounds)
  92. #else // WIN32
  93. #define ASM66 _asm _emit 0x66 _asm
  94. #define DB _asm _emit
  95. #define EAX_TO_DXAX \
  96. DB 0x66 \
  97. DB 0x0F \
  98. DB 0xA4 \
  99. DB 0xC2 \
  100. DB 0x10
  101. #pragma warning(disable:4035 4704)
  102. static LONG MulDiv32(LONG a,LONG b,LONG c)
  103. {
  104. ASM66 mov ax,word ptr c // mov eax, c
  105. ASM66 sar ax,1 // sar eax,1
  106. ASM66 cwd // cdq
  107. ASM66 mov bx,ax // mov ebx,eax
  108. ASM66 mov cx,dx // mov ecx,edx
  109. ASM66 mov ax,word ptr a // mov eax, a
  110. ASM66 imul word ptr b // imul b
  111. ASM66 add ax,bx // add eax,ebx
  112. ASM66 adc dx,cx // adc edx,ecx
  113. ASM66 idiv word ptr c // idiv c
  114. EAX_TO_DXAX
  115. } // MulDiv32()
  116. #pragma warning(default:4035 4704)
  117. #endif // WIN32
  118. //--------------------------------------------------------------------------;
  119. //--------------------------------------------------------------------------;
  120. //
  121. // convert a logical scroll-bar position to a physical pixel position
  122. //
  123. int NEAR PASCAL TBLogToPhys(PTRACKBAR tb, DWORD dwPos)
  124. {
  125. int x;
  126. x = tb->rc.left;
  127. if (tb->lLogMax == tb->lLogMin)
  128. return x;
  129. return (int)MulDiv32(dwPos - tb->lLogMin, tb->iSizePhys - 1,
  130. tb->lLogMax - tb->lLogMin) + x;
  131. }
  132. LONG NEAR PASCAL TBPhysToLog(PTRACKBAR ptb, int iPos)
  133. {
  134. int min, max, x;
  135. min = ptb->rc.left;
  136. max = ptb->rc.right;
  137. x = ptb->rc.left;
  138. if (ptb->iSizePhys <= 1)
  139. return ptb->lLogMin;
  140. if (iPos <= min)
  141. return ptb->lLogMin;
  142. if (iPos >= max)
  143. return ptb->lLogMax;
  144. return MulDiv32(iPos - x, ptb->lLogMax - ptb->lLogMin,
  145. ptb->iSizePhys - 1) + ptb->lLogMin;
  146. }
  147. #pragma code_seg(CODESEG_INIT)
  148. /*
  149. * Initialize the trackbar code
  150. */
  151. BOOL FAR PASCAL InitTrackBar(HINSTANCE hInstance)
  152. {
  153. WNDCLASS wc;
  154. // See if we must register a window class
  155. if (!GetClassInfo(hInstance, s_szSTrackBarClass, &wc)) {
  156. #ifndef WIN32
  157. extern LRESULT CALLBACK _TrackBarWndProc(HWND, UINT, WPARAM, LPARAM);
  158. wc.lpfnWndProc = _TrackBarWndProc;
  159. #else
  160. wc.lpfnWndProc = TrackBarWndProc;
  161. #endif
  162. wc.lpszClassName = s_szSTrackBarClass;
  163. wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  164. wc.hIcon = NULL;
  165. wc.lpszMenuName = NULL;
  166. wc.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1);
  167. wc.hInstance = hInstance;
  168. wc.style = CS_GLOBALCLASS;
  169. wc.cbClsExtra = 0;
  170. wc.cbWndExtra = sizeof(PTRACKBAR);
  171. return RegisterClass(&wc);
  172. }
  173. return TRUE;
  174. }
  175. #pragma code_seg()
  176. /*
  177. * To add vertical capabilities, I'm using a virtual coordinate
  178. * system. the ptb->rcThumb and ptb->rc are in the virtual space (which
  179. * is just a horizontal trackbar). Draw routines use PatRect and
  180. * TBBitBlt which switch to the real coordinate system as needed.
  181. *
  182. * The one gotcha is that the Thumb Bitmap has the pressed bitmap
  183. * to the real right, and the masks to the real right again for both
  184. * the vertical and horizontal Thumbs. So those cases are hardcoded.
  185. * Do a search for ISVERT to find these dependancies.
  186. * -Chee
  187. */
  188. /*
  189. FlipRect Function is moved to cutils.c as other controls were also using it.
  190. -Arul
  191. */
  192. void TBFlipPoint(PTRACKBAR ptb, LPPOINT lppt)
  193. {
  194. if (ISVERT(ptb)) {
  195. FlipPoint(lppt);
  196. }
  197. }
  198. /* added trackbar variable to do auto verticalization */
  199. void NEAR PASCAL PatRect(HDC hdc,int x,int y,int dx,int dy, PTRACKBAR ptb)
  200. {
  201. RECT rc;
  202. rc.left = x;
  203. rc.top = y;
  204. rc.right = x + dx;
  205. rc.bottom = y + dy;
  206. if (ISVERT(ptb))
  207. FlipRect(&rc);
  208. ExtTextOut(hdc,0,0,ETO_OPAQUE,&rc,NULL,0,NULL);
  209. }
  210. #define TBInvalidateRect(hwnd, prc, bErase, ptb) VertInvalidateRect(hwnd, prc, bErase, ISVERT(ptb))
  211. void FAR PASCAL VertInvalidateRect(HWND hwnd, LPRECT qrc, BOOL b, BOOL fVert)
  212. {
  213. RECT rc;
  214. rc = *qrc;
  215. if (fVert) FlipRect(&rc);
  216. InvalidateRect(hwnd, &rc, b);
  217. }
  218. #define TBDrawEdge(hdc, prc, uType, grfFlags, ptb) VertDrawEdge(hdc, prc, uType, grfFlags, ISVERT(ptb))
  219. void FAR PASCAL VertDrawEdge(HDC hdc, LPRECT qrc, UINT edgeType, UINT grfFlags,
  220. BOOL fVert)
  221. {
  222. RECT temprc;
  223. UINT uFlags = grfFlags;
  224. temprc = *qrc;
  225. if (fVert) {
  226. FlipRect(&temprc);
  227. if (!(uFlags & BF_DIAGONAL)) {
  228. if (grfFlags & BF_TOP) uFlags |= BF_LEFT;
  229. else uFlags &= ~BF_LEFT;
  230. if (grfFlags & BF_LEFT) uFlags |= BF_TOP;
  231. else uFlags &= ~BF_TOP;
  232. if (grfFlags & BF_BOTTOM) uFlags |= BF_RIGHT;
  233. else uFlags &= ~BF_RIGHT;
  234. if (grfFlags & BF_RIGHT) uFlags |= BF_BOTTOM;
  235. else uFlags &= ~BF_BOTTOM;
  236. } else {
  237. if ((grfFlags & (BF_BOTTOM | BF_RIGHT)) == (BF_BOTTOM | BF_RIGHT)) {
  238. uFlags = BF_TOP | BF_LEFT;
  239. if (edgeType == EDGE_RAISED) {
  240. edgeType = EDGE_SUNKEN;
  241. } else {
  242. edgeType = EDGE_RAISED;
  243. }
  244. uFlags |= grfFlags & (~BF_RECT);
  245. uFlags ^= BF_SOFT;
  246. }
  247. }
  248. }
  249. DrawEdge(hdc, &temprc, edgeType, uFlags);
  250. }
  251. void NEAR PASCAL TBBitBlt(HDC hdc1, int x1, int y1, int w, int h,
  252. HDC hdc2, int x2, int y2, DWORD rop, PTRACKBAR ptb)
  253. {
  254. if (ISVERT(ptb))
  255. BitBlt(hdc1, y1, x1, h, w, hdc2, x2, y2, rop);
  256. else
  257. BitBlt(hdc1, x1, y1, w, h, hdc2, x2, y2, rop);
  258. }
  259. #define TBPatBlt(hdc1, x1, y1, w, h, rop, ptb) VertPatBlt(hdc1, x1, y1, w, h, rop, ISVERT(ptb))
  260. void FAR PASCAL VertPatBlt(HDC hdc1, int x1, int y1, int w, int h,
  261. DWORD rop, BOOL fVert)
  262. {
  263. if (fVert)
  264. PatBlt(hdc1, y1, x1, h, w, rop);
  265. else
  266. PatBlt(hdc1, x1, y1, w, h, rop);
  267. }
  268. void NEAR PASCAL DrawTic(PTRACKBAR ptb, int x, int y, int dir)
  269. {
  270. if (dir == -1) y -= TICKHEIGHT;
  271. SetBkColor(ptb->hdc, g_clrBtnText);
  272. PatRect(ptb->hdc,x,y,1,TICKHEIGHT, ptb);
  273. }
  274. // dir = direction multiplier (drawing up or down)
  275. // yTic = where (vertically) to draw the line of tics
  276. void NEAR PASCAL DrawTicsOneLine(PTRACKBAR ptb, int dir, int yTic)
  277. {
  278. PDWORD pTics;
  279. int iPos;
  280. int i;
  281. DrawTic(ptb, ptb->rc.left, yTic, dir); // first
  282. DrawTic(ptb, ptb->rc.left, yTic+ (dir * 1), dir);
  283. DrawTic(ptb, ptb->rc.right-1, yTic, dir); // last
  284. DrawTic(ptb, ptb->rc.right-1, yTic+ (dir * 1), dir);
  285. // those inbetween
  286. pTics = ptb->pTics;
  287. if (ptb->ticFreq && pTics) {
  288. for (i = 0; i < ptb->nTics; ++i) {
  289. if (((i+1) % ptb->ticFreq) == 0) {
  290. iPos = TBLogToPhys(ptb,pTics[i]);
  291. DrawTic(ptb, iPos, yTic, dir);
  292. }
  293. }
  294. }
  295. // draw the selection range (triangles)
  296. if ((ptb->Flags & TBF_SELECTION) &&
  297. (ptb->lSelStart <= ptb->lSelEnd) && (ptb->lSelEnd >= ptb->lLogMin)) {
  298. SetBkColor(ptb->hdc, g_clrBtnText);
  299. iPos = TBLogToPhys(ptb,ptb->lSelStart);
  300. for (i = 0; i < TICKHEIGHT; i++)
  301. PatRect(ptb->hdc,iPos-i,yTic+(dir==1 ? i : -TICKHEIGHT),
  302. 1,TICKHEIGHT-i, ptb);
  303. iPos = TBLogToPhys(ptb,ptb->lSelEnd);
  304. for (i = 0; i < TICKHEIGHT; i++)
  305. PatRect(ptb->hdc,iPos+i,yTic+(dir==1 ? i : -TICKHEIGHT),
  306. 1,TICKHEIGHT-i, ptb);
  307. }
  308. }
  309. /* DrawTics() */
  310. /* There is always a tick at the beginning and end of the bar, but you can */
  311. /* add some more of your own with a TBM_SETTIC message. This draws them. */
  312. /* They are kept in an array whose handle is a window word. The first */
  313. /* element is the number of extra ticks, and then the positions. */
  314. void NEAR PASCAL DrawTics(PTRACKBAR ptb)
  315. {
  316. // do they even want this?
  317. if (ptb->ci.style & TBS_NOTICKS) return;
  318. if ((ptb->ci.style & TBS_BOTH) || !(ptb->ci.style & TBS_TOP)) {
  319. DrawTicsOneLine(ptb, 1, ptb->rc.bottom + 1);
  320. }
  321. if ((ptb->ci.style & (TBS_BOTH | TBS_TOP))) {
  322. DrawTicsOneLine(ptb, -1, ptb->rc.top - 1);
  323. }
  324. }
  325. void NEAR PASCAL GetChannelRect(PTRACKBAR ptb, LPRECT lprc)
  326. {
  327. int iwidth, iheight;
  328. if (!lprc)
  329. return;
  330. lprc->left = ptb->rc.left - ptb->iThumbWidth / 2;
  331. iwidth = ptb->iSizePhys + ptb->iThumbWidth - 1;
  332. lprc->right = lprc->left + iwidth;
  333. if (ptb->ci.style & TBS_ENABLESELRANGE) {
  334. iheight = ptb->iThumbHeight / 4 * 3; // this is Scrollheight
  335. } else {
  336. iheight = 4;
  337. }
  338. lprc->top = (ptb->rc.top + ptb->rc.bottom - iheight) /2;
  339. if (!(ptb->ci.style & TBS_BOTH))
  340. if (ptb->ci.style & TBS_TOP) lprc->top++;
  341. else lprc->top--;
  342. lprc->bottom = lprc->top + iheight;
  343. }
  344. /* This draws the track bar itself */
  345. void NEAR PASCAL DrawChannel(PTRACKBAR ptb, LPRECT lprc)
  346. {
  347. TBDrawEdge(ptb->hdc, lprc, EDGE_SUNKEN, BF_RECT,ptb);
  348. SetBkColor(ptb->hdc, g_clrBtnHighlight);
  349. // Fill the center
  350. PatRect(ptb->hdc, lprc->left+2, lprc->top+2, (lprc->right-lprc->left)-4,
  351. (lprc->bottom-lprc->top)-4, ptb);
  352. // now highlight the selection range
  353. if ((ptb->Flags & TBF_SELECTION) &&
  354. (ptb->lSelStart <= ptb->lSelEnd) && (ptb->lSelEnd > ptb->lLogMin)) {
  355. int iStart, iEnd;
  356. iStart = TBLogToPhys(ptb,ptb->lSelStart);
  357. iEnd = TBLogToPhys(ptb,ptb->lSelEnd);
  358. if (iStart + 2 <= iEnd) {
  359. SetBkColor(ptb->hdc, g_clrHighlight);
  360. PatRect(ptb->hdc, iStart+1, lprc->top+3,
  361. iEnd-iStart-1, (lprc->bottom-lprc->top)-6, ptb);
  362. }
  363. }
  364. }
  365. void NEAR PASCAL DrawThumb(PTRACKBAR ptb, LPRECT lprc, BOOL fSelected)
  366. {
  367. // iDpt direction from middle to point of thumb
  368. // a negative value inverts things.
  369. // this allows one code path..
  370. int iDpt = 0;
  371. int i = 0; // size of point triangle
  372. int iYpt = 0; // vertical location of tip;
  373. int iXmiddle = 0;
  374. int icount; // just a loop counter
  375. UINT uEdgeFlags;
  376. RECT rcThumb = *lprc;
  377. if (ptb->Flags & TBF_NOTHUMB ||
  378. ptb->ci.style & TBS_NOTHUMB) // If no thumb, just leave.
  379. return;
  380. ASSERT(ptb->iThumbHeight >= MIN_THUMB_HEIGHT);
  381. ASSERT(ptb->iThumbWidth > 1);
  382. // draw the rectangle part
  383. if (!(ptb->ci.style & TBS_BOTH)) {
  384. int iMiddle;
  385. // do -3 because wThumb is odd (triangles ya know)
  386. // and because draw rects draw inside the rects passed.
  387. // actually should be (width-1)/2-1, but this is the same...
  388. i = (ptb->iThumbWidth - 3) / 2;
  389. iMiddle = ptb->iThumbHeight / 2 + rcThumb.top;
  390. //draw the rectangle part
  391. if (ptb->ci.style & TBS_TOP) {
  392. iMiddle++; //correction because drawing routines
  393. iDpt = -1;
  394. rcThumb.top += (i+1);
  395. uEdgeFlags = BF_SOFT | BF_LEFT | BF_RIGHT | BF_BOTTOM;
  396. } else {
  397. iDpt = 1;
  398. rcThumb.bottom -= (i+1);
  399. // draw on the inside, not on the bottom and rt edge
  400. uEdgeFlags = BF_SOFT | BF_LEFT | BF_RIGHT | BF_TOP;
  401. }
  402. iYpt = iMiddle + (iDpt * (ptb->iThumbHeight / 2));
  403. iXmiddle = rcThumb.left + i;
  404. } else {
  405. uEdgeFlags = BF_SOFT | BF_RECT;
  406. }
  407. // fill in the center
  408. if (fSelected || !IsWindowEnabled(ptb->ci.hwnd)) {
  409. HBRUSH hbrTemp;
  410. // draw the dithered insides;
  411. hbrTemp = SelectObject(ptb->hdc, g_hbrMonoDither);
  412. if (hbrTemp) {
  413. SetTextColor(ptb->hdc, g_clrBtnHighlight);
  414. SetBkColor(ptb->hdc, g_clrBtnFace);
  415. TBPatBlt(ptb->hdc, rcThumb.left +2 , rcThumb.top,
  416. rcThumb.right-rcThumb.left -4, rcThumb.bottom-rcThumb.top,
  417. PATCOPY,ptb);
  418. if (!(ptb->ci.style & TBS_BOTH)) {
  419. for (icount = 1; icount <= i; icount++) {
  420. TBPatBlt(ptb->hdc, iXmiddle-icount+1,
  421. iYpt - (iDpt*icount),
  422. icount*2, 1, PATCOPY, ptb);
  423. }
  424. }
  425. SelectObject(ptb->hdc, hbrTemp);
  426. }
  427. } else {
  428. SetBkColor(ptb->hdc, g_clrBtnFace);
  429. PatRect(ptb->hdc, rcThumb.left+2, rcThumb.top,
  430. rcThumb.right-rcThumb.left-4, rcThumb.bottom-rcThumb.top, ptb);
  431. if (!(ptb->ci.style & TBS_BOTH)) {
  432. for (icount = 1; icount <= i; icount++) {
  433. PatRect(ptb->hdc, iXmiddle-icount+1,
  434. iYpt - (iDpt*icount),
  435. icount*2, 1, ptb);
  436. }
  437. }
  438. }
  439. TBDrawEdge(ptb->hdc, &rcThumb, EDGE_RAISED, uEdgeFlags, ptb);
  440. //now draw the point
  441. if (!(ptb->ci.style & TBS_BOTH)) {
  442. UINT uEdgeFlags2;
  443. // uEdgeFlags is now used to switch between top and bottom.
  444. // we'll or it in with the diagonal and left/right flags below
  445. if (ptb->ci.style & TBS_TOP) {
  446. rcThumb.bottom = rcThumb.top + 1;
  447. rcThumb.top = rcThumb.bottom - (i + 2);
  448. uEdgeFlags = BF_TOP | BF_RIGHT | BF_DIAGONAL | BF_SOFT;
  449. uEdgeFlags2 = BF_BOTTOM | BF_RIGHT | BF_DIAGONAL;
  450. } else {
  451. rcThumb.top = rcThumb.bottom - 1;
  452. rcThumb.bottom = rcThumb.top + (i + 2);
  453. uEdgeFlags = BF_TOP | BF_LEFT | BF_DIAGONAL | BF_SOFT;
  454. uEdgeFlags2 = BF_BOTTOM | BF_LEFT | BF_DIAGONAL;
  455. }
  456. rcThumb.right = rcThumb.left + (i + 2);
  457. // do the left side first
  458. TBDrawEdge(ptb->hdc, &rcThumb, EDGE_RAISED, uEdgeFlags , ptb);
  459. // then do th right side
  460. OffsetRect(&rcThumb, i + 1, 0);
  461. TBDrawEdge(ptb->hdc, &rcThumb, EDGE_RAISED, uEdgeFlags2 , ptb);
  462. }
  463. }
  464. void NEAR PASCAL TBInvalidateAll(PTRACKBAR ptb)
  465. {
  466. if (ptb) {
  467. TBChanged(ptb, TBC_ALL);
  468. InvalidateRect(ptb->ci.hwnd, NULL, FALSE);
  469. }
  470. }
  471. void NEAR PASCAL MoveThumb(PTRACKBAR ptb, LONG lPos)
  472. {
  473. long lOld = ptb->lLogPos;
  474. TBInvalidateRect(ptb->ci.hwnd, &ptb->rcThumb, FALSE,ptb);
  475. ptb->lLogPos = BOUND(lPos,ptb->lLogMin,ptb->lLogMax);
  476. ptb->rcThumb.left = TBLogToPhys(ptb, ptb->lLogPos) - ptb->iThumbWidth / 2;
  477. ptb->rcThumb.right = ptb->rcThumb.left + ptb->iThumbWidth;
  478. TBInvalidateRect(ptb->ci.hwnd, &ptb->rcThumb, FALSE,ptb);
  479. TBChanged(ptb, TBC_THUMB);
  480. UpdateWindow(ptb->ci.hwnd);
  481. if (lOld != ptb->lLogPos)
  482. MyNotifyWinEvent(EVENT_OBJECT_VALUECHANGE, ptb->ci.hwnd, OBJID_CLIENT, 0);
  483. }
  484. void NEAR PASCAL DrawFocus(PTRACKBAR ptb, HBRUSH hbrBackground)
  485. {
  486. RECT rc;
  487. if (ptb->ci.hwnd == GetFocus()
  488. #ifdef KEYBOARDCUES
  489. && !(CCGetUIState(&(ptb->ci)) & UISF_HIDEFOCUS)
  490. #endif
  491. )
  492. {
  493. SetBkColor(ptb->hdc, g_clrBtnHighlight);
  494. GetClientRect(ptb->ci.hwnd, &rc);
  495. // Successive calls to DrawFocusRect will invert it thereby erasing it.
  496. // To avoid this, whenever we process WM_PAINT, we erase the focus rect ourselves
  497. // before we draw it below.
  498. if (hbrBackground)
  499. FrameRect(ptb->hdc, &rc, hbrBackground);
  500. DrawFocusRect(ptb->hdc, &rc);
  501. }
  502. }
  503. void NEAR PASCAL DoAutoTics(PTRACKBAR ptb)
  504. {
  505. LONG NEAR *pl;
  506. LONG l;
  507. if (!(ptb->ci.style & TBS_AUTOTICKS))
  508. return;
  509. if (ptb->pTics)
  510. LocalFree((HLOCAL)ptb->pTics);
  511. ptb->nTics = (int)(ptb->lLogMax - ptb->lLogMin - 1);
  512. if (ptb->nTics > 0)
  513. ptb->pTics = (DWORD NEAR *)LocalAlloc(LPTR, sizeof(DWORD) * ptb->nTics);
  514. else
  515. ptb->pTics = NULL;
  516. if (!ptb->pTics) {
  517. ptb->nTics = 0;
  518. return;
  519. }
  520. for (pl = (LONG NEAR *)ptb->pTics, l = ptb->lLogMin + 1; l < ptb->lLogMax; l++)
  521. *pl++ = l;
  522. }
  523. void NEAR PASCAL ValidateThumbHeight(PTRACKBAR ptb)
  524. {
  525. if (ptb->iThumbHeight < MIN_THUMB_HEIGHT)
  526. ptb->iThumbHeight = MIN_THUMB_HEIGHT;
  527. ptb->iThumbWidth = ptb->iThumbHeight / 2;
  528. ptb->iThumbWidth |= 0x01; // make sure it's odd at at least 3
  529. if (ptb->ci.style & TBS_ENABLESELRANGE) {
  530. if (ptb->ci.style & TBS_FIXEDLENGTH) {
  531. // half of 9/10
  532. ptb->iThumbWidth = (ptb->iThumbHeight * 9) / 20;
  533. ptb->iThumbWidth |= 0x01;
  534. } else {
  535. ptb->iThumbHeight += (ptb->iThumbWidth * 2) / 9;
  536. }
  537. }
  538. }
  539. void TBPositionBuddies(PTRACKBAR ptb)
  540. {
  541. POINT pt;
  542. HWND hwndParent;
  543. RECT rcBuddy;
  544. RECT rcClient;
  545. RECT rcChannel;
  546. int yMid;
  547. GetChannelRect(ptb, &rcChannel);
  548. yMid = (rcChannel.top + rcChannel.bottom) / 2;
  549. GetClientRect(ptb->ci.hwnd, &rcClient);
  550. if (ISVERT(ptb))
  551. FlipRect(&rcClient);
  552. if (ptb->hwndBuddyLeft) {
  553. GetClientRect(ptb->hwndBuddyLeft, &rcBuddy);
  554. if (ISVERT(ptb))
  555. FlipRect(&rcBuddy);
  556. pt.y = yMid - ((RECTHEIGHT(rcBuddy))/2);
  557. pt.x = rcClient.left - RECTWIDTH(rcBuddy) - g_cxEdge;
  558. // x and y are now in trackbar's coordinates.
  559. // convert them to the parent of the buddy's coordinates
  560. hwndParent = GetParent(ptb->hwndBuddyLeft);
  561. TBFlipPoint(ptb, &pt);
  562. MapWindowPoints(ptb->ci.hwnd, hwndParent, &pt, 1);
  563. SetWindowPos(ptb->hwndBuddyLeft, NULL, pt.x, pt.y, 0, 0, SWP_NOSIZE |SWP_NOZORDER | SWP_NOACTIVATE);
  564. }
  565. if (ptb->hwndBuddyRight) {
  566. GetClientRect(ptb->hwndBuddyRight, &rcBuddy);
  567. if (ISVERT(ptb))
  568. FlipRect(&rcBuddy);
  569. pt.y = yMid - ((RECTHEIGHT(rcBuddy))/2);
  570. pt.x = rcClient.right + g_cxEdge;
  571. // x and y are now in trackbar's coordinates.
  572. // convert them to the parent of the buddy's coordinates
  573. hwndParent = GetParent(ptb->hwndBuddyRight);
  574. TBFlipPoint(ptb, &pt);
  575. MapWindowPoints(ptb->ci.hwnd, hwndParent, &pt, 1);
  576. SetWindowPos(ptb->hwndBuddyRight, NULL, pt.x, pt.y, 0, 0, SWP_NOSIZE |SWP_NOZORDER | SWP_NOACTIVATE);
  577. }
  578. }
  579. void NEAR PASCAL TBNukeBuffer(PTRACKBAR ptb)
  580. {
  581. if (ptb->hbmBuffer) {
  582. DeleteObject(ptb->hbmBuffer);
  583. ptb->hbmBuffer = NULL;
  584. TBChanged(ptb, TBC_ALL); // Must do a full repaint
  585. }
  586. }
  587. void NEAR PASCAL TBResize(PTRACKBAR ptb)
  588. {
  589. GetClientRect(ptb->ci.hwnd, &ptb->rc);
  590. if (ISVERT(ptb))
  591. FlipRect(&ptb->rc);
  592. if (!(ptb->ci.style & TBS_FIXEDLENGTH)) {
  593. ptb->iThumbHeight = (g_cyHScroll * 4) / 3;
  594. ValidateThumbHeight(ptb);
  595. if ((ptb->iThumbHeight > MIN_THUMB_HEIGHT) && (ptb->rc.bottom < (int)ptb->iThumbHeight)) {
  596. ptb->iThumbHeight = ptb->rc.bottom - 3*g_cyEdge; // top, bottom, and tic
  597. if (ptb->ci.style & TBS_ENABLESELRANGE)
  598. ptb->iThumbHeight = (ptb->iThumbHeight * 3 / 4);
  599. ValidateThumbHeight(ptb);
  600. }
  601. } else {
  602. ValidateThumbHeight(ptb);
  603. }
  604. if (ptb->ci.style & (TBS_BOTH | TBS_TOP) && !(ptb->ci.style & TBS_NOTICKS))
  605. ptb->rc.top += TICKHEIGHT + BORDERSIZE + 3;
  606. ptb->rc.top += BORDERSIZE;
  607. ptb->rc.bottom = ptb->rc.top + ptb->iThumbHeight;
  608. ptb->rc.left += (ptb->iThumbWidth + BORDERSIZE);
  609. ptb->rc.right -= (ptb->iThumbWidth + BORDERSIZE);
  610. ptb->rcThumb.top = ptb->rc.top;
  611. ptb->rcThumb.bottom = ptb->rc.bottom;
  612. // Figure out how much room we have to move the thumb in
  613. ptb->iSizePhys = ptb->rc.right - ptb->rc.left;
  614. // Elevator isn't there if there's no room.
  615. if (ptb->iSizePhys == 0) {
  616. // Lost our thumb.
  617. ptb->Flags |= TBF_NOTHUMB;
  618. ptb->iSizePhys = 1;
  619. } else {
  620. // Ah. We have a thumb.
  621. ptb->Flags &= ~TBF_NOTHUMB;
  622. }
  623. TBNukeBuffer(ptb);
  624. MoveThumb(ptb, ptb->lLogPos);
  625. TBInvalidateAll(ptb);
  626. TBPositionBuddies(ptb);
  627. }
  628. LRESULT NEAR PASCAL TrackOnCreate(HWND hwnd, LPCREATESTRUCT lpCreate)
  629. {
  630. PTRACKBAR ptb;
  631. #ifdef MAINWIN
  632. DWORD exStyle = WS_EX_MW_UNMANAGED_WINDOW;
  633. #else
  634. DWORD exStyle = 0;
  635. #endif
  636. InitDitherBrush();
  637. InitGlobalColors();
  638. // Get us our window structure.
  639. ptb = (PTRACKBAR)LocalAlloc(LPTR, sizeof(TRACKBAR));
  640. if (!ptb)
  641. return -1;
  642. SetWindowPtr(hwnd, 0, ptb);
  643. CIInitialize(&ptb->ci, hwnd, lpCreate);
  644. ptb->Cmd = (UINT)-1;
  645. ptb->lLogMax = 100;
  646. ptb->ticFreq = 1;
  647. // ptb->hbmBuffer = 0;
  648. ptb->lPageSize = -1;
  649. ptb->lLineSize = 1;
  650. // initial size;
  651. ptb->iThumbHeight = (g_cyHScroll * 4) / 3;
  652. #if defined(FE_IME) || !defined(WINNT)
  653. if (g_fDBCSInputEnabled)
  654. ptb->hPrevImc = ImmAssociateContext(hwnd, 0L);
  655. #endif
  656. #ifdef FEATURE_DEBUG
  657. if (GetAsyncKeyState(VK_SHIFT) < 0 &&
  658. GetAsyncKeyState(VK_CONTROL) < 0)
  659. ptb->ci.style |= TBS_TOOLTIPS;
  660. #endif
  661. if (ISVERT(ptb)) {
  662. if (ptb->ci.style & TBS_TOP) {
  663. ptb->uTipSide = TBTS_RIGHT;
  664. } else {
  665. ptb->uTipSide = TBTS_LEFT;
  666. }
  667. } else {
  668. if (ptb->ci.style & TBS_TOP) {
  669. ptb->uTipSide = TBTS_BOTTOM;
  670. } else {
  671. ptb->uTipSide = TBTS_TOP;
  672. }
  673. }
  674. if (ptb->ci.style & TBS_TOOLTIPS) {
  675. ptb->hwndToolTips = CreateWindowEx(exStyle,
  676. c_szSToolTipsClass, TEXT(""),
  677. WS_POPUP,
  678. CW_USEDEFAULT, CW_USEDEFAULT,
  679. CW_USEDEFAULT, CW_USEDEFAULT,
  680. ptb->ci.hwnd, NULL, HINST_THISDLL,
  681. NULL);
  682. if (ptb->hwndToolTips) {
  683. TOOLINFO ti;
  684. // don't bother setting the rect because we'll do it below
  685. // in FlushToolTipsMgr;
  686. ti.cbSize = sizeof(ti);
  687. ti.uFlags = TTF_TRACK | TTF_IDISHWND | TTF_CENTERTIP;
  688. ti.hwnd = ptb->ci.hwnd;
  689. ti.uId = (UINT_PTR)ptb->ci.hwnd;
  690. ti.lpszText = LPSTR_TEXTCALLBACK;
  691. ti.rect.left = ti.rect.top = ti.rect.bottom = ti.rect.right = 0; // update this on size
  692. SendMessage(ptb->hwndToolTips, TTM_ADDTOOL, 0,
  693. (LPARAM)(LPTOOLINFO)&ti);
  694. } else
  695. ptb->ci.style &= ~(TBS_TOOLTIPS);
  696. }
  697. TBResize(ptb);
  698. #ifdef FEATURE_DEBUG
  699. if (GetAsyncKeyState(VK_SHIFT) < 0 &&
  700. GetAsyncKeyState(VK_CONTROL) < 0 )
  701. {
  702. HWND hwnd =
  703. CreateWindowEx(WS_EX_STATICEDGE, TEXT("static"), TEXT("left"), WS_CHILD | WS_VISIBLE, 0, 0, 30, 20, GetParent(ptb->ci.hwnd), NULL, HINST_THISDLL, NULL);
  704. HWND hwnd2 =
  705. CreateWindowEx(WS_EX_STATICEDGE, TEXT("static"), TEXT("right"), WS_CHILD |WS_VISIBLE, 0, 0, 50, 20, GetParent(ptb->ci.hwnd), NULL, HINST_THISDLL, NULL);
  706. SendMessage(ptb->ci.hwnd, TBM_SETBUDDY, TRUE, (LPARAM)hwnd);
  707. SendMessage(ptb->ci.hwnd, TBM_SETBUDDY, FALSE, (LPARAM)hwnd2);
  708. }
  709. #endif
  710. return 0;
  711. }
  712. void NEAR PASCAL TrackOnNotify(PTRACKBAR ptb, LPNMHDR lpnm)
  713. {
  714. if (lpnm->hwndFrom == ptb->hwndToolTips) {
  715. switch (lpnm->code) {
  716. case TTN_NEEDTEXT:
  717. #define lpttt ((LPTOOLTIPTEXT)lpnm)
  718. wsprintf(lpttt->szText, TEXT("%d"), ptb->lLogPos);
  719. default:
  720. SendNotifyEx(ptb->ci.hwndParent, (HWND)-1,
  721. lpnm->code, lpnm, ptb->ci.bUnicode);
  722. break;
  723. }
  724. }
  725. }
  726. HWND TBSetBuddy(PTRACKBAR ptb, BOOL fLeft, HWND hwndBuddy)
  727. {
  728. HWND hwndOldBuddy;
  729. if (fLeft) {
  730. hwndOldBuddy = ptb->hwndBuddyLeft;
  731. ptb->hwndBuddyLeft = hwndBuddy;
  732. } else {
  733. hwndOldBuddy = ptb->hwndBuddyRight;
  734. ptb->hwndBuddyRight = hwndBuddy;
  735. }
  736. TBResize(ptb);
  737. return hwndOldBuddy;
  738. }
  739. LPARAM FAR CALLBACK TrackBarWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  740. {
  741. PTRACKBAR ptb;
  742. PAINTSTRUCT ps;
  743. HLOCAL h;
  744. ptb = GetWindowPtr(hwnd, 0);
  745. if (!ptb) {
  746. if (uMsg == WM_CREATE)
  747. return TrackOnCreate(hwnd, (LPCREATESTRUCT)lParam);
  748. goto DoDefault;
  749. }
  750. switch (uMsg) {
  751. // If color depth changes, the old buffer is no longer any good
  752. case WM_DISPLAYCHANGE:
  753. TBNukeBuffer(ptb);
  754. break;
  755. case WM_WININICHANGE:
  756. InitGlobalMetrics(wParam);
  757. // fall through to WM_SIZE
  758. case WM_SIZE:
  759. TBResize(ptb);
  760. break;
  761. case WM_SYSCOLORCHANGE:
  762. InitGlobalColors();
  763. TBInvalidateAll(ptb);
  764. break;
  765. case WM_NOTIFYFORMAT:
  766. return CIHandleNotifyFormat(&ptb->ci,lParam);
  767. case WM_NOTIFY:
  768. TrackOnNotify(ptb, (LPNMHDR)lParam);
  769. break;
  770. case WM_DESTROY:
  771. TerminateDitherBrush();
  772. if (ptb) {
  773. #if defined(FE_IME) || !defined(WINNT)
  774. if (g_fDBCSInputEnabled)
  775. ImmAssociateContext(hwnd, ptb->hPrevImc);
  776. #endif
  777. if ((ptb->ci.style & TBS_TOOLTIPS) && IsWindow(ptb->hwndToolTips)) {
  778. DestroyWindow (ptb->hwndToolTips);
  779. }
  780. TBNukeBuffer(ptb);
  781. if (ptb->pTics)
  782. LocalFree((HLOCAL)ptb->pTics);
  783. LocalFree((HLOCAL)ptb);
  784. SetWindowPtr(hwnd, 0, 0);
  785. }
  786. break;
  787. case WM_KILLFOCUS:
  788. // Reset wheel scroll amount
  789. gcWheelDelta = 0;
  790. // fall-through
  791. case WM_SETFOCUS:
  792. ASSERT(gcWheelDelta == 0);
  793. if (ptb)
  794. TBInvalidateAll(ptb);
  795. break;
  796. case WM_ENABLE:
  797. if (wParam) {
  798. ptb->ci.style &= ~WS_DISABLED;
  799. } else {
  800. ptb->ci.style |= WS_DISABLED;
  801. }
  802. TBChanged(ptb, TBC_THUMB);
  803. InvalidateRect(hwnd, NULL, FALSE);
  804. break;
  805. case WM_PRINTCLIENT:
  806. case WM_PAINT: {
  807. RECT rc;
  808. HBITMAP hbmOld;
  809. HDC hdc;
  810. hdc = wParam ? (HDC)wParam : BeginPaint(hwnd, &ps);
  811. //DebugMsg(DM_TRACE, "NumTics = %d", SendMessage(ptb->ci.hwnd, TBM_GETNUMTICS, 0, 0));
  812. //ptb->hdc = GetDC(NULL);
  813. ptb->hdc = CreateCompatibleDC(hdc);
  814. if (!ptb->hbmBuffer) {
  815. GetClientRect(hwnd, &rc);
  816. ptb->hbmBuffer = CreateColorBitmap(rc.right, rc.bottom);
  817. }
  818. hbmOld = SelectObject(ptb->hdc, ptb->hbmBuffer);
  819. FlushChanges(ptb);
  820. //only copy the area that's changable.. ie the clip box
  821. switch(GetClipBox(hdc, &rc)) {
  822. case NULLREGION:
  823. case ERROR:
  824. GetClientRect(ptb->ci.hwnd, &rc);
  825. }
  826. BitBlt(hdc, rc.left, rc.top,
  827. rc.right - rc.left, rc.bottom - rc.top,
  828. ptb->hdc, rc.left, rc.top, SRCCOPY);
  829. #ifdef TB_DEBUG
  830. {
  831. HDC hdcScreen;
  832. RECT rcClient;
  833. hdcScreen = GetDC(NULL);
  834. GetClientRect(ptb->ci.hwnd, &rcClient);
  835. BitBlt(hdcScreen, 0, 0, rcClient.right, rcClient.bottom, ptb->hdc, 0,0, SRCCOPY);
  836. ReleaseDC(NULL, hdcScreen);
  837. }
  838. #endif
  839. SelectObject(ptb->hdc, hbmOld);
  840. DeleteDC(ptb->hdc);
  841. //ReleaseDC(NULL, ptb->hdc);
  842. if (wParam == 0)
  843. EndPaint(hwnd, &ps);
  844. ptb->hdc = NULL;
  845. break;
  846. }
  847. case WM_GETDLGCODE:
  848. return DLGC_WANTARROWS;
  849. case WM_LBUTTONDOWN:
  850. /* Give ourselves focus */
  851. if (!(ptb->ci.style & WS_DISABLED)) {
  852. SetFocus(hwnd); // REVIEW: we may not want to do this
  853. TBTrackInit(ptb, lParam);
  854. }
  855. break;
  856. case WM_LBUTTONUP:
  857. // We're through doing whatever we were doing with the
  858. // button down.
  859. if (!(ptb->ci.style & WS_DISABLED)) {
  860. TBTrackEnd(ptb);
  861. if (GetCapture() == hwnd)
  862. CCReleaseCapture(&ptb->ci);
  863. }
  864. break;
  865. case WM_TIMER:
  866. // The only way we get a timer message is if we're
  867. // autotracking.
  868. lParam = GetMessagePosClient(ptb->ci.hwnd, NULL);
  869. // fall through to WM_MOUSEMOVE
  870. case WM_MOUSEMOVE:
  871. // We only care that the mouse is moving if we're
  872. // tracking the bloody thing.
  873. if ((ptb->Cmd != (UINT)-1) && (!(ptb->ci.style & WS_DISABLED)))
  874. TBTrack(ptb, lParam);
  875. break;
  876. case WM_CAPTURECHANGED:
  877. // someone is stealing the capture from us
  878. TBTrackEnd(ptb);
  879. break;
  880. case WM_KEYUP:
  881. if (!(ptb->ci.style & WS_DISABLED)) {
  882. // If key was any of the keyboard accelerators, send end
  883. // track message when user up clicks on keyboard
  884. switch (wParam) {
  885. case VK_HOME:
  886. case VK_END:
  887. case VK_PRIOR:
  888. case VK_NEXT:
  889. case VK_LEFT:
  890. case VK_UP:
  891. case VK_RIGHT:
  892. case VK_DOWN:
  893. DoTrack(ptb, TB_ENDTRACK, 0);
  894. break;
  895. default:
  896. break;
  897. }
  898. }
  899. break;
  900. case WM_KEYDOWN:
  901. if (!(ptb->ci.style & WS_DISABLED)) {
  902. // Swap the left and right arrow key if the control is mirrored.
  903. wParam = RTLSwapLeftRightArrows(&ptb->ci, wParam);
  904. // If TBS_DOWNISLEFT, then swap left/right or up/down
  905. // depending on whether we are vertical or horizontal.
  906. // Some horizontal trackbars (e.g.) prefer that
  907. // UpArrow=TB_PAGEDOWN.
  908. if (ptb->ci.style & TBS_DOWNISLEFT) {
  909. if (ISVERT(ptb)) {
  910. wParam = CCSwapKeys(wParam, VK_LEFT, VK_RIGHT);
  911. } else {
  912. wParam = CCSwapKeys(wParam, VK_UP, VK_DOWN);
  913. wParam = CCSwapKeys(wParam, VK_PRIOR, VK_NEXT);
  914. }
  915. }
  916. switch (wParam) {
  917. case VK_HOME:
  918. wParam = TB_TOP;
  919. goto KeyTrack;
  920. case VK_END:
  921. wParam = TB_BOTTOM;
  922. goto KeyTrack;
  923. case VK_PRIOR:
  924. wParam = TB_PAGEUP;
  925. goto KeyTrack;
  926. case VK_NEXT:
  927. wParam = TB_PAGEDOWN;
  928. goto KeyTrack;
  929. case VK_LEFT:
  930. case VK_UP:
  931. wParam = TB_LINEUP;
  932. goto KeyTrack;
  933. case VK_RIGHT:
  934. case VK_DOWN:
  935. wParam = TB_LINEDOWN;
  936. KeyTrack:
  937. DoTrack(ptb, (int) wParam, 0);
  938. #ifdef KEYBOARDCUES
  939. //notify of navigation key usage
  940. CCNotifyNavigationKeyUsage(&(ptb->ci), UISF_HIDEFOCUS);
  941. #endif
  942. break;
  943. default:
  944. break;
  945. }
  946. }
  947. break;
  948. case WM_MBUTTONDOWN:
  949. SetFocus(hwnd);
  950. break;
  951. case WM_STYLECHANGED:
  952. if (wParam == GWL_STYLE) {
  953. ptb->ci.style = ((LPSTYLESTRUCT)lParam)->styleNew;
  954. TBResize(ptb);
  955. }
  956. break;
  957. #ifdef KEYBOARDCUES
  958. case WM_UPDATEUISTATE:
  959. {
  960. DWORD dwUIStateMask = MAKEWPARAM(0xFFFF, UISF_HIDEFOCUS);
  961. if (CCOnUIState(&(ptb->ci), WM_UPDATEUISTATE, wParam & dwUIStateMask, lParam))
  962. InvalidateRect(hwnd, NULL, TRUE);
  963. goto DoDefault;
  964. }
  965. #endif
  966. case TBM_GETPOS:
  967. return ptb->lLogPos;
  968. case TBM_GETSELSTART:
  969. return ptb->lSelStart;
  970. case TBM_GETSELEND:
  971. return ptb->lSelEnd;
  972. case TBM_GETRANGEMIN:
  973. return ptb->lLogMin;
  974. case TBM_GETRANGEMAX:
  975. return ptb->lLogMax;
  976. case TBM_GETPTICS:
  977. return (LRESULT)ptb->pTics;
  978. case TBM_CLEARSEL:
  979. ptb->Flags &= ~TBF_SELECTION;
  980. ptb->lSelStart = -1;
  981. ptb->lSelEnd = -1;
  982. goto RedrawTB;
  983. case TBM_CLEARTICS:
  984. if (ptb->pTics)
  985. LocalFree((HLOCAL)ptb->pTics);
  986. ptb->pTics = NULL;
  987. ptb->nTics = 0;
  988. goto RedrawTB;
  989. case TBM_GETTIC:
  990. if (ptb->pTics == NULL || (int)wParam >= ptb->nTics)
  991. return -1L;
  992. return ptb->pTics[wParam];
  993. case TBM_GETTICPOS:
  994. if (ptb->pTics == NULL || (int)wParam >= ptb->nTics)
  995. return -1L;
  996. return TBLogToPhys(ptb,ptb->pTics[wParam]);
  997. case TBM_GETNUMTICS:
  998. if (ptb->ci.style & TBS_NOTICKS)
  999. return 0;
  1000. if (ptb->ticFreq) {
  1001. // first and last +
  1002. return 2 + (ptb->nTics / ptb->ticFreq);
  1003. }
  1004. // if there's no ticFreq, then we fall down here.
  1005. // 2 for the first and last tics that we always draw
  1006. // when NOTICS isn't set.
  1007. return 2;
  1008. case TBM_SETTIC:
  1009. /* not a valid position */
  1010. if (((LONG)lParam) < ptb->lLogMin || ((LONG)lParam) > ptb->lLogMax)
  1011. break;
  1012. h = CCLocalReAlloc(ptb->pTics,
  1013. sizeof(DWORD) * (ptb->nTics + 1));
  1014. if (!h)
  1015. return (LONG)FALSE;
  1016. ptb->pTics = (PDWORD)h;
  1017. ptb->pTics[ptb->nTics++] = (DWORD)lParam;
  1018. TBInvalidateAll(ptb);
  1019. return (LONG)TRUE;
  1020. case TBM_SETTICFREQ:
  1021. ptb->ticFreq = (int) wParam;
  1022. DoAutoTics(ptb);
  1023. goto RedrawTB;
  1024. case TBM_SETPOS:
  1025. /* Only redraw if it will physically move */
  1026. if (wParam && TBLogToPhys(ptb, (DWORD) lParam) !=
  1027. TBLogToPhys(ptb, ptb->lLogPos))
  1028. MoveThumb(ptb, (DWORD) lParam);
  1029. else
  1030. ptb->lLogPos = BOUND((LONG)lParam,ptb->lLogMin,ptb->lLogMax);
  1031. break;
  1032. case TBM_SETSEL:
  1033. if (!(ptb->ci.style & TBS_ENABLESELRANGE)) break;
  1034. ptb->Flags |= TBF_SELECTION;
  1035. if (((LONG)(SHORT)LOWORD(lParam)) < ptb->lLogMin)
  1036. ptb->lSelStart = ptb->lLogMin;
  1037. else
  1038. ptb->lSelStart = (LONG)(SHORT)LOWORD(lParam);
  1039. if (((LONG)(SHORT)HIWORD(lParam)) > ptb->lLogMax)
  1040. ptb->lSelEnd = ptb->lLogMax;
  1041. else
  1042. ptb->lSelEnd = (LONG)(SHORT)HIWORD(lParam);
  1043. if (ptb->lSelEnd < ptb->lSelStart)
  1044. ptb->lSelEnd = ptb->lSelStart;
  1045. goto RedrawTB;
  1046. case TBM_SETSELSTART:
  1047. if (!(ptb->ci.style & TBS_ENABLESELRANGE)) break;
  1048. ptb->Flags |= TBF_SELECTION;
  1049. if (lParam < ptb->lLogMin)
  1050. ptb->lSelStart = ptb->lLogMin;
  1051. else
  1052. ptb->lSelStart = (LONG) lParam;
  1053. if (ptb->lSelEnd < ptb->lSelStart || ptb->lSelEnd == -1)
  1054. ptb->lSelEnd = ptb->lSelStart;
  1055. goto RedrawTB;
  1056. case TBM_SETSELEND:
  1057. if (!(ptb->ci.style & TBS_ENABLESELRANGE)) break;
  1058. ptb->Flags |= TBF_SELECTION;
  1059. if (lParam > ptb->lLogMax)
  1060. ptb->lSelEnd = ptb->lLogMax;
  1061. else
  1062. ptb->lSelEnd = (LONG) lParam;
  1063. if (ptb->lSelStart > ptb->lSelEnd || ptb->lSelStart == -1)
  1064. ptb->lSelStart = ptb->lSelEnd;
  1065. goto RedrawTB;
  1066. case TBM_SETRANGE:
  1067. ptb->lLogMin = (LONG)(SHORT)LOWORD(lParam);
  1068. ptb->lLogMax = (LONG)(SHORT)HIWORD(lParam);
  1069. if (ptb->lSelStart < ptb->lLogMin)
  1070. ptb->lSelStart = ptb->lLogMin;
  1071. if (ptb->lSelEnd > ptb->lLogMax)
  1072. ptb->lSelEnd = ptb->lLogMax;
  1073. DoAutoTics(ptb);
  1074. goto RedrawTB;
  1075. case TBM_SETRANGEMIN:
  1076. ptb->lLogMin = (LONG)lParam;
  1077. if (ptb->lSelStart < ptb->lLogMin)
  1078. ptb->lSelStart = ptb->lLogMin;
  1079. DoAutoTics(ptb);
  1080. goto RedrawTB;
  1081. case TBM_SETRANGEMAX:
  1082. ptb->lLogMax = (LONG)lParam;
  1083. if (ptb->lSelEnd > ptb->lLogMax)
  1084. ptb->lSelEnd = ptb->lLogMax;
  1085. DoAutoTics(ptb);
  1086. RedrawTB:
  1087. ptb->lLogPos = BOUND(ptb->lLogPos, ptb->lLogMin,ptb->lLogMax);
  1088. TBChanged(ptb, TBC_ALL);
  1089. /* Only redraw if flag says so */
  1090. if (wParam) {
  1091. InvalidateRect(hwnd, NULL, FALSE);
  1092. MoveThumb(ptb, ptb->lLogPos);
  1093. }
  1094. break;
  1095. case TBM_SETTHUMBLENGTH:
  1096. if (ptb->ci.style & TBS_FIXEDLENGTH) {
  1097. ptb->iThumbHeight = (UINT)wParam;
  1098. TBResize(ptb);
  1099. }
  1100. break;
  1101. case TBM_GETTHUMBLENGTH:
  1102. return ptb->iThumbHeight;
  1103. case TBM_SETPAGESIZE: {
  1104. LONG lOldPage = ptb->lPageSize == -1 ? (ptb->lLogMax - ptb->lLogMin)/5 : ptb->lPageSize;
  1105. ptb->lPageSize = (LONG)lParam;
  1106. return lOldPage;
  1107. }
  1108. case TBM_GETPAGESIZE:
  1109. return ptb->lPageSize == -1 ? (ptb->lLogMax - ptb->lLogMin)/5 : ptb->lPageSize;
  1110. case TBM_SETLINESIZE: {
  1111. LONG lOldLine = ptb->lLineSize;
  1112. ptb->lLineSize = (LONG)lParam;
  1113. return lOldLine;
  1114. }
  1115. case TBM_GETLINESIZE:
  1116. return ptb->lLineSize;
  1117. case TBM_GETTHUMBRECT:
  1118. if (lParam) {
  1119. *((LPRECT)lParam) = ptb->rcThumb;
  1120. if (ISVERT(ptb)) FlipRect((LPRECT)lParam);
  1121. }
  1122. break;
  1123. case TBM_GETTOOLTIPS:
  1124. return (LRESULT)ptb->hwndToolTips;
  1125. case TBM_SETTOOLTIPS:
  1126. ptb->hwndToolTips = (HWND)wParam;
  1127. break;
  1128. case TBM_SETTIPSIDE:
  1129. {
  1130. UINT uOldSide = ptb->uTipSide;
  1131. ptb->uTipSide = (UINT) wParam;
  1132. return uOldSide;
  1133. }
  1134. case TBM_GETCHANNELRECT:
  1135. GetChannelRect(ptb, (LPRECT)lParam);
  1136. break;
  1137. case TBM_SETBUDDY:
  1138. return (LRESULT)TBSetBuddy(ptb, (BOOL)wParam, (HWND)lParam);
  1139. case TBM_GETBUDDY:
  1140. return (LRESULT)(wParam ? ptb->hwndBuddyLeft : ptb->hwndBuddyRight);
  1141. case WM_GETOBJECT:
  1142. if( lParam == OBJID_QUERYCLASSNAMEIDX )
  1143. return MSAA_CLASSNAMEIDX_TRACKBAR;
  1144. goto DoDefault;
  1145. default:
  1146. if (uMsg == g_msgMSWheel) {
  1147. int iWheelDelta;
  1148. int cDetants;
  1149. long lPos;
  1150. ULONG ulPos;
  1151. if (g_bRunOnNT || g_bRunOnMemphis)
  1152. iWheelDelta = (int)(short)HIWORD(wParam);
  1153. else
  1154. iWheelDelta = (int)wParam;
  1155. // Update count of scroll amount
  1156. gcWheelDelta -= iWheelDelta;
  1157. cDetants = gcWheelDelta / WHEEL_DELTA;
  1158. if (cDetants != 0) {
  1159. gcWheelDelta %= WHEEL_DELTA;
  1160. }
  1161. if (g_bRunOnNT || g_bRunOnMemphis)
  1162. {
  1163. if (wParam & (MK_SHIFT | MK_CONTROL))
  1164. goto DoDefault;
  1165. }
  1166. else
  1167. {
  1168. if (GetKeyState(VK_SHIFT) < 0 || GetKeyState(VK_CONTROL) < 0)
  1169. goto DoDefault;
  1170. }
  1171. if (SHRT_MIN <= ptb->lLogPos && ptb->lLogPos <= SHRT_MAX) {
  1172. lPos = ptb->lLogPos + cDetants;
  1173. lPos = BOUND(lPos, ptb->lLogMin, ptb->lLogMax);
  1174. ulPos = BOUND(lPos, SHRT_MIN, SHRT_MAX);
  1175. if ((long) ulPos != ptb->lLogPos) {
  1176. MoveThumb(ptb, (long) ulPos);
  1177. DoTrack(ptb, TB_THUMBPOSITION, ulPos);
  1178. }
  1179. }
  1180. return TRUE;
  1181. } else {
  1182. LRESULT lres;
  1183. if (CCWndProc(&ptb->ci, uMsg, wParam, lParam, &lres))
  1184. return lres;
  1185. }
  1186. DoDefault:
  1187. return DefWindowProc(hwnd, uMsg, wParam, lParam);
  1188. }
  1189. return 0L;
  1190. }
  1191. /* DoTrack() */
  1192. void NEAR PASCAL DoTrack(PTRACKBAR ptb, int cmd, DWORD dwPos)
  1193. {
  1194. LONG dpos;
  1195. switch(cmd) {
  1196. case TB_LINEDOWN:
  1197. dpos = ptb->lLineSize;
  1198. goto DMoveThumb;
  1199. case TB_LINEUP:
  1200. dpos = -ptb->lLineSize;
  1201. goto DMoveThumb;
  1202. case TB_PAGEUP:
  1203. case TB_PAGEDOWN:
  1204. if (ptb->lPageSize == -1) {
  1205. dpos = (ptb->lLogMax - ptb->lLogMin) / 5;
  1206. if (!dpos)
  1207. dpos = 1;
  1208. } else {
  1209. dpos = ptb->lPageSize;
  1210. }
  1211. if (cmd == TB_PAGEUP)
  1212. dpos *= -1;
  1213. DMoveThumb: // move delta
  1214. MoveThumb(ptb, ptb->lLogPos + dpos);
  1215. break;
  1216. case TB_BOTTOM:
  1217. dpos = ptb->lLogMax; // the BOUND will take care of this;
  1218. goto ABSMoveThumb;
  1219. case TB_TOP:
  1220. dpos = ptb->lLogMin; // the BOUND will take care of this;
  1221. ABSMoveThumb: // move absolute
  1222. MoveThumb(ptb, dpos);
  1223. break;
  1224. default: // do nothing
  1225. break;
  1226. }
  1227. // BUGBUG: for now, send both in vertical mode
  1228. // note: we only send back a WORD worth of the position.
  1229. if (ISVERT(ptb)) {
  1230. FORWARD_WM_VSCROLL(ptb->ci.hwndParent, ptb->ci.hwnd, cmd, LOWORD(dwPos), SendMessage);
  1231. } else
  1232. FORWARD_WM_HSCROLL(ptb->ci.hwndParent, ptb->ci.hwnd, cmd, LOWORD(dwPos), SendMessage);
  1233. }
  1234. /* WTrackType() */
  1235. WORD NEAR PASCAL WTrackType(PTRACKBAR ptb, LONG lParam)
  1236. {
  1237. POINT pt;
  1238. pt.x = GET_X_LPARAM(lParam);
  1239. pt.y = GET_Y_LPARAM(lParam);
  1240. if (ptb->Flags & TBF_NOTHUMB ||
  1241. ptb->ci.style & TBS_NOTHUMB) // If no thumb, just leave.
  1242. return 0;
  1243. if (ISVERT(ptb)) {
  1244. // put point in virtual coordinates
  1245. int temp;
  1246. temp = pt.x;
  1247. pt.x = pt.y;
  1248. pt.y = temp;
  1249. }
  1250. if (PtInRect(&ptb->rcThumb, pt))
  1251. return TB_THUMBTRACK;
  1252. if (!PtInRect(&ptb->rc, pt))
  1253. return 0;
  1254. if (pt.x >= ptb->rcThumb.left)
  1255. return TB_PAGEDOWN;
  1256. else
  1257. return TB_PAGEUP;
  1258. }
  1259. /* TBTrackInit() */
  1260. void NEAR PASCAL TBTrackInit(PTRACKBAR ptb, LPARAM lParam)
  1261. {
  1262. WORD wCmd;
  1263. if (ptb->Flags & TBF_NOTHUMB ||
  1264. ptb->ci.style & TBS_NOTHUMB) // No thumb: just leave.
  1265. return;
  1266. wCmd = WTrackType(ptb, (LONG) lParam);
  1267. if (!wCmd)
  1268. return;
  1269. SetCapture(ptb->ci.hwnd);
  1270. ptb->Cmd = wCmd;
  1271. ptb->dwDragPos = (DWORD)-1;
  1272. // Set up for auto-track (if needed).
  1273. if (wCmd != TB_THUMBTRACK) {
  1274. // Set our timer up
  1275. SetTimer(ptb->ci.hwnd, TIMER_ID, REPEATTIME, NULL);
  1276. } else {
  1277. int xPos;
  1278. // thumb tracking...
  1279. // store the offset between the cursor's position and the center of the thumb
  1280. xPos = TBLogToPhys(ptb, ptb->lLogPos);
  1281. ptb->dwDragOffset = (ISVERT(ptb) ? HIWORD(lParam) : LOWORD(lParam)) - xPos;
  1282. if (ptb->hwndToolTips) {
  1283. TOOLINFO ti;
  1284. // don't bother setting the rect because we'll do it below
  1285. // in FlushToolTipsMgr;
  1286. ti.cbSize = sizeof(ti);
  1287. ti.uFlags = TTF_TRACK | TTF_CENTERTIP;
  1288. ti.hwnd = ptb->ci.hwnd;
  1289. ti.uId = (UINT_PTR)ptb->ci.hwnd;
  1290. SendMessage(ptb->hwndToolTips, TTM_TRACKACTIVATE, (WPARAM)TRUE, (LPARAM)&ti);
  1291. }
  1292. }
  1293. TBTrack(ptb, lParam);
  1294. }
  1295. /* EndTrack() */
  1296. void NEAR PASCAL TBTrackEnd(PTRACKBAR ptb)
  1297. {
  1298. // Decide how we're ending this thing.
  1299. if (ptb->Cmd == TB_THUMBTRACK) {
  1300. if (ptb->hwndToolTips)
  1301. SendMessage(ptb->hwndToolTips, TTM_TRACKACTIVATE, (WPARAM)FALSE, 0);
  1302. DoTrack(ptb, TB_THUMBPOSITION, ptb->dwDragPos);
  1303. }
  1304. KillTimer(ptb->ci.hwnd, TIMER_ID);
  1305. // Always send TB_ENDTRACK message if there's some sort of command tracking.
  1306. if (ptb->Cmd != (UINT)-1) {
  1307. DoTrack(ptb, TB_ENDTRACK, 0);
  1308. // Nothing going on.
  1309. ptb->Cmd = (UINT)-1;
  1310. }
  1311. MoveThumb(ptb, ptb->lLogPos);
  1312. }
  1313. #define TBTS_RIGHTLEFT 1 // low bit means it's on the right or left
  1314. void NEAR PASCAL TBTrack(PTRACKBAR ptb, LPARAM lParam)
  1315. {
  1316. DWORD dwPos;
  1317. WORD pos;
  1318. // See if we're tracking the thumb
  1319. if (ptb->Cmd == TB_THUMBTRACK) {
  1320. pos = (ISVERT(ptb)) ? HIWORD(lParam) : LOWORD(lParam);
  1321. pos -= (WORD) ptb->dwDragOffset;
  1322. dwPos = TBPhysToLog(ptb, (int)(SHORT)pos);
  1323. // Tentative position changed -- notify the guy.
  1324. if (dwPos != ptb->dwDragPos) {
  1325. ptb->dwDragPos = dwPos;
  1326. MoveThumb(ptb, dwPos);
  1327. DoTrack(ptb, TB_THUMBTRACK, dwPos);
  1328. }
  1329. if (ptb->hwndToolTips) {
  1330. RECT rc;
  1331. POINT pt;
  1332. int iPixel;
  1333. UINT uTipSide = ptb->uTipSide;
  1334. // find the center of the window
  1335. GetClientRect(ptb->ci.hwnd, &rc);
  1336. pt.x = rc.right / 2;
  1337. pt.y = rc.bottom / 2;
  1338. //find the position of the thumb
  1339. iPixel = TBLogToPhys(ptb, dwPos);
  1340. if (ISVERT(ptb)) {
  1341. pt.y = iPixel;
  1342. uTipSide |= TBTS_RIGHTLEFT;
  1343. } else {
  1344. pt.x = iPixel;
  1345. uTipSide &= ~TBTS_RIGHTLEFT;
  1346. }
  1347. // move it out to the requested side
  1348. switch (uTipSide) {
  1349. case TBTS_TOP:
  1350. pt.y = -1;
  1351. break;
  1352. case TBTS_LEFT:
  1353. pt.x = -1;
  1354. break;
  1355. case TBTS_BOTTOM:
  1356. pt.y = rc.bottom + 1;
  1357. break;
  1358. case TBTS_RIGHT:
  1359. pt.x = rc.right + 1;
  1360. break;
  1361. }
  1362. // map it to screen coordinates
  1363. MapWindowPoints(ptb->ci.hwnd, HWND_DESKTOP, &pt, 1);
  1364. SendMessage(ptb->hwndToolTips, TTM_TRACKPOSITION, 0, MAKELONG(pt.x, pt.y));
  1365. }
  1366. }
  1367. else {
  1368. if (ptb->Cmd != WTrackType(ptb, (LONG) lParam))
  1369. return;
  1370. DoTrack(ptb, ptb->Cmd, 0);
  1371. }
  1372. }
  1373. void NEAR PASCAL FlushChanges(PTRACKBAR ptb)
  1374. {
  1375. HBRUSH hbr;
  1376. NMCUSTOMDRAW nmcd;
  1377. #ifdef WIN32
  1378. hbr = FORWARD_WM_CTLCOLORSTATIC(ptb->ci.hwndParent, ptb->hdc, ptb->ci.hwnd, SendMessage);
  1379. #else
  1380. hbr = FORWARD_WM_CTLCOLOR(ptb->ci.hwndParent, ptb->hdc, ptb->ci.hwnd, CTLCOLOR_STATIC, SendMessage);
  1381. #endif
  1382. if (hbr) {
  1383. RECT rc;
  1384. BOOL fClear = FALSE;
  1385. if ( ptb->wDirtyFlags == TBC_ALL ) {
  1386. GetClientRect(ptb->ci.hwnd, &rc);
  1387. fClear = TRUE;
  1388. } else if (ptb->wDirtyFlags & TBC_THUMB) {
  1389. rc = ptb->rc;
  1390. rc.left = 0;
  1391. rc.right += ptb->iThumbWidth;
  1392. if (ISVERT(ptb))
  1393. FlipRect(&rc);
  1394. fClear = TRUE;
  1395. }
  1396. if (fClear)
  1397. FillRect(ptb->hdc, &rc, hbr);
  1398. }
  1399. nmcd.hdc = ptb->hdc;
  1400. if (ptb->ci.hwnd == GetFocus())
  1401. nmcd.uItemState = CDIS_FOCUS;
  1402. else
  1403. nmcd.uItemState = 0;
  1404. #ifdef KEYBOARDCUES
  1405. #if 0
  1406. // BUGBUG: Custom draw stuff for UISTATE (stephstm)
  1407. if (CCGetUIState(&(ptb->ci), KC_TBD))
  1408. nmcd.uItemState |= CDIS_SHOWKEYBOARDCUES;
  1409. #endif
  1410. #endif
  1411. nmcd.lItemlParam = 0;
  1412. ptb->ci.dwCustom = CICustomDrawNotify(&ptb->ci, CDDS_PREPAINT, &nmcd);
  1413. // for skip default, no other flags make sense.. only allow that one
  1414. if (!(ptb->ci.dwCustom == CDRF_SKIPDEFAULT)) {
  1415. DWORD dwRet = 0;
  1416. // do the actual drawing
  1417. if (nmcd.uItemState & CDIS_FOCUS)
  1418. {
  1419. DrawFocus(ptb, hbr);
  1420. }
  1421. nmcd.uItemState = 0;
  1422. if (ptb->wDirtyFlags & TBC_TICS) {
  1423. nmcd.dwItemSpec = TBCD_TICS;
  1424. dwRet = CICustomDrawNotify(&ptb->ci, CDDS_ITEMPREPAINT, &nmcd);
  1425. if (!(dwRet == CDRF_SKIPDEFAULT)) {
  1426. DrawTics(ptb);
  1427. if (dwRet & CDRF_NOTIFYPOSTPAINT) {
  1428. nmcd.dwItemSpec = TBCD_TICS;
  1429. CICustomDrawNotify(&ptb->ci, CDDS_ITEMPOSTPAINT, &nmcd);
  1430. }
  1431. }
  1432. }
  1433. if (ptb->wDirtyFlags & TBC_THUMB) {
  1434. // the channel
  1435. GetChannelRect(ptb, &nmcd.rc);
  1436. if (ISVERT(ptb))
  1437. FlipRect(&nmcd.rc);
  1438. nmcd.dwItemSpec = TBCD_CHANNEL;
  1439. dwRet = CICustomDrawNotify(&ptb->ci, CDDS_ITEMPREPAINT, &nmcd);
  1440. if (!(dwRet == CDRF_SKIPDEFAULT)) {
  1441. // flip it back from the last notify
  1442. if (ISVERT(ptb))
  1443. FlipRect(&nmcd.rc);
  1444. // the actual drawing
  1445. DrawChannel(ptb, &nmcd.rc);
  1446. if (dwRet & CDRF_NOTIFYPOSTPAINT) {
  1447. if (ISVERT(ptb))
  1448. FlipRect(&nmcd.rc);
  1449. nmcd.dwItemSpec = TBCD_CHANNEL;
  1450. CICustomDrawNotify(&ptb->ci, CDDS_ITEMPOSTPAINT, &nmcd);
  1451. }
  1452. }
  1453. // the thumb
  1454. nmcd.rc = ptb->rcThumb;
  1455. if (ptb->Cmd == TB_THUMBTRACK) {
  1456. nmcd.uItemState = CDIS_SELECTED;
  1457. }
  1458. if (ISVERT(ptb))
  1459. FlipRect(&nmcd.rc);
  1460. nmcd.dwItemSpec = TBCD_THUMB;
  1461. dwRet = CICustomDrawNotify(&ptb->ci, CDDS_ITEMPREPAINT, &nmcd);
  1462. if (!(dwRet == CDRF_SKIPDEFAULT)) {
  1463. if (ISVERT(ptb))
  1464. FlipRect(&nmcd.rc);
  1465. // the actual drawing
  1466. DrawThumb(ptb, &nmcd.rc, nmcd.uItemState & CDIS_SELECTED);
  1467. if (dwRet & CDRF_NOTIFYPOSTPAINT) {
  1468. if (ISVERT(ptb))
  1469. FlipRect(&nmcd.rc);
  1470. nmcd.dwItemSpec = TBCD_THUMB;
  1471. CICustomDrawNotify(&ptb->ci, CDDS_ITEMPOSTPAINT, &nmcd);
  1472. }
  1473. }
  1474. }
  1475. ptb->wDirtyFlags = 0;
  1476. // notify parent afterwards if they want us to
  1477. if (ptb->ci.dwCustom & CDRF_NOTIFYPOSTPAINT) {
  1478. CICustomDrawNotify(&ptb->ci, CDDS_POSTPAINT, &nmcd);
  1479. }
  1480. }
  1481. #ifdef TB_DEBUG
  1482. DebugMsg(DM_TRACE, TEXT("DrawDone"));
  1483. {
  1484. HDC hdcScreen;
  1485. RECT rcClient;
  1486. hdcScreen = GetDC(NULL);
  1487. GetClientRect(ptb->ci.hwnd, &rcClient);
  1488. BitBlt(hdcScreen, 200, 0, 200 + rcClient.right, rcClient.bottom, ptb->hdc, 0,0, SRCCOPY);
  1489. ReleaseDC(NULL, hdcScreen);
  1490. }
  1491. #endif
  1492. }