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.

764 lines
20 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. * cal.c
  7. *
  8. */
  9. #include "cal.h"
  10. BOOL FAR APIENTRY IsDefaultPrinterStillValid(LPSTR);
  11. /**** FCalSize ****/
  12. BOOL APIENTRY FCalSize (
  13. HWND hwnd,
  14. INT x,
  15. INT y,
  16. INT code)
  17. {
  18. INT cy, ytop, cx, xleft,
  19. cyUseable, vclnOld, dln = 0,
  20. tmp, dx, dy;
  21. if (hwnd==vhwnd0)
  22. {
  23. /* Store appointment currently being edited so it gets repainted
  24. * by DayPaint.
  25. */
  26. if (GetFocus () == vhwnd3)
  27. {
  28. StoreQd ();
  29. }
  30. switch (code)
  31. {
  32. case SIZEFULLSCREEN:
  33. case SIZENORMAL:
  34. MoveWindow(vhwnd1, xleft=XcoWnd1(),
  35. ytop=YcoWnd1(), vcxWnd1,
  36. vcyWnd1, TRUE);
  37. cy = vcyWnd2B;
  38. if (cy > y-ytop-vcyWnd2A)
  39. cy=y-ytop-vcyWnd2A;
  40. cx = vcxWnd1 - vcxBorder;
  41. if (cx > x-xleft)
  42. cx=x-xleft;
  43. MoveWindow(vhwnd2B, 0, vcyWnd2A, cx, cy, FALSE);
  44. /* Reset global variables according to new window size. */
  45. cyUseable = cy - 2 * vcyBorder - vcyExtLead ;
  46. vclnOld = vcln;
  47. vcln = cyUseable/vcyLineToLine;
  48. /* Always display at least one line. In addition to
  49. avoiding div by 0 errors, this shows user that there
  50. is something that is "trying" to be displayed even if
  51. there is not enough space. */
  52. //- FCalSize: Fixed to handle vcln < 0.
  53. if (vcln <= 0)
  54. vcln = 1;
  55. vlnLast = vcln-1;
  56. /* If we're in day mode, reset the scroll range so user
  57. can scroll 11:00 pm to bottom of window. (If in month
  58. mode, smallest unit of scrolling is one month.) */
  59. /* foll. lines set up vertical scroll globals for month view
  60. Set vmScrollMax so that if less than a certain fraction of
  61. the bottom line is visible, scrolling should be possible.
  62. This has been determined by trial and error
  63. */
  64. dy = (vcyWnd2BBot - vcyBorder)/ vcWeeksMonth;
  65. tmp = y/dy;
  66. if ((y%dy) < (5*dy/6))
  67. tmp--;
  68. vmScrollMax =max ( 0, vcWeeksMonth + 1 - tmp);
  69. vmScrollPos = 0;
  70. /* foll lines set up horizontal scroll globals for month view
  71. Set hmScrollMax so that if less than a certain fraction of
  72. the rightmost column is visible, scrolling should be possible.
  73. This has been determined by trial and error */
  74. dx = (vcxWnd2B + vcxBorder)/7;
  75. tmp = x/dx;
  76. if ((x%dx) < (5*dx/6))
  77. tmp-- ;
  78. hmScrollMax = max (0, 6 - tmp);
  79. hmScrollPos = 0;
  80. if (vfDayMode)
  81. {
  82. SetDayScrollRange();
  83. /* If our resizing made window bigger, pick up extra
  84. ld records that need to be put in tld. */
  85. if ((dln = vcln - vclnOld) > 0)
  86. {
  87. while (dln && FGetNextLd(vtld[vlnLast-dln].tm, &vtld[vlnLast-dln+1]))
  88. dln--;
  89. /* If there is going to be extra space at bottom
  90. of window, scroll to fill that space. */
  91. ScrollDownDay(dln, TRUE, TRUE);
  92. }
  93. }
  94. else
  95. {
  96. /* set up horiz. and vertical scroll bars in monthmode */
  97. SetScrollPos (vhwnd2B, SB_VERT, vmScrollPos, TRUE);
  98. SetScrollRange (vhwnd2B, SB_VERT, 0, vmScrollMax,TRUE);
  99. SetScrollPos (vhwnd2B, SB_HORZ, hmScrollPos, TRUE);
  100. SetScrollRange (vhwnd2B, SB_HORZ, 0, hmScrollMax, TRUE);
  101. }
  102. return (TRUE);
  103. }
  104. }
  105. return (FALSE);
  106. }
  107. /**** CalWndProc ****/
  108. LRESULT APIENTRY CalWndProc (
  109. HWND hwnd,
  110. UINT message,
  111. WPARAM wParam,
  112. LPARAM lParam)
  113. {
  114. PAINTSTRUCT ps;
  115. register BOOL fActed; /* TRUE if we acted on the message. */
  116. HCURSOR hcsr;
  117. register WORD wlParamHi;
  118. INT lnT;
  119. fActed=TRUE;
  120. wlParamHi=HIWORD(lParam);
  121. switch(message)
  122. {
  123. case WM_CLOSE:
  124. if (FCheckSave (FALSE))
  125. DestroyWindow (vhwnd0);
  126. break;
  127. case WM_QUERYENDSESSION:
  128. return (FCheckSave (TRUE));
  129. case WM_DESTROY:
  130. if (hwnd == vhwnd0)
  131. {
  132. /* Time to say goodbye - only field this message
  133. for our main window.
  134. */
  135. /* Get rid of the change file - ignore errors since
  136. there is nothing to be done about it now and the
  137. the user has either said to discard the changes
  138. or they have already been saved.
  139. */
  140. DeleteChangeFile ();
  141. WinHelp(hwnd, vszHelpFile, HELP_QUIT, 0L);
  142. /* Free all global objects */
  143. CalTerminate(2);
  144. /* Terminate with exit code 0, meaning no errors. */
  145. PostQuitMessage (0);
  146. }
  147. else
  148. fActed = FALSE;
  149. break;
  150. case WM_ENDSESSION:
  151. /* If wParam is TRUE, we are never coming back again. */
  152. if (wParam)
  153. DeleteChangeFile ();
  154. /* Get rid of the change file - ignore errors since
  155. there is nothing to be done about it now and the
  156. the user has either said to discard the changes
  157. or they have already been saved.
  158. */
  159. break;
  160. case WM_SIZE:
  161. fActed=FCalSize(hwnd, (SHORT)LOWORD(lParam),
  162. (SHORT)HIWORD(lParam), (int)wParam);
  163. break;
  164. case WM_PAINT:
  165. /* Hiding the caret of the appointment description edit
  166. control before painting wnd2B in day mode is necessary
  167. to prevent leaving cursor droppings around. So hide
  168. the caret here, and show it after painting. Note that
  169. this is OK even if we're not painting wnd2B, so no
  170. extra code is used here to only do it for the wnd2B
  171. case.
  172. */
  173. HideCaret (vhwnd3);
  174. BeginPaint (hwnd, &ps);
  175. SetDefaultColors (ps.hdc);
  176. CalPaint (hwnd, ps.hdc);
  177. EndPaint (hwnd, &ps);
  178. ShowCaret (vhwnd3);
  179. break;
  180. case WM_COMMAND:
  181. /* HIWORD (lParam) == 0 means a command has been selected
  182. via the menu. 1 means a command has been selected
  183. via an accelerator. Something other than 0 or 1 is
  184. the window handle of a control sending a notification.
  185. */
  186. if (GET_WM_COMMAND_CMD (wParam, lParam) <= 1)
  187. {
  188. /* A menu item has been selected. */
  189. CalCommand (hwnd, GET_WM_MENUSELECT_CMD (wParam, lParam));
  190. }
  191. else
  192. {
  193. /* Handle notifications from edit controls. */
  194. //- EcNotification (wParam, GET_WM_COMMAND_ID(wParam, lParam));
  195. EcNotification ((WORD)wParam, (WORD)HIWORD (wParam));
  196. /* Even if we handled the message, say we didn't
  197. in case something else needs to be done with it.
  198. */
  199. fActed = FALSE;
  200. }
  201. break;
  202. case WM_SYSCOMMAND:
  203. fActed = FALSE;
  204. break;
  205. case WM_TIMER:
  206. CalTimer (FALSE);
  207. break;
  208. case WM_TIMECHANGE:
  209. CalTimer(TRUE);
  210. break;
  211. case WM_VSCROLL:
  212. if (IsWindowEnabled(vhwnd0))
  213. {
  214. if (fActed == vfDayMode)
  215. {
  216. FScrollDay (GET_WM_VSCROLL_CODE(wParam, lParam),
  217. GET_WM_VSCROLL_POS(wParam, lParam));
  218. }
  219. else
  220. {
  221. FScrollMonth (GET_WM_VSCROLL_CODE(wParam, lParam),
  222. GET_WM_VSCROLL_POS(wParam, lParam));
  223. }
  224. }
  225. break;
  226. case WM_HSCROLL: /* added 11/3/88 for horiz. scroll in month view */
  227. /* We have replaced the bitmap arrows with a scrollbar control; So, the
  228. * following code handles the scroll messages from it
  229. * Fix for Bug #8560 -- SANKAR -- 01-28-90
  230. */
  231. #ifndef BUG_8560
  232. /* Check if this is the Horizontal Scroll bar control */
  233. if(GET_WM_HSCROLL_HWND(wParam, lParam) == vhScrollWnd)
  234. {
  235. switch(GET_WM_HSCROLL_CODE(wParam, lParam))
  236. {
  237. case SB_LINEUP:
  238. CalCommand (vhwnd0, IDCM_PREVIOUS);
  239. break;
  240. case SB_LINEDOWN:
  241. CalCommand (vhwnd0, IDCM_NEXT);
  242. break;
  243. }
  244. break;
  245. }
  246. #endif
  247. if (IsWindowEnabled(vhwnd0))
  248. {
  249. if (fActed != vfDayMode)
  250. FHorizScrollMonth (GET_WM_HSCROLL_CODE(wParam, lParam),
  251. GET_WM_HSCROLL_POS(wParam, lParam));
  252. }
  253. case WM_MOUSEMOVE:
  254. /* The mouse cursor is an arrow everywhere except when
  255. in an appointment description or in the notes area, when
  256. it must be the Ibeam. There are two reasons we can't just
  257. let the edit controls take care of this:
  258. 1) The appointment description edit control only covers
  259. one appointment description, and we want the Ibeam
  260. to appear on all the descriptions.
  261. 2) The notes edit control does not use up the entire
  262. bottom box of the calendar, and we want the Ibeam in the
  263. entire box. Note that we make the Ibeam start if the
  264. cursor is below the line we drew at vycoNotesBox.
  265. */
  266. hcsr = vhcsrArrow;
  267. if (hwnd == vhwnd2B && vfDayMode
  268. && (INT)LOWORD (lParam) >= vxcoQdFirst
  269. || hwnd == vhwnd1 && (INT)HIWORD (lParam) > vycoNotesBox)
  270. hcsr = vhcsrIbeam;
  271. SetCursor (hcsr);
  272. break;
  273. case WM_LBUTTONDBLCLK:
  274. case WM_LBUTTONDOWN:
  275. if (hwnd == vhwnd1 && (INT)HIWORD (lParam) > vycoNotesBox)
  276. {
  277. /* Click in the bottom box (below the line at vycoNotesBox)
  278. but not in the notes edit control. Pass the click
  279. to the notes edit control.
  280. The mouse coordinates we were passed are
  281. relative to the origin of wnd1. Make
  282. them relative to the origin of the notes
  283. edit control.
  284. */
  285. ((POINTS*)&lParam)->x -= (short)vxcoWnd2C;
  286. ((POINTS*)&lParam)->y -= (short)vycoWnd2C;
  287. PostMessage (vhwnd2C, message, wParam, lParam);
  288. break;
  289. }
  290. if (hwnd == vhwnd2A )
  291. {
  292. /* Double clicking in the date field is the same as
  293. using the View Month command (i.e., switch to
  294. month mode).
  295. */
  296. if (message == WM_LBUTTONDBLCLK &&
  297. (INT)LOWORD (lParam) >= vxcoDate)
  298. {
  299. if (vfDayMode)
  300. CalCommand (vhwnd0, IDCM_MONTH);
  301. else
  302. /* Switch back to today */
  303. DayMode (&vd3Sel);
  304. }
  305. break;
  306. }
  307. if (hwnd == vhwnd2B)
  308. {
  309. if (vfDayMode)
  310. {
  311. /* If we just clicked on a new line, "move" edit ctl
  312. window to new line. Otherwise, just pass mouse
  313. message to edit ctl. */
  314. if ((lnT = LnFromYco (wlParamHi)) != vlnCur) {
  315. /* Suppose that the appointment window is not clean.
  316. In particular, we are concerned about the
  317. rectangle that we are about to put the appointment
  318. description edit control on top of. SetQdEc
  319. validates the edit control after moving it to
  320. prevent it from repainting, and the ValidateRect
  321. call in turn validates that portion of the parent.
  322. This means that if it was dirty before calling
  323. SetQdEc, it won't get repainted by DayPaint, which
  324. it should. In order to get around this problem,
  325. make sure everything is clean before calling
  326. SetQdEc. An example of where this was a problem:
  327. Zoom and immediately click on a new appointment.
  328. The click was seen before all painting has been
  329. done, so a hole was left where the edit control
  330. was moved.
  331. Force everthing to be clean by calling
  332. UpdateWindow for our main window (using
  333. wnd2B caused out-of-sequence painting and
  334. really left a mess on the screen).
  335. */
  336. UpdateWindow (vhwnd0);
  337. SetQdEc (lnT);
  338. }
  339. /* Let the edit control see the click too.
  340. The mouse coordinates we were passed are
  341. relative to the origin of wnd2B. Make
  342. them relative to the origin of the QD
  343. edit control.
  344. */
  345. ((POINTS*)&lParam)->x -= (short)vxcoQdFirst;
  346. ((POINTS*)&lParam)->y -= (short)YcoFromLn(vlnCur);
  347. PostMessage (vhwnd3, message, wParam, lParam);
  348. }
  349. else
  350. {
  351. /* Note - if the mouse position is not on a box for a
  352. valid day of the month, the click is ignored.
  353. However, we still leave fActed == TRUE since the
  354. mouse click has been acted on by us in the sense
  355. that we do not expect Windows to do anything
  356. further with it.
  357. */
  358. MouseSelectDay(MAKEMPOINT(lParam),
  359. message == WM_LBUTTONDBLCLK);
  360. }
  361. }
  362. else
  363. {
  364. fActed = FALSE;
  365. }
  366. break;
  367. case WM_KEYDOWN:
  368. fActed = FCalKey (hwnd, wParam);
  369. break;
  370. case WM_ACTIVATE:
  371. if (!fInitComplete)
  372. {
  373. fActed = FALSE;
  374. break;
  375. }
  376. if (GET_WM_ACTIVATE_STATE(wParam, lParam))
  377. {
  378. /* Becoming active. */
  379. /* If not iconic, give the focus to the last one who
  380. had it.
  381. */
  382. if (GET_WM_ACTIVATE_FMINIMIZED(wParam, lParam))
  383. CalSetFocus (vhwndFocus);
  384. /* Tell the user about any alarms that went off while
  385. we were inactive.
  386. */
  387. PostMessage(hwnd, CM_PROCALARMS, 0, 0L);
  388. }
  389. else
  390. {
  391. /* Becoming inactive - pass this off to DefWindowProc. */
  392. fActed = FALSE;
  393. }
  394. break;
  395. case CM_PROCALARMS:
  396. uProcessAlarms ();
  397. break;
  398. case WM_SETFOCUS:
  399. /* If the monthly calendar is getting the focus, create,
  400. position, and show its caret. Otherwise, do not process
  401. this message.
  402. */
  403. if (hwnd == vhwnd2B && !vfDayMode)
  404. {
  405. /* Create a caret for month mode. Specifying NULL for the
  406. second parameter gives a black caret. The third
  407. parameter is the width, and by making the fourth
  408. parameter 0, we get a height of a horizontal border
  409. (same as vcyBorder).
  410. */
  411. CreateCaret (vhwnd2B, (HBITMAP)NULL, 2 * vcxFont, 0);
  412. /* Position the caret to the selected day. */
  413. PositionCaret ();
  414. /* Make the caret visible. */
  415. ShowCaret (vhwnd2B);
  416. /* Remember we last had the focus so we get it
  417. back when re-activated.
  418. */
  419. vhwndFocus = vhwnd2B;
  420. }
  421. else if (hwnd == vhwnd0)
  422. /* 12-Mar-1987. to make sure focus set somewhere when
  423. * parent gets focus.
  424. */
  425. CalSetFocus (vhwndFocus);
  426. else
  427. fActed = FALSE;
  428. break;
  429. case WM_KILLFOCUS:
  430. /* If the monthly calendar is losing the focus,
  431. destroy its caret. Otherwise, do not process this message.
  432. */
  433. if (hwnd == vhwnd2B && !vfDayMode)
  434. DestroyCaret ();
  435. else
  436. fActed = FALSE;
  437. break;
  438. case WM_SYSCOLORCHANGE:
  439. /* The system colors have changed. Destroy and recreate
  440. the brushes.
  441. */
  442. DestroyBrushes ();
  443. CreateBrushes ();
  444. /* Repaint since AppWorkspace color may have changed */
  445. InvalidateRect(hwnd, NULL, TRUE);
  446. break;
  447. case WM_ERASEBKGND:
  448. PaintBack (hwnd, (HDC)wParam);
  449. break;
  450. case WM_INITMENU:
  451. /* Menu is being pulled down. Enable/disable, check/uncheck
  452. menu items.
  453. */
  454. InitMenuItems ();
  455. break;
  456. case WM_WININICHANGE:
  457. CalWinIniChange();
  458. break;
  459. default:
  460. fActed = FALSE;
  461. break;
  462. }
  463. return (fActed ? 0L : DefWindowProc (hwnd, message, wParam, lParam));
  464. }
  465. /**** XcoWnd1 - return the xco of where to put Wnd1 */
  466. INT APIENTRY XcoWnd1 ()
  467. {
  468. RECT rect;
  469. int cxDesired, cxAvailable, xcoLeft;
  470. GetClientRect (vhwnd0, &rect);
  471. cxDesired = vcxWnd1;
  472. cxAvailable = rect.right - rect.left;
  473. xcoLeft = 0;
  474. if (cxAvailable > cxDesired)
  475. xcoLeft = (cxAvailable - cxDesired) / 2;
  476. #ifdef DISABLE
  477. return (max (xcoLeft, vcxFont));
  478. #endif
  479. return (xcoLeft);
  480. }
  481. /**** YcoWnd1 - return the yco of where to put Wnd1 */
  482. INT APIENTRY YcoWnd1 ()
  483. {
  484. RECT rect;
  485. INT cyAvailable;
  486. INT ycoTop;
  487. GetClientRect (vhwnd0, &rect);
  488. cyAvailable = rect.bottom - rect.top;
  489. ycoTop = 0;
  490. if (cyAvailable > vcyWnd1)
  491. ycoTop = (cyAvailable - vcyWnd1) / 2;
  492. #ifdef DISABLE
  493. return (max (ycoTop, vcyBorder));
  494. #endif
  495. return (ycoTop);
  496. }
  497. /**** CalSetFocus - Set the focus unless vfNoGrabFocus is set. This
  498. is used to prevent Calendar from grabbing the focus if brought
  499. up iconic.
  500. */
  501. VOID APIENTRY FAR CalSetFocus (HWND hwnd)
  502. {
  503. if (!vfNoGrabFocus)
  504. SetFocus (hwnd);
  505. }
  506. /**** InitMenuItems */
  507. VOID APIENTRY InitMenuItems ()
  508. {
  509. register WORD mf1;
  510. register WORD mf2;
  511. HMENU hMenu;
  512. WORD2DWORD iSelFirst;
  513. WORD2DWORD iSelLast;
  514. UINT wFmt;
  515. /* Get a handle to the menu. */
  516. hMenu = GetMenu (vhwnd0);
  517. /* Cut and Copy - enable iff edit control has focus and
  518. some text is selected.
  519. Paste - enable iff edit control has focus and the clipboard
  520. is not empty.
  521. */
  522. mf1 = mf2 = MF_GRAYED;
  523. if (vhwndFocus != vhwnd2B)
  524. {
  525. /* Focus is not on monthly calendar so it must be on either
  526. the appointment edit control or the notes edit control.
  527. */
  528. /* Enable Cut and Copy if the selection isn't null. */
  529. MSendMsgEM_GETSEL(vhwndFocus, &iSelFirst, &iSelLast);
  530. if (iSelFirst != iSelLast)
  531. mf1 = MF_ENABLED;
  532. /* Enable Paste if the clipboard isn't empty. */
  533. if (OpenClipboard (vhwnd0))
  534. {
  535. wFmt = 0;
  536. /* If clipboard has any text data, enable paste item. otherwise
  537. leave it grayed. */
  538. while ((wFmt = EnumClipboardFormats(wFmt)) != 0)
  539. {
  540. if (wFmt == CF_TEXT)
  541. {
  542. mf2 = MF_ENABLED;
  543. break;
  544. }
  545. }
  546. CloseClipboard ();
  547. }
  548. }
  549. EnableMenuItem (hMenu, IDCM_CUT, mf1);
  550. EnableMenuItem (hMenu, IDCM_COPY, mf1);
  551. EnableMenuItem (hMenu, IDCM_PASTE, mf2);
  552. /* Check day if in day mode, check month if in month mode.
  553. Uncheck the other.
  554. */
  555. mf1 = MF_CHECKED;
  556. mf2 = MF_UNCHECKED;
  557. if (!vfDayMode)
  558. {
  559. mf1 = MF_UNCHECKED;
  560. mf2 = MF_CHECKED;
  561. }
  562. CheckMenuItem (hMenu, IDCM_DAY, mf1);
  563. CheckMenuItem (hMenu, IDCM_MONTH, mf2);
  564. /* Alarm Set - enable iff focus is on an appointment.
  565. If enabled, check iff appointment has alarm set.
  566. */
  567. mf1 = MF_GRAYED;
  568. mf2 = MF_UNCHECKED;
  569. if (vhwndFocus == vhwnd3)
  570. {
  571. mf1 = MF_ENABLED;
  572. if (FAlarm (vlnCur))
  573. mf2 = MF_CHECKED;
  574. }
  575. EnableMenuItem (hMenu, IDCM_SET, mf1);
  576. CheckMenuItem (hMenu, IDCM_SET, mf2);
  577. /* Options special time - enable if in day mode. */
  578. mf1 = MF_GRAYED;
  579. if (vfDayMode)
  580. mf1 = MF_ENABLED;
  581. EnableMenuItem (hMenu, IDCM_SPECIALTIME, mf1);
  582. }
  583. VOID APIENTRY CalWinIniChange()
  584. {
  585. HMENU hMenu;
  586. SHORT id, nx;
  587. CHAR ch;
  588. static bszDecRead=FALSE;
  589. /* Set decimal to scan for */
  590. if (bszDecRead)
  591. ch=szDec[0]; /* If we already changed it. */
  592. else
  593. ch='.'; /* First time. */
  594. bszDecRead=TRUE;
  595. /* Get the intl decimal character for use in Page Setup Box. */
  596. GetProfileString("intl", "sDecimal", ".", szDec, 4);
  597. /* Scan for . and replace with intl decimal */
  598. for (id=2; id<6; id++)
  599. {
  600. for (nx=0; nx < lstrlen((LPSTR)chPageText[id]); nx++)
  601. if (chPageText[id][nx]==ch)
  602. chPageText[id][nx]=szDec[0];
  603. }
  604. hMenu = GetMenu(vhwnd0);
  605. /* Check if a default printer exists */
  606. /* Fix for Bug #5607 --SANKAR-- 10-30-89 */
  607. if(bPrinterSetupDone)
  608. {
  609. if(!IsDefaultPrinterStillValid((LPSTR)szPrinter))
  610. bPrinterSetupDone = FALSE; /* No longer valid */
  611. }
  612. #ifdef SLOWTHINGSDOWN
  613. if (!(hdc = GetPrinterDC()))
  614. {
  615. EnableMenuItem(hMenu, IDCM_PRINT, MF_GRAYED);
  616. }
  617. else
  618. {
  619. DeleteDC(hdc);
  620. EnableMenuItem(hMenu, IDCM_PRINT, MF_ENABLED);
  621. }
  622. #endif
  623. InitTimeDate (vhInstance, vfHour24 ? GTS_24HOUR : GTS_12HOUR);
  624. /* Force repainting of windows that contain date time strings. */
  625. InvalidateRect(vhwnd0, NULL, TRUE);
  626. }