main.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  1. /*
  2. * Clock
  3. *
  4. * Copyright 1998 Marcel Baur <mbaur@g26.ethz.ch>
  5. *
  6. * Clock is partially based on
  7. * - Program Manager by Ulrich Schmied
  8. * - rolex.c by Jim Peterson
  9. *
  10. *
  11. * This library is free software; you can redistribute it and/or
  12. * modify it under the terms of the GNU Lesser General Public
  13. * License as published by the Free Software Foundation; either
  14. * version 2.1 of the License, or (at your option) any later version.
  15. *
  16. * This library is distributed in the hope that it will be useful,
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  19. * Lesser General Public License for more details.
  20. *
  21. * You should have received a copy of the GNU Lesser General Public
  22. * License along with this library; if not, write to the Free Software
  23. * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  24. */
  25. #include <stdio.h>
  26. #include "windows.h"
  27. #include "commdlg.h"
  28. #include "shellapi.h"
  29. #include "main.h"
  30. #include "winclock.h"
  31. #define INITIAL_WINDOW_SIZE 200
  32. #define TIMER_ID 1
  33. CLOCK_GLOBALS Globals;
  34. static VOID CLOCK_UpdateMenuCheckmarks(VOID)
  35. {
  36. HMENU hPropertiesMenu;
  37. hPropertiesMenu = GetSubMenu(Globals.hMainMenu, 0);
  38. if (!hPropertiesMenu)
  39. return;
  40. if(Globals.bAnalog) {
  41. /* analog clock */
  42. CheckMenuRadioItem(hPropertiesMenu, IDM_ANALOG, IDM_DIGITAL, IDM_ANALOG, MF_CHECKED);
  43. EnableMenuItem(hPropertiesMenu, IDM_FONT, MF_GRAYED);
  44. }
  45. else
  46. {
  47. /* digital clock */
  48. CheckMenuRadioItem(hPropertiesMenu, IDM_ANALOG, IDM_DIGITAL, IDM_DIGITAL, MF_CHECKED);
  49. EnableMenuItem(hPropertiesMenu, IDM_FONT, 0);
  50. }
  51. CheckMenuItem(hPropertiesMenu, IDM_NOTITLE, (Globals.bWithoutTitle ? MF_CHECKED : MF_UNCHECKED));
  52. CheckMenuItem(hPropertiesMenu, IDM_ONTOP, (Globals.bAlwaysOnTop ? MF_CHECKED : MF_UNCHECKED));
  53. CheckMenuItem(hPropertiesMenu, IDM_SECONDS, (Globals.bSeconds ? MF_CHECKED : MF_UNCHECKED));
  54. CheckMenuItem(hPropertiesMenu, IDM_DATE, (Globals.bDate ? MF_CHECKED : MF_UNCHECKED));
  55. }
  56. static VOID CLOCK_UpdateWindowCaption(VOID)
  57. {
  58. WCHAR szCaption[MAX_STRING_LEN];
  59. int chars = 0;
  60. /* Set frame caption */
  61. if (Globals.bDate) {
  62. chars = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_LONGDATE, NULL, NULL,
  63. szCaption, ARRAY_SIZE(szCaption));
  64. if (chars) {
  65. --chars;
  66. szCaption[chars++] = ' ';
  67. szCaption[chars++] = '-';
  68. szCaption[chars++] = ' ';
  69. szCaption[chars] = '\0';
  70. }
  71. }
  72. LoadStringW(0, IDS_CLOCK, szCaption + chars, MAX_STRING_LEN - chars);
  73. SetWindowTextW(Globals.hMainWnd, szCaption);
  74. }
  75. /***********************************************************************
  76. *
  77. * CLOCK_ResetTimer
  78. */
  79. static BOOL CLOCK_ResetTimer(void)
  80. {
  81. UINT period; /* milliseconds */
  82. KillTimer(Globals.hMainWnd, TIMER_ID);
  83. if (Globals.bSeconds)
  84. if (Globals.bAnalog)
  85. period = 50;
  86. else
  87. period = 500;
  88. else
  89. period = 1000;
  90. if (!SetTimer (Globals.hMainWnd, TIMER_ID, period, NULL)) {
  91. static const WCHAR notimersW[] = {'N','o',' ','a','v','a','i','l','a','b','l','e',' ','t','i','m','e','r','s',0};
  92. WCHAR szApp[MAX_STRING_LEN];
  93. LoadStringW(Globals.hInstance, IDS_CLOCK, szApp, MAX_STRING_LEN);
  94. MessageBoxW(0, notimersW, szApp, MB_ICONEXCLAMATION | MB_OK);
  95. return FALSE;
  96. }
  97. return TRUE;
  98. }
  99. /***********************************************************************
  100. *
  101. * CLOCK_ResetFont
  102. */
  103. static VOID CLOCK_ResetFont(VOID)
  104. {
  105. HFONT newfont;
  106. HDC dc = GetDC(Globals.hMainWnd);
  107. newfont = SizeFont(dc, Globals.MaxX, Globals.MaxY, Globals.bSeconds, &Globals.logfont);
  108. if (newfont) {
  109. DeleteObject(Globals.hFont);
  110. Globals.hFont = newfont;
  111. }
  112. ReleaseDC(Globals.hMainWnd, dc);
  113. }
  114. /***********************************************************************
  115. *
  116. * CLOCK_ChooseFont
  117. */
  118. static VOID CLOCK_ChooseFont(VOID)
  119. {
  120. LOGFONTW lf;
  121. CHOOSEFONTW cf;
  122. memset(&cf, 0, sizeof(cf));
  123. lf = Globals.logfont;
  124. cf.lStructSize = sizeof(cf);
  125. cf.hwndOwner = Globals.hMainWnd;
  126. cf.lpLogFont = &lf;
  127. cf.Flags = CF_SCREENFONTS | CF_INITTOLOGFONTSTRUCT | CF_NOVERTFONTS;
  128. if (ChooseFontW(&cf)) {
  129. Globals.logfont = lf;
  130. CLOCK_ResetFont();
  131. }
  132. }
  133. /***********************************************************************
  134. *
  135. * CLOCK_ToggleTitle
  136. */
  137. static VOID CLOCK_ToggleTitle(VOID)
  138. {
  139. /* Also shows/hides the menu */
  140. LONG style = GetWindowLongW(Globals.hMainWnd, GWL_STYLE);
  141. if ((Globals.bWithoutTitle = !Globals.bWithoutTitle)) {
  142. style = (style & ~WS_OVERLAPPEDWINDOW) | WS_POPUP|WS_THICKFRAME;
  143. SetMenu(Globals.hMainWnd, 0);
  144. }
  145. else {
  146. style = (style & ~(WS_POPUP|WS_THICKFRAME)) | WS_OVERLAPPEDWINDOW;
  147. SetMenu(Globals.hMainWnd, Globals.hMainMenu);
  148. SetWindowRgn(Globals.hMainWnd, 0, TRUE);
  149. }
  150. SetWindowLongW(Globals.hMainWnd, GWL_STYLE, style);
  151. SetWindowPos(Globals.hMainWnd, 0,0,0,0,0,
  152. SWP_DRAWFRAME|SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER);
  153. CLOCK_UpdateMenuCheckmarks();
  154. CLOCK_UpdateWindowCaption();
  155. }
  156. /***********************************************************************
  157. *
  158. * CLOCK_ToggleOnTop
  159. */
  160. static VOID CLOCK_ToggleOnTop(VOID)
  161. {
  162. if ((Globals.bAlwaysOnTop = !Globals.bAlwaysOnTop)) {
  163. SetWindowPos(Globals.hMainWnd, HWND_TOPMOST, 0,0,0,0,
  164. SWP_NOMOVE|SWP_NOSIZE);
  165. }
  166. else {
  167. SetWindowPos(Globals.hMainWnd, HWND_NOTOPMOST, 0,0,0,0,
  168. SWP_NOMOVE|SWP_NOSIZE);
  169. }
  170. CLOCK_UpdateMenuCheckmarks();
  171. }
  172. /***********************************************************************
  173. *
  174. * CLOCK_MenuCommand
  175. *
  176. * All handling of main menu events
  177. */
  178. static int CLOCK_MenuCommand (WPARAM wParam)
  179. {
  180. WCHAR szApp[MAX_STRING_LEN];
  181. WCHAR szAppRelease[MAX_STRING_LEN];
  182. switch (wParam) {
  183. /* switch to analog */
  184. case IDM_ANALOG: {
  185. Globals.bAnalog = TRUE;
  186. CLOCK_UpdateMenuCheckmarks();
  187. CLOCK_ResetTimer();
  188. InvalidateRect(Globals.hMainWnd, NULL, FALSE);
  189. break;
  190. }
  191. /* switch to digital */
  192. case IDM_DIGITAL: {
  193. Globals.bAnalog = FALSE;
  194. CLOCK_UpdateMenuCheckmarks();
  195. CLOCK_ResetTimer();
  196. CLOCK_ResetFont();
  197. InvalidateRect(Globals.hMainWnd, NULL, FALSE);
  198. break;
  199. }
  200. /* change font */
  201. case IDM_FONT: {
  202. CLOCK_ChooseFont();
  203. break;
  204. }
  205. /* hide title bar */
  206. case IDM_NOTITLE: {
  207. CLOCK_ToggleTitle();
  208. break;
  209. }
  210. /* always on top */
  211. case IDM_ONTOP: {
  212. CLOCK_ToggleOnTop();
  213. break;
  214. }
  215. /* show or hide seconds */
  216. case IDM_SECONDS: {
  217. Globals.bSeconds = !Globals.bSeconds;
  218. CLOCK_UpdateMenuCheckmarks();
  219. CLOCK_ResetTimer();
  220. if (!Globals.bAnalog)
  221. CLOCK_ResetFont();
  222. InvalidateRect(Globals.hMainWnd, NULL, FALSE);
  223. break;
  224. }
  225. /* show or hide date */
  226. case IDM_DATE: {
  227. Globals.bDate = !Globals.bDate;
  228. CLOCK_UpdateMenuCheckmarks();
  229. CLOCK_UpdateWindowCaption();
  230. break;
  231. }
  232. /* show "about" box */
  233. case IDM_ABOUT: {
  234. LoadStringW(Globals.hInstance, IDS_CLOCK, szApp, ARRAY_SIZE(szApp));
  235. lstrcpyW(szAppRelease,szApp);
  236. ShellAboutW(Globals.hMainWnd, szApp, szAppRelease, 0);
  237. break;
  238. }
  239. }
  240. return 0;
  241. }
  242. /***********************************************************************
  243. *
  244. * CLOCK_Paint
  245. */
  246. static VOID CLOCK_Paint(HWND hWnd)
  247. {
  248. PAINTSTRUCT ps;
  249. HDC dcMem, dc;
  250. HBITMAP bmMem, bmOld;
  251. dc = BeginPaint(hWnd, &ps);
  252. /* Use an offscreen dc to avoid flicker */
  253. dcMem = CreateCompatibleDC(dc);
  254. bmMem = CreateCompatibleBitmap(dc, ps.rcPaint.right - ps.rcPaint.left,
  255. ps.rcPaint.bottom - ps.rcPaint.top);
  256. bmOld = SelectObject(dcMem, bmMem);
  257. SetViewportOrgEx(dcMem, -ps.rcPaint.left, -ps.rcPaint.top, NULL);
  258. /* Erase the background */
  259. FillRect(dcMem, &ps.rcPaint, GetSysColorBrush(COLOR_3DFACE));
  260. if(Globals.bAnalog)
  261. AnalogClock(dcMem, Globals.MaxX, Globals.MaxY, Globals.bSeconds, Globals.bWithoutTitle);
  262. else
  263. DigitalClock(dcMem, Globals.MaxX, Globals.MaxY, Globals.bSeconds, Globals.hFont);
  264. /* Blit the changes to the screen */
  265. BitBlt(dc,
  266. ps.rcPaint.left, ps.rcPaint.top,
  267. ps.rcPaint.right - ps.rcPaint.left, ps.rcPaint.bottom - ps.rcPaint.top,
  268. dcMem,
  269. ps.rcPaint.left, ps.rcPaint.top,
  270. SRCCOPY);
  271. SelectObject(dcMem, bmOld);
  272. DeleteObject(bmMem);
  273. DeleteDC(dcMem);
  274. EndPaint(hWnd, &ps);
  275. }
  276. /***********************************************************************
  277. *
  278. * CLOCK_WndProc
  279. */
  280. static LRESULT WINAPI CLOCK_WndProc (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
  281. {
  282. switch (msg) {
  283. /* L button drag moves the window */
  284. case WM_NCHITTEST: {
  285. LRESULT ret = DefWindowProcW(hWnd, msg, wParam, lParam);
  286. if (ret == HTCLIENT)
  287. ret = HTCAPTION;
  288. return ret;
  289. }
  290. case WM_NCLBUTTONDBLCLK:
  291. case WM_LBUTTONDBLCLK: {
  292. CLOCK_ToggleTitle();
  293. break;
  294. }
  295. case WM_PAINT: {
  296. CLOCK_Paint(hWnd);
  297. break;
  298. }
  299. case WM_SIZE: {
  300. Globals.MaxX = LOWORD(lParam);
  301. Globals.MaxY = HIWORD(lParam);
  302. if (Globals.bAnalog && Globals.bWithoutTitle)
  303. {
  304. RECT rect;
  305. INT diameter = min( Globals.MaxX, Globals.MaxY );
  306. HRGN hrgn = CreateEllipticRgn( (Globals.MaxX - diameter) / 2,
  307. (Globals.MaxY - diameter) / 2,
  308. (Globals.MaxX + diameter) / 2,
  309. (Globals.MaxY + diameter) / 2 );
  310. GetWindowRect( hWnd, &rect );
  311. MapWindowPoints( 0, hWnd, (LPPOINT)&rect, 2 );
  312. OffsetRgn( hrgn, -rect.left, -rect.top );
  313. SetWindowRgn( Globals.hMainWnd, hrgn, TRUE );
  314. }
  315. CLOCK_ResetFont();
  316. break;
  317. }
  318. case WM_COMMAND: {
  319. CLOCK_MenuCommand(wParam);
  320. break;
  321. }
  322. case WM_TIMER: {
  323. /* Could just invalidate what has changed,
  324. * but it doesn't really seem worth the effort
  325. */
  326. InvalidateRect(Globals.hMainWnd, NULL, FALSE);
  327. break;
  328. }
  329. case WM_DESTROY: {
  330. PostQuitMessage (0);
  331. break;
  332. }
  333. default:
  334. return DefWindowProcW(hWnd, msg, wParam, lParam);
  335. }
  336. return 0;
  337. }
  338. /***********************************************************************
  339. *
  340. * WinMain
  341. */
  342. int PASCAL WinMain (HINSTANCE hInstance, HINSTANCE prev, LPSTR cmdline, int show)
  343. {
  344. MSG msg;
  345. WNDCLASSW class;
  346. static const WCHAR szClassName[] = {'C','L','C','l','a','s','s',0};
  347. static const WCHAR szWinName[] = {'C','l','o','c','k',0};
  348. /* Setup Globals */
  349. memset(&Globals.hFont, 0, sizeof (Globals.hFont));
  350. Globals.bAnalog = TRUE;
  351. Globals.bSeconds = TRUE;
  352. if (!prev){
  353. class.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
  354. class.lpfnWndProc = CLOCK_WndProc;
  355. class.cbClsExtra = 0;
  356. class.cbWndExtra = 0;
  357. class.hInstance = hInstance;
  358. class.hIcon = LoadIconW(0, (LPCWSTR)IDI_APPLICATION);
  359. class.hCursor = LoadCursorW(0, (LPCWSTR)IDC_ARROW);
  360. class.hbrBackground = 0;
  361. class.lpszMenuName = 0;
  362. class.lpszClassName = szClassName;
  363. }
  364. if (!RegisterClassW(&class)) return FALSE;
  365. Globals.MaxX = Globals.MaxY = INITIAL_WINDOW_SIZE;
  366. Globals.hMainWnd = CreateWindowW(szClassName, szWinName, WS_OVERLAPPEDWINDOW,
  367. CW_USEDEFAULT, CW_USEDEFAULT,
  368. Globals.MaxX, Globals.MaxY, 0,
  369. 0, hInstance, 0);
  370. if (!CLOCK_ResetTimer())
  371. return FALSE;
  372. Globals.hMainMenu = LoadMenuW(0, MAKEINTRESOURCEW(MAIN_MENU));
  373. SetMenu(Globals.hMainWnd, Globals.hMainMenu);
  374. CLOCK_UpdateMenuCheckmarks();
  375. CLOCK_UpdateWindowCaption();
  376. ShowWindow (Globals.hMainWnd, show);
  377. UpdateWindow (Globals.hMainWnd);
  378. while (GetMessageW(&msg, 0, 0, 0)) {
  379. TranslateMessage(&msg);
  380. DispatchMessageW(&msg);
  381. }
  382. KillTimer(Globals.hMainWnd, TIMER_ID);
  383. DeleteObject(Globals.hFont);
  384. return 0;
  385. }