/Samples/Chap09/OwnDraw/OwnDraw.d

http://github.com/AndrejMitrovic/DWinProgramming · D · 292 lines · 224 code · 54 blank · 14 comment · 7 complexity · 5001c5d0afdd32afc47b184b8743cc4f MD5 · raw file

  1. /+
  2. + Copyright (c) Charles Petzold, 1998.
  3. + Ported to the D Programming Language by Andrej Mitrovic, 2011.
  4. +/
  5. module OwnDraw;
  6. import core.runtime;
  7. import core.thread;
  8. import std.conv;
  9. import std.math;
  10. import std.range;
  11. import std.string;
  12. import std.utf;
  13. auto toUTF16z(S)(S s)
  14. {
  15. return toUTFz!(const(wchar)*)(s);
  16. }
  17. pragma(lib, "gdi32.lib");
  18. import core.sys.windows.windef;
  19. import core.sys.windows.winuser;
  20. import core.sys.windows.wingdi;
  21. import core.sys.windows.winbase;
  22. enum ID_TIMER = 1;
  23. enum TWOPI =(2 * PI);
  24. enum ID_SMALLER = 1; // button window unique id
  25. enum ID_LARGER = 2; // same
  26. enum BTN_WIDTH = "(8 * cxChar)";
  27. enum BTN_HEIGHT = "(4 * cyChar)";
  28. struct Button
  29. {
  30. int iStyle;
  31. immutable(char) * szText;
  32. }
  33. Button[] buttons =
  34. [
  35. Button(BS_PUSHBUTTON, "PUSHBUTTON" ),
  36. Button(BS_DEFPUSHBUTTON, "DEFPUSHBUTTON"),
  37. Button(BS_CHECKBOX, "CHECKBOX" ),
  38. Button(BS_AUTOCHECKBOX, "AUTOCHECKBOX" ),
  39. Button(BS_RADIOBUTTON, "RADIOBUTTON" ),
  40. Button(BS_3STATE, "3STATE" ),
  41. Button(BS_AUTO3STATE, "AUTO3STATE" ),
  42. Button(BS_GROUPBOX, "GROUPBOX" ),
  43. Button(BS_AUTORADIOBUTTON, "AUTORADIO" ),
  44. Button(BS_OWNERDRAW, "OWNERDRAW" ),
  45. ];
  46. HINSTANCE hInst;
  47. extern (Windows)
  48. int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int iCmdShow)
  49. {
  50. int result;
  51. try
  52. {
  53. Runtime.initialize();
  54. result = myWinMain(hInstance, hPrevInstance, lpCmdLine, iCmdShow);
  55. Runtime.terminate();
  56. }
  57. catch (Throwable o)
  58. {
  59. MessageBox(null, o.toString().toUTF16z, "Error", MB_OK | MB_ICONEXCLAMATION);
  60. result = 0;
  61. }
  62. return result;
  63. }
  64. int myWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int iCmdShow)
  65. {
  66. string appName = "OwnDraw";
  67. HWND hwnd;
  68. MSG msg;
  69. WNDCLASS wndclass;
  70. wndclass.style = CS_HREDRAW | CS_VREDRAW;
  71. wndclass.lpfnWndProc = &WndProc;
  72. wndclass.cbClsExtra = 0;
  73. wndclass.cbWndExtra = 0;
  74. wndclass.hInstance = hInstance;
  75. wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
  76. wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
  77. wndclass.hbrBackground = cast(HBRUSH) GetStockObject(WHITE_BRUSH);
  78. wndclass.lpszMenuName = NULL;
  79. wndclass.lpszClassName = appName.toUTF16z;
  80. if (!RegisterClass(&wndclass))
  81. {
  82. MessageBox(NULL, "This program requires Windows NT!", appName.toUTF16z, MB_ICONERROR);
  83. return 0;
  84. }
  85. hwnd = CreateWindow(appName.toUTF16z, // window class name
  86. "Owner-Draw Button Demo", // window caption
  87. WS_OVERLAPPEDWINDOW, // window style
  88. CW_USEDEFAULT, // initial x position
  89. CW_USEDEFAULT, // initial y position
  90. CW_USEDEFAULT, // initial x size
  91. CW_USEDEFAULT, // initial y size
  92. NULL, // parent window handle
  93. NULL, // window menu handle
  94. hInstance, // program instance handle
  95. NULL); // creation parameters
  96. ShowWindow(hwnd, iCmdShow);
  97. UpdateWindow(hwnd);
  98. while (GetMessage(&msg, NULL, 0, 0))
  99. {
  100. TranslateMessage(&msg);
  101. DispatchMessage(&msg);
  102. }
  103. return msg.wParam;
  104. }
  105. void Triangle(HDC hdc, POINT[] pt)
  106. {
  107. SelectObject(hdc, GetStockObject(BLACK_BRUSH));
  108. Polygon(hdc, pt.ptr, 3);
  109. SelectObject(hdc, GetStockObject(WHITE_BRUSH));
  110. }
  111. extern(Windows)
  112. LRESULT WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) nothrow
  113. {
  114. scope (failure) assert(0);
  115. static HWND hwndSmaller, hwndLarger; // button window handles
  116. static int cxClient, cyClient, cxChar, cyChar;
  117. int cx, cy;
  118. LPDRAWITEMSTRUCT pdis;
  119. POINT[3] pt;
  120. RECT rc;
  121. switch (message)
  122. {
  123. case WM_CREATE:
  124. cxChar = LOWORD(GetDialogBaseUnits());
  125. cyChar = HIWORD(GetDialogBaseUnits());
  126. // Create the owner-draw pushbuttons
  127. hwndSmaller = CreateWindow("button", "",
  128. WS_CHILD | WS_VISIBLE | BS_OWNERDRAW,
  129. 0, 0, mixin(BTN_WIDTH), mixin(BTN_HEIGHT),
  130. hwnd, cast(HMENU)ID_SMALLER, hInst, NULL);
  131. hwndLarger = CreateWindow("button", "",
  132. WS_CHILD | WS_VISIBLE | BS_OWNERDRAW,
  133. 0, 0, mixin(BTN_WIDTH), mixin(BTN_HEIGHT),
  134. hwnd, cast(HMENU)ID_LARGER, hInst, NULL);
  135. return 0;
  136. case WM_SIZE:
  137. cxClient = LOWORD(lParam);
  138. cyClient = HIWORD(lParam);
  139. // Move the buttons to the new center
  140. MoveWindow(hwndSmaller, cxClient / 2 - 3 * mixin(BTN_WIDTH) / 2,
  141. cyClient / 2 - mixin(BTN_HEIGHT) / 2,
  142. mixin(BTN_WIDTH), mixin(BTN_HEIGHT), TRUE);
  143. MoveWindow(hwndLarger, cxClient / 2 + mixin(BTN_WIDTH) / 2,
  144. cyClient / 2 - mixin(BTN_HEIGHT) / 2,
  145. mixin(BTN_WIDTH), mixin(BTN_HEIGHT), TRUE);
  146. return 0;
  147. case WM_COMMAND:
  148. GetWindowRect(hwnd, &rc);
  149. // Make the window 10% smaller or larger
  150. switch (wParam)
  151. {
  152. case ID_SMALLER:
  153. rc.left += cxClient / 20;
  154. rc.right -= cxClient / 20;
  155. rc.top += cyClient / 20;
  156. rc.bottom -= cyClient / 20;
  157. break;
  158. case ID_LARGER:
  159. rc.left -= cxClient / 20;
  160. rc.right += cxClient / 20;
  161. rc.top -= cyClient / 20;
  162. rc.bottom += cyClient / 20;
  163. break;
  164. default:
  165. }
  166. // resize window
  167. MoveWindow(hwnd, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, TRUE);
  168. return 0;
  169. case WM_DRAWITEM: // called when a button window wants to be drawn
  170. // See http://msdn.microsoft.com/en-us/library/bb775802%28v=vs.85%29.aspx,
  171. // for DRAWITEMSTRUCT definition
  172. pdis = cast(LPDRAWITEMSTRUCT)lParam;
  173. // Fill area with white and frame it black
  174. FillRect(pdis.hDC, &pdis.rcItem, cast(HBRUSH)GetStockObject(WHITE_BRUSH));
  175. FrameRect(pdis.hDC, &pdis.rcItem, cast(HBRUSH)GetStockObject(BLACK_BRUSH));
  176. // Draw inward and outward black triangles
  177. cx = pdis.rcItem.right - pdis.rcItem.left;
  178. cy = pdis.rcItem.bottom - pdis.rcItem.top;
  179. switch (pdis.CtlID)
  180. {
  181. case ID_SMALLER:
  182. pt[0].x = 3 * cx / 8; pt[0].y = 1 * cy / 8;
  183. pt[1].x = 5 * cx / 8; pt[1].y = 1 * cy / 8;
  184. pt[2].x = 4 * cx / 8; pt[2].y = 3 * cy / 8;
  185. Triangle(pdis.hDC, pt);
  186. pt[0].x = 7 * cx / 8; pt[0].y = 3 * cy / 8;
  187. pt[1].x = 7 * cx / 8; pt[1].y = 5 * cy / 8;
  188. pt[2].x = 5 * cx / 8; pt[2].y = 4 * cy / 8;
  189. Triangle(pdis.hDC, pt);
  190. pt[0].x = 5 * cx / 8; pt[0].y = 7 * cy / 8;
  191. pt[1].x = 3 * cx / 8; pt[1].y = 7 * cy / 8;
  192. pt[2].x = 4 * cx / 8; pt[2].y = 5 * cy / 8;
  193. Triangle(pdis.hDC, pt);
  194. pt[0].x = 1 * cx / 8; pt[0].y = 5 * cy / 8;
  195. pt[1].x = 1 * cx / 8; pt[1].y = 3 * cy / 8;
  196. pt[2].x = 3 * cx / 8; pt[2].y = 4 * cy / 8;
  197. Triangle(pdis.hDC, pt);
  198. break;
  199. case ID_LARGER:
  200. pt[0].x = 5 * cx / 8; pt[0].y = 3 * cy / 8;
  201. pt[1].x = 3 * cx / 8; pt[1].y = 3 * cy / 8;
  202. pt[2].x = 4 * cx / 8; pt[2].y = 1 * cy / 8;
  203. Triangle(pdis.hDC, pt);
  204. pt[0].x = 5 * cx / 8; pt[0].y = 5 * cy / 8;
  205. pt[1].x = 5 * cx / 8; pt[1].y = 3 * cy / 8;
  206. pt[2].x = 7 * cx / 8; pt[2].y = 4 * cy / 8;
  207. Triangle(pdis.hDC, pt);
  208. pt[0].x = 3 * cx / 8; pt[0].y = 5 * cy / 8;
  209. pt[1].x = 5 * cx / 8; pt[1].y = 5 * cy / 8;
  210. pt[2].x = 4 * cx / 8; pt[2].y = 7 * cy / 8;
  211. Triangle(pdis.hDC, pt);
  212. pt[0].x = 3 * cx / 8; pt[0].y = 3 * cy / 8;
  213. pt[1].x = 3 * cx / 8; pt[1].y = 5 * cy / 8;
  214. pt[2].x = 1 * cx / 8; pt[2].y = 4 * cy / 8;
  215. Triangle(pdis.hDC, pt);
  216. break;
  217. default:
  218. }
  219. // Invert the rectangle if the button is selected
  220. if (pdis.itemState & ODS_SELECTED)
  221. InvertRect(pdis.hDC, &pdis.rcItem);
  222. // Draw a focus rectangle if the button has the focus
  223. if (pdis.itemState & ODS_FOCUS)
  224. {
  225. pdis.rcItem.left += cx / 16;
  226. pdis.rcItem.top += cy / 16;
  227. pdis.rcItem.right -= cx / 16;
  228. pdis.rcItem.bottom -= cy / 16;
  229. DrawFocusRect(pdis.hDC, &pdis.rcItem);
  230. }
  231. return 0;
  232. case WM_DESTROY:
  233. PostQuitMessage(0);
  234. return 0;
  235. default:
  236. }
  237. return DefWindowProc(hwnd, message, wParam, lParam);
  238. }