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.

598 lines
18 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. */
  7. /*
  8. *****
  9. ***** caltqr.c
  10. *****
  11. */
  12. #include "cal.h"
  13. /**** FSearchTqr */
  14. BOOL APIENTRY FSearchTqr (TM tm)
  15. {
  16. BYTE *pbPrev;
  17. BYTE *pbCur;
  18. BYTE *pbNext;
  19. BYTE *pbFirst;
  20. BYTE *pbMax;
  21. BOOL fFound;
  22. DR *pdr;
  23. pdr = PdrLockCur ();
  24. votqrPrev = votqrCur = OTQRNIL;
  25. votqrNext = pdr -> cbTqr;
  26. pbMax = (pbCur = pbFirst = PbTqrFromPdr (pdr)) + pdr -> cbTqr;
  27. fFound = FALSE;
  28. while (pbCur < pbMax)
  29. {
  30. pbNext = pbCur + ((PQR )pbCur) -> cb;
  31. if (((PQR )pbCur) -> tm == tm)
  32. {
  33. votqrCur = (WORD)(pbCur - pbFirst);
  34. votqrNext = (WORD)(pbNext - pbFirst);
  35. fFound = TRUE;
  36. break;
  37. }
  38. else if (((PQR )pbCur) -> tm > tm)
  39. {
  40. votqrNext = (WORD)(pbCur - pbFirst);
  41. break;
  42. }
  43. pbPrev = pbCur;
  44. pbCur = pbNext;
  45. }
  46. if (pbCur != pbFirst)
  47. votqrPrev = (WORD)(pbPrev - pbFirst);
  48. DrUnlockCur ();
  49. return (fFound);
  50. }
  51. /**** StoreQd - Move the appointment description from the edit control
  52. into the tqr.
  53. No need to check for room in DR here since we have been monitoring
  54. key strokes and checking for room.
  55. */
  56. VOID APIENTRY StoreQd ()
  57. {
  58. BYTE rgbQrBuf [CBQRHEAD + CCHQDMAX + 1];
  59. CHAR szqdOld [CCHQDMAX + 1];
  60. INT cchqdOld;
  61. INT cchqdNew;
  62. WORD otqr;
  63. BYTE *pb;
  64. BOOL fSame;
  65. register INT ln;
  66. /* Copy into register variable to save code. */
  67. ln = vlnCur;
  68. /* Fetch the text from the edit control. */
  69. cchqdNew = (int)SendMessage (vhwnd3, WM_GETTEXT, CCHQDMAX + 1,
  70. (LPARAM)(rgbQrBuf + CBQRHEAD));
  71. *szqdOld = '\0';
  72. cchqdOld = 0;
  73. pb = PbTqrLock ();
  74. if ((otqr = vtld [ln].otqr) != OTQRNIL)
  75. {
  76. cchqdOld = ((PQR )(pb + otqr)) -> cb - CBQRHEAD - 1;
  77. lstrcpy (szqdOld, (CHAR *)(pb + otqr + CBQRHEAD));
  78. }
  79. fSame = cchqdOld == cchqdNew
  80. && lstrcmp ((LPSTR)szqdOld, (LPSTR)(rgbQrBuf + CBQRHEAD)) == 0;
  81. DrUnlockCur ();
  82. if (fSame)
  83. {
  84. /* This thing hasn't changed, so don't waste alot of time. */
  85. return;
  86. }
  87. /* The QD has changed, so mark the DR and the file dirty. */
  88. PdrLockCur () -> fDirty = vfDirty = TRUE;
  89. DrUnlockCur ();
  90. if (otqr == OTQRNIL)
  91. {
  92. /* There was no previous QR. Say there is no alarm set,
  93. say it's not a special time, and copy in the appointment
  94. time from the tld.
  95. */
  96. ((PQR )rgbQrBuf) -> fAlarm = ((PQR )rgbQrBuf) -> fSpecial = FALSE;
  97. ((PQR )rgbQrBuf) -> tm = vtld [ln].tm;
  98. }
  99. else
  100. {
  101. /* There was a previous QR. Copy it's header information
  102. (we want the flags and the appointment time) into the
  103. new QR we're building.
  104. */
  105. BltByte (PbTqrLock () + otqr, rgbQrBuf, CBQRHEAD);
  106. DrUnlockCur ();
  107. /* Delete the old QR. */
  108. DeleteQr (otqr);
  109. /* In case we don't insert a new qr. */
  110. vtld [ln].otqr = OTQRNIL;
  111. /* Adjust down the otqrs in the tld beyond the current ln. */
  112. AdjustOtqr (ln, -((PQR )rgbQrBuf) -> cb);
  113. }
  114. /* Set the length of the new QR. */
  115. ((PQR )rgbQrBuf) -> cb = (WORD)(CBQRHEAD + cchqdNew + 1);
  116. if (cchqdNew != 0 || ((PQR )rgbQrBuf) -> fAlarm
  117. || ((PQR )rgbQrBuf) -> fSpecial)
  118. {
  119. if (otqr == OTQRNIL)
  120. {
  121. /* There was no previous QR, so search the tqr to find
  122. out where to put the new one. Since we know there
  123. was no old QR, we know FSearchTqr will not find a
  124. match - so we ignore its return value.
  125. */
  126. FSearchTqr (vtld [ln].tm);
  127. otqr = votqrNext;
  128. }
  129. FInsertQr (otqr, (PQR )rgbQrBuf);
  130. vtld [ln].otqr = otqr;
  131. /* Adjust up the otqrs in the tld beyond the current ln. */
  132. AdjustOtqr (ln, ((PQR )rgbQrBuf) -> cb);
  133. }
  134. }
  135. /**** AdjustOtqr */
  136. VOID APIENTRY AdjustOtqr (
  137. INT ln, /* otqr starting with vtld [ln + 1] are adjusted. */
  138. INT cb) /* The amount to adjust the otqr by (can be negative). */
  139. {
  140. while (++ln < vcln)
  141. {
  142. if (vtld [ln].otqr != OTQRNIL)
  143. vtld [ln].otqr += (WORD)cb;
  144. }
  145. }
  146. /**** DeleteQr - delete qr from tqr. */
  147. VOID APIENTRY DeleteQr (WORD otqr)
  148. {
  149. BYTE *pbTqr;
  150. BYTE *pbSrc;
  151. BYTE *pbDst;
  152. WORD cb;
  153. DR *pdr;
  154. pdr = PdrLockCur ();
  155. pbDst = (pbTqr = PbTqrFromPdr (pdr)) + otqr;
  156. pbSrc = pbDst + (cb = ((PQR )pbDst) -> cb);
  157. BltByte (pbSrc, pbDst, (WORD)(pbTqr + pdr -> cbTqr - pbSrc));
  158. pdr -> cbTqr -= cb;
  159. DrUnlockCur ();
  160. }
  161. /**** FInsertQr - insert qr into tqr. */
  162. BOOL APIENTRY FInsertQr (
  163. WORD otqr,
  164. PQR pqr)
  165. {
  166. BYTE *pbTqr;
  167. register BYTE *pbSrc;
  168. BYTE *pbDst;
  169. WORD cb;
  170. register DR *pdr;
  171. BOOL fOk;
  172. /* Make sure there's enough room. */
  173. pdr = PdrLockCur ();
  174. if ((WORD)(fOk = cb = pqr->cb) <= (WORD)(CBTQRMAX - pdr -> cbTqr))
  175. {
  176. /* Zero the unused bits. */
  177. pqr -> reserved = 0;
  178. pbSrc = (pbTqr = PbTqrFromPdr (pdr)) + otqr;
  179. pbDst = pbSrc + cb;
  180. BltByte (pbSrc, pbDst, (WORD)(pbTqr + pdr -> cbTqr - pbSrc));
  181. BltByte ((BYTE *)(pqr), pbSrc, cb);
  182. pdr -> cbTqr += cb;
  183. }
  184. else
  185. {
  186. /* Tell the user the date is full. */
  187. AlertBox (vszDateIsFull, (CHAR *)NULL,
  188. MB_APPLMODAL | MB_OK | MB_ICONEXCLAMATION);
  189. }
  190. DrUnlockCur ();
  191. return (fOk);
  192. }
  193. /**** PbTqrLock - Lock the current DR, returning a pointer to the first
  194. byte of the embedded Tqr.
  195. */
  196. BYTE * APIENTRY PbTqrLock ()
  197. {
  198. register DR *pdr;
  199. pdr = PdrLockCur ();
  200. return (PbTqrFromPdr (pdr));
  201. }
  202. /**** PdrLockCur - Lock the current DR, returning a pointer to it. */
  203. DR * APIENTRY PdrLockCur ()
  204. {
  205. return (PdrLock (vidrCur));
  206. }
  207. /**** DrUnlockCur - Unlock the current DR. */
  208. VOID APIENTRY DrUnlockCur ()
  209. {
  210. DrUnlock (vidrCur);
  211. }
  212. /**** PdrLock - lock and return a pointer to the specified DR. */
  213. DR * APIENTRY PdrLock (UINT idr)
  214. {
  215. return ((DR *)LocalLock (vrghlmDr [idr]));
  216. }
  217. /**** DrUnlock - unlock the specified DR. */
  218. VOID APIENTRY DrUnlock (UINT idr)
  219. {
  220. LocalUnlock (vrghlmDr [idr]);
  221. }
  222. /**** PbTqrFromPdr - Given a pdr, return a pointer to the first byte
  223. of the embedded tqr.
  224. */
  225. BYTE * APIENTRY PbTqrFromPdr (DR*pdr)
  226. {
  227. return ((BYTE *)pdr + CBDRHEAD + pdr -> cbNotes);
  228. }
  229. /**** StoreNotes - store the notes into the DR.
  230. No need to check for room in DR here since we have been monitoring
  231. key strokes and checking for room.
  232. */
  233. VOID APIENTRY StoreNotes ()
  234. {
  235. BYTE rgbEcNotes [CBNOTESMAX];
  236. CHAR *szNew;
  237. CHAR *szOld;
  238. register INT cbNew;
  239. INT cbMore;
  240. register DR *pdr;
  241. BOOL fFormatted;
  242. /* Set up a pointer to the old notes, using the null string if there
  243. were no old notes. Also assume the new notes are empty.
  244. */
  245. szNew = szOld = "";
  246. pdr = PdrLockCur ();
  247. if (pdr -> cbNotes != 0)
  248. szOld = (CHAR *)((BYTE *)pdr + CBDRHEAD);
  249. /* Format the new text. This inserts <CR,CR,LF> where soft line breaks
  250. are. (Hard line breaks are represented by <CR,LF>.)
  251. */
  252. fFormatted = (BOOL)SendMessage (vhwnd2C, EM_FMTLINES, TRUE, 0L);
  253. /* Find out how long the text is (not including the string
  254. terminator).
  255. */
  256. cbNew = (int)SendMessage (vhwnd2C, WM_GETTEXTLENGTH, 0, 0L);
  257. if (cbNew != 0)
  258. {
  259. /* String is not null - will need an extra byte to store the zero
  260. terminator (which is why we ++cbNew here).
  261. */
  262. SendMessage (vhwndFocus, WM_GETTEXT, ++cbNew,
  263. (LPARAM)(szNew = rgbEcNotes));
  264. }
  265. /* If the notes have not changed don't mark the DR dirty or store the
  266. new (unchanged) text. (For speed, don't strcmp if length not same.)
  267. */
  268. if ((cbMore = cbNew - pdr -> cbNotes) != 0 || lstrcmp ((LPSTR)szOld, (LPSTR)szNew) != 0)
  269. {
  270. /* Adjust the hole for the notes if necessary by moving the tqr
  271. up or down. Note that the same length case works OK too.
  272. */
  273. BltByte (PbTqrFromPdr (pdr), PbTqrFromPdr (pdr) + cbMore,
  274. pdr -> cbTqr);
  275. /* Copy in the new string. Note - can't copy to szOld since
  276. it is not in the DR if the old string was null.
  277. */
  278. BltByte ((BYTE *)szNew, (BYTE *)pdr + CBDRHEAD, cbNew);
  279. /* Set the new length. */
  280. pdr -> cbNotes = (WORD)cbNew;
  281. /* Mark the DR and the file dirty. */
  282. pdr -> fDirty = vfDirty = TRUE;
  283. }
  284. DrUnlockCur ();
  285. /* Remove soft line breaks. */
  286. if (fFormatted)
  287. SendMessage (vhwnd2C, EM_FMTLINES, FALSE, 0L);
  288. }
  289. /**** SetNotesEc - Set the text of the notes edit control. */
  290. VOID APIENTRY SetNotesEc ()
  291. {
  292. register CHAR *szNotes;
  293. register DR *pdr;
  294. szNotes = "";
  295. if ((pdr = PdrLockCur ()) -> cbNotes != 0)
  296. szNotes = (CHAR *)((BYTE *)pdr + CBDRHEAD);
  297. SetEcText(vhwnd2C, szNotes);
  298. DrUnlockCur ();
  299. /* Length of text in edit ctl will probably be different than cbNotes
  300. since formatting info has been removed. Set selection to end of text
  301. based on new length. */
  302. }
  303. /**** EcNotification - handle notifications from edit controls.
  304. Note - all of the vcbEcTextMax code assumes that the data in the
  305. DR is up-to-date. This is the case as long as my assumption that
  306. you can't get an EN_SETFOCUS for the same edit control without
  307. having seen an EN_KILLFOCUS in the intervening time.
  308. (The KILLFOCUS will bring the DR up-to-date because that's when
  309. we store the data.)
  310. */
  311. INT vcbEcTextMax = 32767; /* This is the maximum number of bytes we can
  312. allow the text of the edit control to grow to.
  313. This does NOT include the zero terminator,
  314. only the space used by the text itself.
  315. We reserve space for the terminator outside
  316. of this count.
  317. Keep it set very large when not otherwise
  318. set up so an EN_CHANGE (which can come in
  319. from a WM_SETTEXT for example) won't
  320. erroneously trigger a DR full situation.
  321. */
  322. VOID APIENTRY EcNotification (
  323. WORD idec, /* ID of the edit control. */
  324. WORD en) /* The notification code. */
  325. {
  326. register DR *pdr;
  327. WORD otqr;
  328. register BOOL fQd;
  329. if ((fQd = idec == IDECQD) || idec == IDECNOTES)
  330. {
  331. switch (en)
  332. {
  333. case EN_SETFOCUS:
  334. if (fQd)
  335. {
  336. vhwndFocus = vhwnd3;
  337. /* Assuming there is no QR for this appointment time,
  338. we will need room to insert one (CBQRHEAD + 1 (for
  339. the string terminator)). Subtract this from the
  340. available space for the tqr to grow to determine
  341. the maximum
  342. size we can allow for the edit control text.
  343. (The available space is given by CBTQRMAX -
  344. cbTqr.)
  345. */
  346. pdr = PdrLockCur ();
  347. vcbEcTextMax = CBTQRMAX - CBQRHEAD - 1 - pdr -> cbTqr;
  348. if ((otqr = vtld [vlnCur].otqr) != OTQRNIL)
  349. {
  350. /* There is already a QR for this appointment
  351. time, so the space it's occupying will also
  352. be available.
  353. */
  354. vcbEcTextMax +=
  355. ((PQR )(PbTqrFromPdr (pdr) + otqr)) -> cb;
  356. }
  357. DrUnlockCur ();
  358. /* Don't allow vcbEcTextMax to be negative (which
  359. it could be right now if there's no QR for this
  360. appointment and the amount of free space for
  361. the tqr is less than CBQRHEAD + 1). By setting it
  362. to zero we will prevent any characters from
  363. being typed, and the pruning process will work
  364. correctly (which it wouldn't for a negative
  365. value).
  366. */
  367. if (vcbEcTextMax < 0)
  368. vcbEcTextMax = 0;
  369. }
  370. else
  371. {
  372. vhwndFocus = vhwnd2C;
  373. /* The notes edit control is not allowed to grow
  374. beyond CBNOTESTESTMAX bytes.
  375. */
  376. vcbEcTextMax = CBNOTESTEXTMAX;
  377. }
  378. break;
  379. case EN_KILLFOCUS:
  380. /* Put big value in while not in use. */
  381. vcbEcTextMax = 32767;
  382. if (fQd)
  383. {
  384. /* Leaving appointment edit control - store away
  385. the text if it has changed.
  386. */
  387. StoreQd ();
  388. }
  389. else
  390. {
  391. /* Leaving notes edit control - store away the
  392. text if it has changed.
  393. */
  394. StoreNotes ();
  395. }
  396. break;
  397. case EN_ERRSPACE:
  398. AlertBox (vszOutOfMemory, (CHAR *) NULL,
  399. MB_APPLMODAL | MB_OK | MB_ICONEXCLAMATION);
  400. break;
  401. case EN_MAXTEXT:
  402. AlertBox (vszTextTruncated, (CHAR *) NULL,
  403. MB_APPLMODAL | MB_OK | MB_ICONEXCLAMATION);
  404. break;
  405. case EN_CHANGE:
  406. /* If this change is not for the edit control that has
  407. the focus, ignore it. (This happens for example
  408. when we have set the focus to a QD and then set the
  409. text of the notes - as when switching to a new date.)
  410. If we didn't ignore it we would be comparing the
  411. wrong lengths against each other.
  412. Also check out of here if we haven't exceeded the
  413. maximum text length yet.
  414. */
  415. if (vhwndFocus == (fQd ? vhwnd3 : vhwnd2C)
  416. && ((INT)SendMessage (vhwndFocus, WM_GETTEXTLENGTH,0,0L) > vcbEcTextMax))
  417. PruneEcText ();
  418. /* Fall into the return. */
  419. }
  420. }
  421. }
  422. /**** PruneEcText - truncate edit control text since it won't fit in the DR.
  423. This is a separate routine so that the large buffer on the stack
  424. would not be allocated in EcNotification. This is because EcNotification
  425. gets called recursively (because WM_SETTEXT causes an EN_CHANGE) and
  426. it also calls StoreNotes which also uses a large buffer on the stack.
  427. */
  428. VOID APIENTRY PruneEcText ()
  429. {
  430. BYTE rgbEcTextBuf [CBNOTESMAX];
  431. register BYTE *pbCur;
  432. INT cbEcText;
  433. INT cbTemp;
  434. /* That last change made the text too big to fit in
  435. the DR. Truncate the text, and tell the user about it.
  436. */
  437. SendMessage (vhwndFocus, WM_GETTEXT, CBNOTESMAX, (LPARAM)rgbEcTextBuf);
  438. /* Truncate what doesn't fit, being careful not
  439. to truncate in the middle of a multi-byte
  440. character (Kanji).
  441. Note that this loop is guaranteed to execute at
  442. least once (since 0 has to be <= vcbEcTectMax), so
  443. cbEcText will definitely get set.
  444. */
  445. for (pbCur = rgbEcTextBuf;
  446. (cbTemp = (int)(pbCur - rgbEcTextBuf)) <= vcbEcTextMax;
  447. pbCur = (BYTE *)AnsiNext ((LPSTR)pbCur))
  448. cbEcText = cbTemp;
  449. /* cbEcText now contains the count of bytes in the
  450. the characters that would fit (not including the
  451. string terminator). Put in the terminator.
  452. */
  453. rgbEcTextBuf [cbEcText] = '\0';
  454. /* Put the shortened text back into the edit control.
  455. This will put the caret
  456. at the end of the text and redisplay. (Putting
  457. the caret at the end is good since it draws the
  458. user's attention to where the chopping occurred.)
  459. Note that this WM_SETTEXT will cause an EN_CHANGE, but the
  460. text will no longer be too long so we will not end up
  461. recursing into PruneEcText (which we want to avoid due
  462. to the large buffer on the stack).
  463. */
  464. SetEcText(vhwndFocus, rgbEcTextBuf);
  465. /* Tell the user the bad news. */
  466. AlertBox (vszDateIsFull, vszTextTruncated,
  467. MB_APPLMODAL | MB_OK | MB_ICONEXCLAMATION);
  468. }