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.

599 lines
16 KiB

  1. /*-----------------------------------------------------------------------
  2. **
  3. ** Progress.c
  4. **
  5. ** A "gas gauge" type control for showing application progress.
  6. **
  7. **-----------------------------------------------------------------------*/
  8. #include "ctlspriv.h"
  9. // REARCHITECT raymondc - should Process control support __int64 on Win64?
  10. // Should it support this anyway? Used in the filesystem,
  11. // this would prevent the shell from having to fudge it
  12. typedef struct {
  13. HWND hwnd;
  14. DWORD dwStyle;
  15. int iLow, iHigh;
  16. int iPos;
  17. int iMarqueePos;
  18. int iStep;
  19. HFONT hfont;
  20. COLORREF _clrBk;
  21. COLORREF _clrBar;
  22. HTHEME hTheme;
  23. } PRO_DATA;
  24. LRESULT CALLBACK ProgressWndProc(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam);
  25. BOOL InitProgressClass(HINSTANCE hInstance)
  26. {
  27. WNDCLASS wc = {0};
  28. wc.lpfnWndProc = ProgressWndProc;
  29. wc.lpszClassName = s_szPROGRESS_CLASS;
  30. wc.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;
  31. wc.hInstance = hInstance; // use DLL instance if in DLL
  32. wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  33. wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
  34. wc.cbWndExtra = sizeof(PRO_DATA *); // store a pointer
  35. if (!RegisterClass(&wc) && !GetClassInfo(hInstance, s_szPROGRESS_CLASS, &wc))
  36. return FALSE;
  37. return TRUE;
  38. }
  39. #define MARQUEE_TIMER 1
  40. void ProEraseBkgnd(PRO_DATA *ppd, HDC hdc, RECT* prcClient)
  41. {
  42. COLORREF clrBk = ppd->_clrBk;
  43. if (clrBk == CLR_DEFAULT)
  44. clrBk = g_clrBtnFace;
  45. FillRectClr(hdc, prcClient, clrBk);
  46. }
  47. void ProGetPaintMetrics(PRO_DATA *ppd, RECT* prcClient, RECT *prc, int *pdxSpace, int *pdxBlock)
  48. {
  49. int dxSpace, dxBlock;
  50. RECT rc;
  51. GetClientRect(ppd->hwnd, prcClient);
  52. if (ppd->hTheme)
  53. {
  54. int iPartBar = (ppd->dwStyle & PBS_VERTICAL)? PP_BARVERT : PP_BAR;
  55. GetThemeBackgroundContentRect(ppd->hTheme, NULL, iPartBar, 0, prcClient, &rc);
  56. }
  57. else
  58. {
  59. // give 1 pixel around the bar
  60. rc = *prcClient;
  61. InflateRect(&rc, -1, -1);
  62. }
  63. if (ppd->dwStyle & PBS_VERTICAL)
  64. dxBlock = (rc.right - rc.left) * 2 / 3;
  65. else
  66. dxBlock = (rc.bottom - rc.top) * 2 / 3;
  67. dxSpace = 2;
  68. if (dxBlock == 0)
  69. dxBlock = 1; // avoid div by zero
  70. if (ppd->dwStyle & PBS_SMOOTH)
  71. {
  72. dxBlock = 1;
  73. dxSpace = 0;
  74. }
  75. if (ppd->hTheme)
  76. {
  77. int dx;
  78. if (SUCCEEDED(GetThemeInt(ppd->hTheme, 0, 0, TMT_PROGRESSCHUNKSIZE, &dx)))
  79. {
  80. dxBlock = dx;
  81. }
  82. if (SUCCEEDED(GetThemeInt(ppd->hTheme, 0, 0, TMT_PROGRESSSPACESIZE, &dx)))
  83. {
  84. dxSpace = dx;
  85. }
  86. }
  87. *prc = rc;
  88. *pdxSpace = dxSpace;
  89. *pdxBlock = dxBlock;
  90. }
  91. int GetProgressScreenPos(PRO_DATA *ppd, int iNewPos, RECT *pRect)
  92. {
  93. int iStart, iEnd;
  94. if (ppd->dwStyle & PBS_VERTICAL)
  95. {
  96. iStart = pRect->top;
  97. iEnd = pRect->bottom;
  98. }
  99. else
  100. {
  101. iStart = pRect->left;
  102. iEnd = pRect->right;
  103. }
  104. return MulDiv(iEnd - iStart, iNewPos - ppd->iLow, ppd->iHigh - ppd->iLow);
  105. }
  106. BOOL ProNeedsRepaint(PRO_DATA *ppd, int iOldPos)
  107. {
  108. BOOL fRet = FALSE;
  109. RECT rc, rcClient;
  110. int dxSpace, dxBlock;
  111. int x, xOld;
  112. if (iOldPos != ppd->iPos)
  113. {
  114. ProGetPaintMetrics(ppd, &rcClient, &rc, &dxSpace, &dxBlock);
  115. x = GetProgressScreenPos(ppd, ppd->iPos, &rc);
  116. xOld = GetProgressScreenPos(ppd, iOldPos, &rc);
  117. if (x != xOld)
  118. {
  119. if (dxBlock == 1 && dxSpace == 0)
  120. {
  121. fRet = TRUE;
  122. }
  123. else
  124. {
  125. int nBlocks, nOldBlocks;
  126. nBlocks = (x + (dxBlock + dxSpace) - 1) / (dxBlock + dxSpace); // round up
  127. nOldBlocks = (xOld + (dxBlock + dxSpace) - 1) / (dxBlock + dxSpace); // round up
  128. if (nBlocks != nOldBlocks)
  129. fRet = TRUE;
  130. }
  131. }
  132. }
  133. return fRet;
  134. }
  135. int UpdatePosition(PRO_DATA *ppd, int iNewPos, BOOL bAllowWrap)
  136. {
  137. int iOldPos = ppd->iPos;
  138. UINT uRedraw = RDW_INVALIDATE | RDW_UPDATENOW;
  139. BOOL fNeedsRepaint = TRUE;
  140. if (ppd->dwStyle & PBS_MARQUEE)
  141. {
  142. // Do an immediate repaint
  143. uRedraw |= RDW_ERASE;
  144. }
  145. else
  146. {
  147. if (ppd->iLow == ppd->iHigh)
  148. iNewPos = ppd->iLow;
  149. if (iNewPos < ppd->iLow)
  150. {
  151. if (!bAllowWrap)
  152. iNewPos = ppd->iLow;
  153. else
  154. iNewPos = ppd->iHigh - ((ppd->iLow - iNewPos) % (ppd->iHigh - ppd->iLow));
  155. }
  156. else if (iNewPos > ppd->iHigh)
  157. {
  158. if (!bAllowWrap)
  159. iNewPos = ppd->iHigh;
  160. else
  161. iNewPos = ppd->iLow + ((iNewPos - ppd->iHigh) % (ppd->iHigh - ppd->iLow));
  162. }
  163. // if moving backwards, erase old version
  164. if (iNewPos < iOldPos)
  165. uRedraw |= RDW_ERASE;
  166. ppd->iPos = iNewPos;
  167. fNeedsRepaint = ProNeedsRepaint(ppd, iOldPos);
  168. }
  169. if (fNeedsRepaint)
  170. {
  171. RedrawWindow(ppd->hwnd, NULL, NULL, uRedraw);
  172. NotifyWinEvent(EVENT_OBJECT_VALUECHANGE, ppd->hwnd, OBJID_CLIENT, 0);
  173. }
  174. return iOldPos;
  175. }
  176. /* MarqueeShowBlock
  177. iBlock = The block we're considering - returns TRUE if this block should be shown.
  178. iMarqueeBlock = The block at the center of the marquee pattern
  179. nBlocks = The number of blocks in the bar
  180. */
  181. #define BLOCKSINMARQUEE 5
  182. BOOL MarqueeShowBlock(int iBlock, int iMarqueeBlock, int nBlocks)
  183. {
  184. int i;
  185. for (i = 0; i < BLOCKSINMARQUEE; i++)
  186. {
  187. if ((iMarqueeBlock + i - (BLOCKSINMARQUEE / 2)) % nBlocks == iBlock)
  188. {
  189. return TRUE;
  190. }
  191. }
  192. return FALSE;
  193. }
  194. #define HIGHBG g_clrHighlight
  195. #define HIGHFG g_clrHighlightText
  196. #define LOWBG g_clrBtnFace
  197. #define LOWFG g_clrBtnText
  198. void ProPaint(PRO_DATA *ppd, HDC hdcIn)
  199. {
  200. int x, dxSpace, dxBlock, nBlocks, i;
  201. HDC hdc, hdcPaint, hdcMem = NULL;
  202. HBITMAP hbmpOld = NULL;
  203. RECT rc, rcClient;
  204. PAINTSTRUCT ps;
  205. HRESULT hr = E_FAIL;
  206. int iPart;
  207. BOOL fTransparent = FALSE;
  208. BOOL fShowBlock;
  209. if (hdcIn == NULL)
  210. {
  211. hdc = hdcPaint = BeginPaint(ppd->hwnd, &ps);
  212. // Only make large enough for clipping region
  213. hdcMem = CreateCompatibleDC(hdc);
  214. if (hdcMem)
  215. {
  216. HBITMAP hMemBm = CreateCompatibleBitmap(hdc, RECTWIDTH(ps.rcPaint), RECTHEIGHT(ps.rcPaint));
  217. if (hMemBm)
  218. {
  219. hbmpOld = SelectObject(hdcMem, hMemBm);
  220. // Override painting DC with memory DC
  221. hdc = hdcMem;
  222. }
  223. else
  224. DeleteDC(hdcMem);
  225. }
  226. }
  227. else
  228. hdc = hdcIn;
  229. ProGetPaintMetrics(ppd, &rcClient, &rc, &dxSpace, &dxBlock);
  230. if (hdcMem)
  231. {
  232. // OffsetWindowOrgEx() doesn't work with the themes, need to change painting rects
  233. OffsetRect(&rcClient, -ps.rcPaint.left, -ps.rcPaint.top);
  234. OffsetRect(&rc, -ps.rcPaint.left, -ps.rcPaint.top);
  235. }
  236. x = GetProgressScreenPos(ppd, ppd->iPos, &rcClient);
  237. // Paint background
  238. if (ppd->hTheme)
  239. {
  240. int iPartBar = (ppd->dwStyle & PBS_VERTICAL)? PP_BARVERT : PP_BAR;
  241. iPart = (ppd->dwStyle & PBS_VERTICAL)? PP_CHUNKVERT: PP_CHUNK;
  242. DrawThemeBackground(ppd->hTheme, hdc, iPartBar, 0, &rcClient, 0);
  243. }
  244. else
  245. {
  246. ProEraseBkgnd(ppd, hdc, &rcClient);
  247. }
  248. if (dxBlock == 1 && dxSpace == 0 && ppd->hTheme != NULL)
  249. {
  250. if (ppd->dwStyle & PBS_VERTICAL)
  251. rc.top = x;
  252. else
  253. rc.right = x;
  254. hr = DrawThemeBackground(ppd->hTheme, hdc, iPart, 0, &rc, 0);
  255. }
  256. else
  257. {
  258. if (ppd->dwStyle & PBS_MARQUEE)
  259. {
  260. // Consider the full bar
  261. if (ppd->dwStyle & PBS_VERTICAL)
  262. {
  263. nBlocks = ((rc.bottom - rc.top) + (dxBlock + dxSpace) - 1) / (dxBlock + dxSpace); // round up
  264. }
  265. else
  266. {
  267. nBlocks = ((rc.right - rc.left) + (dxBlock + dxSpace) - 1) / (dxBlock + dxSpace); // round up
  268. }
  269. ppd->iMarqueePos = (ppd->iMarqueePos + 1) % nBlocks;
  270. }
  271. else
  272. {
  273. nBlocks = (x + (dxBlock + dxSpace) - 1) / (dxBlock + dxSpace); // round up
  274. }
  275. for (i = 0; i < nBlocks; i++)
  276. {
  277. if (ppd->dwStyle & PBS_VERTICAL)
  278. {
  279. rc.top = rc.bottom - dxBlock;
  280. // are we past the end?
  281. if (rc.bottom <= rcClient.top)
  282. break;
  283. if (rc.top <= rcClient.top)
  284. rc.top = rcClient.top + 1;
  285. }
  286. else
  287. {
  288. rc.right = rc.left + dxBlock;
  289. // are we past the end?
  290. if (rc.left >= rcClient.right)
  291. break;
  292. if (rc.right >= rcClient.right)
  293. rc.right = rcClient.right - 1;
  294. }
  295. if (ppd->dwStyle & PBS_MARQUEE)
  296. {
  297. fShowBlock = MarqueeShowBlock(i, ppd->iMarqueePos, nBlocks);
  298. }
  299. else
  300. {
  301. fShowBlock = TRUE;
  302. }
  303. if (fShowBlock)
  304. {
  305. if (ppd->hTheme)
  306. {
  307. hr = DrawThemeBackground(ppd->hTheme, hdc, iPart, 0, &rc, 0);
  308. }
  309. if (FAILED(hr))
  310. {
  311. if (ppd->_clrBar == CLR_DEFAULT)
  312. FillRectClr(hdc, &rc, g_clrHighlight);
  313. else
  314. FillRectClr(hdc, &rc, ppd->_clrBar);
  315. }
  316. }
  317. if (ppd->dwStyle & PBS_VERTICAL)
  318. {
  319. rc.bottom = rc.top - dxSpace;
  320. }
  321. else
  322. {
  323. rc.left = rc.right + dxSpace;
  324. }
  325. }
  326. }
  327. if (hdcMem != NULL)
  328. {
  329. BitBlt(hdcPaint, ps.rcPaint.left, ps.rcPaint.top, RECTWIDTH(ps.rcPaint), RECTHEIGHT(ps.rcPaint),
  330. hdc, 0, 0, SRCCOPY);
  331. DeleteObject(SelectObject(hdcMem, hbmpOld));
  332. DeleteDC(hdcMem);
  333. }
  334. if (hdcIn == NULL)
  335. EndPaint(ppd->hwnd, &ps);
  336. }
  337. LRESULT Progress_OnCreate(HWND hWnd, LPCREATESTRUCT pcs)
  338. {
  339. PRO_DATA *ppd = (PRO_DATA *)LocalAlloc(LPTR, sizeof(*ppd));
  340. if (!ppd)
  341. return -1;
  342. // remove ugly double 3d edge
  343. SetWindowPtr(hWnd, 0, ppd);
  344. ppd->hwnd = hWnd;
  345. ppd->iHigh = 100; // default to 0-100
  346. ppd->iStep = 10; // default to step of 10
  347. ppd->dwStyle = pcs->style;
  348. ppd->_clrBk = CLR_DEFAULT;
  349. ppd->_clrBar = CLR_DEFAULT;
  350. ppd->hTheme = OpenThemeData(hWnd, L"Progress");
  351. if (ppd->hTheme)
  352. {
  353. SetWindowLong(hWnd, GWL_EXSTYLE, (pcs->dwExStyle & ~(WS_EX_CLIENTEDGE | WS_EX_STATICEDGE | WS_BORDER)));
  354. SetWindowPos(hWnd, NULL, 0,0,0,0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
  355. }
  356. else
  357. {
  358. // hack of the 3d client edge that WS_BORDER implies in dialogs
  359. // add the 1 pixel static edge that we really want
  360. SetWindowLong(hWnd, GWL_EXSTYLE, (pcs->dwExStyle & ~WS_EX_CLIENTEDGE) | WS_EX_STATICEDGE);
  361. if (!(pcs->dwExStyle & WS_EX_STATICEDGE))
  362. SetWindowPos(hWnd, NULL, 0,0,0,0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
  363. }
  364. return 0;
  365. }
  366. LRESULT MarqueeSetTimer(PRO_DATA *ppd, BOOL fDoMarquee, UINT iMilliseconds)
  367. {
  368. if (fDoMarquee)
  369. {
  370. SetTimer(ppd->hwnd, MARQUEE_TIMER, iMilliseconds ? iMilliseconds : 30, NULL);
  371. ppd->iMarqueePos = 0;
  372. }
  373. else
  374. {
  375. KillTimer(ppd->hwnd, MARQUEE_TIMER);
  376. }
  377. return 1;
  378. }
  379. LRESULT CALLBACK ProgressWndProc(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam)
  380. {
  381. int x;
  382. HFONT hFont;
  383. PRO_DATA *ppd = (PRO_DATA *)GetWindowPtr(hWnd, 0);
  384. switch (wMsg)
  385. {
  386. case WM_CREATE:
  387. return Progress_OnCreate(hWnd, (LPCREATESTRUCT)lParam);
  388. case WM_DESTROY:
  389. if (ppd)
  390. {
  391. if (ppd->hTheme)
  392. {
  393. CloseThemeData(ppd->hTheme);
  394. }
  395. KillTimer(hWnd, MARQUEE_TIMER);
  396. LocalFree((HLOCAL)ppd);
  397. }
  398. break;
  399. case WM_SYSCOLORCHANGE:
  400. InitGlobalColors();
  401. InvalidateRect(hWnd, NULL, TRUE);
  402. break;
  403. case WM_SETFONT:
  404. hFont = ppd->hfont;
  405. ppd->hfont = (HFONT)wParam;
  406. return (LRESULT)(UINT_PTR)hFont;
  407. case WM_GETFONT:
  408. return (LRESULT)(UINT_PTR)ppd->hfont;
  409. case PBM_GETPOS:
  410. return ppd->iPos;
  411. case PBM_GETRANGE:
  412. if (lParam) {
  413. PPBRANGE ppb = (PPBRANGE)lParam;
  414. ppb->iLow = ppd->iLow;
  415. ppb->iHigh = ppd->iHigh;
  416. }
  417. return (wParam ? ppd->iLow : ppd->iHigh);
  418. case PBM_SETRANGE:
  419. // win95 compat
  420. wParam = LOWORD(lParam);
  421. lParam = HIWORD(lParam);
  422. // fall through
  423. case PBM_SETRANGE32:
  424. {
  425. LRESULT lret = MAKELONG(ppd->iLow, ppd->iHigh);
  426. // only repaint if something actually changed
  427. if ((int)wParam != ppd->iLow || (int)lParam != ppd->iHigh)
  428. {
  429. ppd->iHigh = (int)lParam;
  430. ppd->iLow = (int)wParam;
  431. // force an invalidation/erase but don't redraw yet
  432. RedrawWindow(ppd->hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE);
  433. UpdatePosition(ppd, ppd->iPos, FALSE);
  434. }
  435. return lret;
  436. }
  437. case PBM_SETPOS:
  438. return (LRESULT)UpdatePosition(ppd, (int) wParam, FALSE);
  439. case PBM_SETSTEP:
  440. x = ppd->iStep;
  441. ppd->iStep = (int)wParam;
  442. return (LRESULT)x;
  443. case PBM_SETMARQUEE:
  444. return MarqueeSetTimer(ppd, (BOOL) wParam, (UINT) lParam);
  445. case WM_TIMER:
  446. // Pos doesn't move for PSB_MARQUEE mode
  447. UpdatePosition(ppd, ppd->iPos, TRUE);
  448. return 0;
  449. case PBM_STEPIT:
  450. return (LRESULT)UpdatePosition(ppd, ppd->iStep + ppd->iPos, TRUE);
  451. case PBM_DELTAPOS:
  452. return (LRESULT)UpdatePosition(ppd, ppd->iPos + (int)wParam, FALSE);
  453. case PBM_SETBKCOLOR:
  454. {
  455. COLORREF clr = ppd->_clrBk;
  456. ppd->_clrBk = (COLORREF)lParam;
  457. InvalidateRect(hWnd, NULL, TRUE);
  458. return clr;
  459. }
  460. case PBM_SETBARCOLOR:
  461. {
  462. COLORREF clr = ppd->_clrBar;
  463. ppd->_clrBar = (COLORREF)lParam;
  464. InvalidateRect(hWnd, NULL, TRUE);
  465. return clr;
  466. }
  467. case WM_PRINTCLIENT:
  468. case WM_PAINT:
  469. ProPaint(ppd,(HDC)wParam);
  470. break;
  471. case WM_ERASEBKGND:
  472. return 1; // Filled in ProPaint
  473. case WM_GETOBJECT:
  474. if (lParam == OBJID_QUERYCLASSNAMEIDX)
  475. return MSAA_CLASSNAMEIDX_PROGRESS;
  476. goto DoDefault;
  477. case WM_THEMECHANGED:
  478. if (ppd->hTheme)
  479. CloseThemeData(ppd->hTheme);
  480. ppd->hTheme = OpenThemeData(hWnd, L"Progress");
  481. if (ppd->hTheme == NULL)
  482. {
  483. SetWindowLong(hWnd, GWL_EXSTYLE, GetWindowLong(hWnd, GWL_EXSTYLE) | WS_EX_STATICEDGE);
  484. SetWindowPos(hWnd, NULL, 0,0,0,0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
  485. }
  486. InvalidateRect(hWnd, NULL, TRUE);
  487. break;
  488. case WM_STYLECHANGED:
  489. if (wParam == GWL_STYLE)
  490. {
  491. ppd->dwStyle = ((STYLESTRUCT *)lParam)->styleNew;
  492. // change positions to force repaint
  493. ppd->iPos = ppd->iLow + 1;
  494. UpdatePosition(ppd, ppd->iLow, TRUE);
  495. }
  496. break;
  497. DoDefault:
  498. default:
  499. return DefWindowProc(hWnd,wMsg,wParam,lParam);
  500. }
  501. return 0;
  502. }