Leaked source code of windows server 2003
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.

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