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.

406 lines
14 KiB

  1. /*
  2. * Windows Calendar
  3. * Copyright (c) 1985 by Microsoft Corporation, all rights reserved.
  4. * Written by Mark L. Chamberlin, consultant to Microsoft.
  5. *
  6. ***** calmain.c - small segment containing main loop and caltimer stuff
  7. *
  8. */
  9. #include "cal.h"
  10. /**** WinMain ****/
  11. MMain(hInstance, hPrevInstance, lpszCmdLine, cmdShow)
  12. /* { */
  13. MSG msg;
  14. if (!CalInit (hInstance, hPrevInstance, lpszCmdLine, cmdShow))
  15. return (FALSE);
  16. // OK to process WM_ACTIVATE from now onwards
  17. fInitComplete = TRUE;
  18. while (GetMessage (&msg, NULL, 0, 0))
  19. {
  20. /* Filter the special keys BEFORE calling translate message.
  21. * This way, the WM_KEYDOWN messages will get trapped before
  22. * the WM_CHAR messages are created so we need not worry about
  23. * trapping the latter messages.
  24. */
  25. if (!FKeyFiltered (&msg))
  26. {
  27. if (TranslateAccelerator (vhwnd0, vhAccel, &msg) == 0)
  28. {
  29. TranslateMessage (&msg);
  30. DispatchMessage (&msg);
  31. }
  32. }
  33. }
  34. return (int)msg.wParam;
  35. }
  36. /**** FKeyFiltered - return TRUE if the key has been filtered. ****/
  37. BOOL APIENTRY FKeyFiltered (MSG *pmsg)
  38. {
  39. register WPARAM wParam;
  40. wParam = pmsg -> wParam;
  41. /* Handle TIMER message here so we don't pull in another segment. */
  42. if (pmsg -> message == WM_TIMER)
  43. {
  44. CalTimer(FALSE);
  45. return TRUE;
  46. }
  47. /* Look for key down messages going to the edit controls.
  48. Karl Stock says there is no need to filter out the
  49. key up messages, and we will not call TranslateMessage
  50. for the filtered keys so there will be no WM_CHAR messages
  51. to filter.
  52. */
  53. if (pmsg -> message == WM_KEYDOWN)
  54. {
  55. if (pmsg -> hwnd == vhwnd2C)
  56. {
  57. /* In the notes area. Tab means leave the notes area. */
  58. if (wParam == VK_TAB)
  59. {
  60. /* Leave the notes area. */
  61. if (!vfDayMode)
  62. {
  63. /* Give the focus to the monthly calendar. */
  64. CalSetFocus (vhwnd2B);
  65. }
  66. else
  67. {
  68. /* In day mode - give focus to the appointment
  69. description edit control.
  70. */
  71. CalSetFocus (vhwnd3);
  72. }
  73. return (TRUE);
  74. }
  75. return (FALSE);
  76. }
  77. else if (vfDayMode)
  78. {
  79. switch (wParam)
  80. {
  81. case VK_RETURN:
  82. case VK_DOWN:
  83. /* If on last appointment, scroll up one appoinment.
  84. * If not on last appointment in window, change
  85. * focus to next appointment in window.
  86. */
  87. if (vlnCur == vlnLast)
  88. ScrollUpDay (1, FALSE);
  89. else
  90. SetQdEc (vlnCur + 1);
  91. break;
  92. case VK_UP:
  93. /* If on first appointment in window, scroll down
  94. * one appointment.
  95. * If not on first appointment in window, change
  96. * focus to previous appointment in window.
  97. */
  98. if (vlnCur == 0)
  99. ScrollDownDay (1, FALSE, FALSE);
  100. else
  101. SetQdEc (vlnCur-1);
  102. break;
  103. case VK_NEXT:
  104. case VK_PRIOR:
  105. if (GetKeyState (VK_CONTROL) < 0)
  106. {
  107. /* Control Pg Up and Control Pg Dn are
  108. * the accelerators for Show Previous and
  109. * Show Next. We want TranslateAccelerator
  110. * to see them, so return FALSE.
  111. */
  112. return (FALSE);
  113. }
  114. /* Translate into a scroll command (as if area
  115. * below or above thumb had been clicked).
  116. */
  117. SendMessage(vhwnd2B, WM_VSCROLL,
  118. wParam==VK_NEXT ? SB_PAGEDOWN : SB_PAGEUP, 0L);
  119. break;
  120. case VK_TAB:
  121. /* Switch to the notes area. */
  122. CalSetFocus (vhwnd2C);
  123. break;
  124. default:
  125. return (FALSE);
  126. }
  127. return (TRUE);
  128. }
  129. }
  130. return (FALSE);
  131. }
  132. /**** CalTimer ****/
  133. VOID APIENTRY CalTimer (BOOL fAdjust)
  134. {
  135. HDC hDC;
  136. D3 d3New;
  137. DT dtNew;
  138. TM tmNew;
  139. FT ftPrev;
  140. if (vfFlashing)
  141. FlashWindow (vhwnd0, TRUE);
  142. if (vcAlarmBeeps != 0)
  143. {
  144. MessageBeep (ALARMBEEP);
  145. vcAlarmBeeps--;
  146. }
  147. /* Fetch the date and time. */
  148. ReadClock (&d3New, &tmNew);
  149. /* See if the time or date has changed. Note that it's necessary
  150. * to check all parts in order to immediartely detect all changes.
  151. * (I used to just check the time, but that meant a date change was
  152. * not detected until the minute changed.)
  153. */
  154. if (tmNew != vftCur.tm || d3New.wMonth != vd3Cur.wMonth
  155. || d3New.wDay != vd3Cur.wDay || d3New.wYear != vd3Cur.wYear)
  156. {
  157. /* Remember the old date and time */
  158. ftPrev = vftCur;
  159. vftCur.tm = tmNew;
  160. /* Show new date/time only if not iconic */
  161. if (!IsIconic(vhwnd0))
  162. {
  163. hDC = CalGetDC (vhwnd2A);
  164. DispTime (hDC);
  165. if ((dtNew = DtFromPd3 (&d3New)) != vftCur.dt)
  166. {
  167. vftCur.dt = dtNew;
  168. vd3Cur = d3New;
  169. if (!vfDayMode)
  170. {
  171. /* Display the new date. */
  172. DispDate (hDC, &vd3Cur);
  173. /* If the old or new date is in the
  174. month currently being displayed, redisplay to get rid of
  175. the >< on the old date.
  176. Also, if the new date is in the month being displayed,
  177. it will get marked with the >< as a result.
  178. */
  179. if ((vd3Cur.wMonth == vd3Sel.wMonth && vd3Cur.wYear ==
  180. vd3Sel.wYear) || (d3New.wMonth == vd3Sel.wMonth
  181. && d3New.wYear == vd3Sel.wYear))
  182. {
  183. /* Note - neither vcDaysMonth nor vwDaySticky
  184. has changed, so UpdateMonth will end up selecting
  185. the same day that's currently selected (which
  186. is what we want).
  187. */
  188. vd3To = vd3Sel;
  189. UpdateMonth ();
  190. }
  191. }
  192. }
  193. ReleaseDC (vhwnd2A, hDC);
  194. }
  195. /* If the new date/time is less than the previous one, or the
  196. new one is a day (1440 minutes) or more greater than the
  197. previous one, we want to resynchronize the next alarm.
  198. Obviously, if the date/time is less than the previouse one,
  199. the system clock has been adjusted (except in the case
  200. where it wraps on December 31, 2099, which I am not worried
  201. about). However, it is not obvious when the clock has been
  202. set forward. For example, if the user is running Calendar
  203. and then switches to an old application that grabs the
  204. whole machine (.g., Lotus 123), Calendar will not get
  205. timer messages while the olf app is running. It is completely
  206. reasonable to expect that the user may not return to Windows
  207. for a long time (on the order of hours), so we only assume
  208. the clock has been set forward if it changes by a day or
  209. more (1440 minutes). We don't want to make this period
  210. too great either since if we don't think the clock has been
  211. set ahead, we will put all the alarms that have passed into
  212. the alarm acknowledgement listbox. In fact, avoiding this
  213. was the main reason for detecting clock adjustments. Without
  214. setting the date/time on a machine without a hardware clock,
  215. the date/time would start out back in January, 1980. If he
  216. then noticed the date was wrong and set it, all the alarms
  217. since January 1980 would be put into the listbox, which is
  218. not only rediculous, but could take a long time to read
  219. the disk for a bunch of old dates. With the one day adjustment
  220. period, this is no longer a problem, because we ignore alarms
  221. that go off due to a forward clock adjustment.
  222. Note - do not set vfMustSyncAlarm FALSE in any case since
  223. it may already be TRUE and hasn't been serviced yet
  224. (because uProcessAlarms is locked).
  225. */
  226. /* If there is no NextAlarm present, then we dont have to resync
  227. * any alaram at all;
  228. * Fix for Bug #6196 --SANKAR-- 11-14-89
  229. */
  230. if (fAdjust && CompareFt (&vftCur, &ftPrev) != 0
  231. && vftAlarmNext.dt != DTNIL)
  232. {
  233. /* The clock has been adjusted - set flag to tell
  234. uProcessAlarms we want to resync, and force the
  235. call to AlarmCheck (below) to trigger an alarm
  236. immediately by setting the alarm time to the current
  237. time.
  238. */
  239. vfMustSyncAlarm=TRUE;
  240. vftAlarmNext=vftCur;
  241. }
  242. /* See if it's time to trigger the alarm (also handle
  243. resynchronization).
  244. */
  245. AlarmCheck ();
  246. }
  247. }
  248. /**** AlarmCheck ****/
  249. VOID APIENTRY AlarmCheck ()
  250. {
  251. FT ftTemp;
  252. /* If the current time plus the early ring period is greater than or
  253. equal to the next alarm time, trigger the alarm.
  254. */
  255. ftTemp = vftCur;
  256. AddMinsToFt (&ftTemp, vcMinEarlyRing);
  257. if (CompareFt (&ftTemp, &vftAlarmNext) > -1)
  258. {
  259. /* Sound the alarm if sound is enabled. Give the first beep
  260. right now and the rest at one second intervals in the timer
  261. message routine.
  262. */
  263. if (vfSound)
  264. {
  265. MessageBeep (ALARMBEEP);
  266. vcAlarmBeeps = CALARMBEEPS - 1;
  267. }
  268. if (vftAlarmFirst.dt == DTNIL)
  269. {
  270. /* This is the first unacknowledged alarm - remember it. */
  271. vftAlarmFirst = vftAlarmNext;
  272. if (GetActiveWindow () == vhwnd0)
  273. {
  274. /* We are the active window, so process the alarm now. */
  275. uProcessAlarms ();
  276. return;
  277. }
  278. /* Not the active window, so fall through. */
  279. }
  280. /* Let the user know there are unacknowledged alarms. */
  281. StartStopFlash (TRUE);
  282. /* The next alarm is the first one > the one that just went off.
  283. GetNextAlarm looks for >=, so add one minute.
  284. Do not go to the disk - only arm the next alarm if it's in
  285. memory. Note that this is absolutely necessary in the case
  286. where we don't have the focus (the user is doing something
  287. else, so it would be rude to start spinning the the disk and
  288. possibly asking for the correct floppy to be inserted). In
  289. the case where we are active but the alarm acknowledgement
  290. dialog is already up, it would actually be OK to go to the disk,
  291. but I have decided it would be too confusing for the user if
  292. a disk I/O error were to occur at this point.
  293. */
  294. ftTemp = vftAlarmNext;
  295. AddMinsToFt (&ftTemp, 1);
  296. GetNextAlarm (&vftAlarmNext, &ftTemp, FALSE, (HWND)NULL);
  297. }
  298. }
  299. /**** AddMinsToFt ****/
  300. VOID APIENTRY AddMinsToFt (
  301. FT *pft,
  302. UINT cMin) /* Not to exceed the minutes in one day (1440). */
  303. {
  304. /* Add cMin to the time. Note that the highest legitimate
  305. TM and the largest cMin cannot overflow a WORD, which
  306. is what a TM is, so we needn't worry about overflow here.
  307. */
  308. if ((pft -> tm += (TM)cMin) > TMLAST)
  309. {
  310. /* The time wrapped into the next day. Adjust down the time,
  311. and increment the day. If the date goes beyond DTLAST (that
  312. of December 31, 2099, the value should still be OK for the
  313. caller since it will only be used for comparison purposes.
  314. Anyway, I will be dead when that case comes up, so if it doesn't
  315. work correctly, it won't be my problem.
  316. */
  317. pft -> tm -= TMLAST + 1;
  318. pft -> dt++;
  319. }
  320. }
  321. /**** CompareFt - compare the two FTs returning:
  322. -1 iff ft1 < ft2
  323. 0 iff ft1 = ft2
  324. +1 iff ft1 > ft2
  325. ****/
  326. INT APIENTRY CompareFt (
  327. FT *pft1,
  328. FT *pft2)
  329. {
  330. register FT *pft1Temp;
  331. register FT *pft2Temp;
  332. if ((pft1Temp = pft1) -> dt < (pft2Temp = pft2) -> dt)
  333. return (-1);
  334. if (pft1Temp -> dt > pft2Temp -> dt)
  335. return (1);
  336. /* DTs are equal, compare the TMs. */
  337. if (pft1Temp -> tm < pft2Temp -> tm)
  338. return (-1);
  339. if (pft1Temp -> tm > pft2Temp -> tm)
  340. return (1);
  341. return (0);
  342. }