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.

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