Leaked source code of windows server 2003
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.

758 lines
23 KiB

  1. /****************************************************************************/
  2. /* */
  3. /* WFTREE.C - */
  4. /* */
  5. /* Windows File System Tree Window Proc Routines */
  6. /* */
  7. /****************************************************************************/
  8. #include "winfile.h"
  9. #include "winnet.h"
  10. #include "lfn.h"
  11. #include "wfcopy.h"
  12. HICON GetTreeIcon(HWND hWnd);
  13. VOID APIENTRY CheckEscapes(LPSTR);
  14. HICON
  15. GetTreeIcon(
  16. HWND hWnd
  17. )
  18. {
  19. HWND hwndTree, hwndDir;
  20. hwndTree = HasTreeWindow(hWnd);
  21. hwndDir = HasDirWindow(hWnd);
  22. if (hwndTree && hwndDir)
  23. return hicoTreeDir;
  24. else if (hwndTree)
  25. return hicoTree;
  26. else
  27. return hicoDir;
  28. }
  29. VOID
  30. APIENTRY
  31. GetTreeWindows(
  32. HWND hwnd,
  33. PHWND phwndTree,
  34. PHWND phwndDir,
  35. PHWND phwndDrives
  36. )
  37. {
  38. if (phwndTree) {
  39. *phwndTree = GetDlgItem(hwnd, IDCW_TREECONTROL);
  40. }
  41. if (phwndDir) {
  42. *phwndDir = GetDlgItem(hwnd, IDCW_DIR);
  43. }
  44. if (phwndDrives) {
  45. *phwndDrives = GetDlgItem(hwnd, IDCW_DRIVES);
  46. }
  47. }
  48. // returns hwndTree, hwndDir or hwndDrives depending on the focus tracking
  49. // for the window. if none is found we return NULL
  50. HWND
  51. APIENTRY
  52. GetTreeFocus(
  53. HWND hwndTree
  54. )
  55. {
  56. HWND hwnd, hwndLast = NULL;
  57. hwndLast = hwnd = (HWND)GetWindowLongPtr(hwndTree, GWLP_LASTFOCUS);
  58. while (hwnd && hwnd != hwndTree) {
  59. hwndLast = hwnd;
  60. hwnd = GetParent(hwnd);
  61. }
  62. return hwndLast;
  63. }
  64. /*--------------------------------------------------------------------------*/
  65. /* */
  66. /* CompactPath() - */
  67. /* */
  68. /*--------------------------------------------------------------------------*/
  69. BOOL
  70. APIENTRY
  71. CompactPath(
  72. HDC hDC,
  73. LPSTR lpszPath,
  74. WORD dx
  75. )
  76. {
  77. register INT len;
  78. INT dxFixed, dxT;
  79. LPSTR lpEnd; /* end of the unfixed string */
  80. LPSTR lpFixed; /* start of text that we always display */
  81. BOOL bEllipsesIn;
  82. CHAR szTemp[MAXPATHLEN];
  83. /* Does it already fit? */
  84. MGetTextExtent(hDC, lpszPath, lstrlen(lpszPath), &dxFixed, NULL);
  85. if (dxFixed <= (INT)dx)
  86. return(TRUE);
  87. /* Search backwards for the '\', and man, it better be there! */
  88. lpFixed = lpszPath + lstrlen(lpszPath);
  89. while (*lpFixed != '\\')
  90. lpFixed = AnsiPrev(lpszPath, lpFixed);
  91. /* Save this guy to prevent overlap. */
  92. lstrcpy(szTemp, lpFixed);
  93. lpEnd = lpFixed;
  94. bEllipsesIn = FALSE;
  95. MGetTextExtent(hDC, lpFixed, lstrlen(lpFixed), &dxFixed, NULL);
  96. while (TRUE) {
  97. MGetTextExtent(hDC, lpszPath, (int)(lpEnd - lpszPath), &dxT, NULL);
  98. len = dxFixed + dxT;
  99. if (bEllipsesIn)
  100. len += dxEllipses;
  101. if (len <= (INT)dx)
  102. break;
  103. bEllipsesIn = TRUE;
  104. if (lpEnd <= lpszPath) {
  105. /* Things didn't fit. */
  106. lstrcpy(lpszPath, szEllipses);
  107. lstrcat(lpszPath, szTemp);
  108. return(FALSE);
  109. }
  110. /* Step back a character. */
  111. lpEnd = AnsiPrev(lpszPath, lpEnd);
  112. }
  113. if (bEllipsesIn) {
  114. lstrcpy(lpEnd, szEllipses);
  115. lstrcat(lpEnd, szTemp);
  116. }
  117. return(TRUE);
  118. }
  119. //
  120. // BOOL APIENTRY ResizeSplit(HWND hWnd, int dxSplit)
  121. //
  122. // creates/resizes children of the MDI child for the given path or resizes
  123. // (perhaps creating and destroying) these guys based on the dxSplit
  124. // parameter
  125. //
  126. // in:
  127. // hWnd window to fiddle with
  128. // dxSpit location of split between tree and dir panes
  129. // if less than size limit no tree is created
  130. // (current is destroyed)
  131. // if past limit on right margin the dir window
  132. // is destroyed (or not created)
  133. //
  134. // returns:
  135. // TRUE success, windows created
  136. // FALSE failure, windows failed creation, out of mem, or
  137. // the tree was in a state the couldn't be resized
  138. //
  139. BOOL
  140. APIENTRY
  141. ResizeSplit(
  142. HWND hWnd,
  143. INT dxSplit
  144. )
  145. {
  146. RECT rc;
  147. HWND hwndTree, hwndDir, hwndDrives, hwndLB;
  148. DWORD dwTemp;
  149. GetTreeWindows(hWnd, &hwndTree, &hwndDir, &hwndDrives);
  150. if (hwndTree && GetWindowLong(hwndTree, GWL_READLEVEL))
  151. return FALSE;
  152. GetClientRect(hWnd, &rc);
  153. // create the drives
  154. if (!hwndDrives) {
  155. // make new drives window
  156. hwndDrives = CreateWindowEx(0, szDrivesClass, NULL,
  157. WS_CHILD | WS_VISIBLE,
  158. 0, 0, 0, 0,
  159. hWnd, (HMENU)IDCW_DRIVES,
  160. hAppInstance, NULL);
  161. if (!hwndDrives)
  162. return FALSE;
  163. }
  164. if (dxSplit > dxDriveBitmap * 2) {
  165. if (!hwndTree) { // make new tree window
  166. hwndTree = CreateWindowEx(0, szTreeControlClass,
  167. NULL, WS_CHILD | WS_VISIBLE,
  168. 0, 0, 0, 0, hWnd, (HMENU)IDCW_TREECONTROL,
  169. hAppInstance, NULL);
  170. if (!hwndTree)
  171. return FALSE;
  172. // only reset this if the dir window already
  173. // exists, that is we are creating the tree
  174. // by splitting open a dir window
  175. if (hwndDir)
  176. SendMessage(hwndTree, TC_SETDRIVE, MAKEWORD(FALSE, 0), 0L);
  177. }
  178. } else if (hwndTree) { // we are closing the tree window
  179. // If the directory window is empty, then set the focus to the
  180. // drives window.
  181. if (hwndDir) {
  182. hwndLB = GetDlgItem (hwndDir,IDCW_LISTBOX);
  183. if (hwndLB) {
  184. SendMessage (hwndLB,LB_GETTEXT,0,(LPARAM)(LPSTR) &dwTemp);
  185. if (!dwTemp)
  186. SetFocus (hwndDrives);
  187. }
  188. }
  189. DestroyWindow(hwndTree);
  190. dxSplit = 0;
  191. }
  192. if ((rc.right - dxSplit) > dxDriveBitmap * 2) {
  193. if (!hwndDir) {
  194. hwndDir = CreateWindowEx(0, szDirClass, NULL,
  195. WS_CHILD | WS_VISIBLE,
  196. 0, 0, 0, 0,
  197. hWnd,(HMENU)IDCW_DIR,
  198. hAppInstance, NULL);
  199. if (!hwndDir)
  200. return FALSE;
  201. }
  202. } else if (hwndDir) {
  203. DestroyWindow(hwndDir);
  204. dxSplit = rc.right;
  205. }
  206. UpdateStatus(hWnd);
  207. SetWindowLong(hWnd, GWL_SPLIT, dxSplit);
  208. return TRUE;
  209. }
  210. /*--------------------------------------------------------------------------*/
  211. /* */
  212. /* TreeWndProc() - */
  213. /* */
  214. /*--------------------------------------------------------------------------*/
  215. /* WndProc for the MDI child window containing the drives, volume, and
  216. * directory tree child windows.
  217. */
  218. INT_PTR
  219. APIENTRY
  220. TreeWndProc(
  221. HWND hWnd,
  222. UINT wMsg,
  223. WPARAM wParam,
  224. LPARAM lParam
  225. )
  226. {
  227. HWND hwndTree, hwndDir, hwndDrives, hwndFocus;
  228. CHAR szDir[MAXPATHLEN];
  229. RECT rc;
  230. HDC hdc;
  231. STKCHK();
  232. switch (wMsg) {
  233. case WM_FILESYSCHANGE:
  234. MSG("TreeWndProc", "WM_FILESYSCHANGE");
  235. if (hwndDir = HasDirWindow(hWnd))
  236. SendMessage(hwndDir, wMsg, wParam, lParam);
  237. break;
  238. case FS_CHANGEDRIVES:
  239. MSG("TreeWndProc", "FS_CHANGEDRIVES");
  240. {
  241. INT iNewDrive;
  242. if (!(hwndDrives = GetDlgItem(hWnd, IDCW_DRIVES)))
  243. break;
  244. DestroyWindow(hwndDrives);
  245. // see if this drive has gone, if so set this to the
  246. // last drive in the list
  247. iNewDrive = -1;
  248. if (!IsValidDisk((INT)GetWindowLong(hWnd, GWL_TYPE))) {
  249. iNewDrive = rgiDrive[cDrives - 1];
  250. SetWindowLong(hWnd, GWL_TYPE, iNewDrive);
  251. }
  252. hwndDrives = CreateWindowEx(0, szDrivesClass, NULL,
  253. WS_CHILD | WS_VISIBLE,
  254. 0, 0, 0, 0,
  255. hWnd, (HMENU)IDCW_DRIVES,
  256. hAppInstance,
  257. NULL);
  258. if (!hwndDrives)
  259. return -1L;
  260. // Don't show the new stuff if the tree window is iconic
  261. if (IsIconic(hWnd))
  262. break;
  263. /* HACK! Send SIZENOMDICRAP in the wParam of the size message.
  264. * This will re-compute the sizes of all three areas of
  265. * the tree window (in case the drive section grows or
  266. * shrinks) and not pass it the size message on to the
  267. * DefMDIChildProc() */
  268. GetClientRect(hWnd, &rc);
  269. SendMessage(hWnd, WM_SIZE, SIZENOMDICRAP, MAKELONG(rc.right, rc.bottom));
  270. // refresh the tree if necessary
  271. if (iNewDrive >= 0) {
  272. GetSelectedDirectory((WORD)(iNewDrive+1), szDir);
  273. SendMessage(GetDlgItem(hWnd, IDCW_TREECONTROL),
  274. TC_SETDRIVE, MAKEWORD(FALSE, 0), (LPARAM)szDir);
  275. }
  276. break;
  277. }
  278. case FS_GETSELECTION:
  279. {
  280. #define pfDir (BOOL *)lParam
  281. LPSTR p;
  282. MSG("TreeWndProc", "FS_GETSELECTION");
  283. GetTreeWindows(hWnd, &hwndTree, &hwndDir, &hwndDrives);
  284. hwndFocus = GetTreeFocus(hWnd);
  285. if (hwndFocus == hwndDir || !hwndTree) {
  286. return SendMessage(hwndDir, FS_GETSELECTION, wParam, lParam);
  287. } else {
  288. p = (LPSTR)LocalAlloc(LPTR, MAXPATHLEN);
  289. if (p) {
  290. SendMessage(hWnd, FS_GETDIRECTORY, MAXPATHLEN, (LPARAM)p);
  291. StripBackslash(p);
  292. CheckEscapes(p);
  293. if (wParam == 2) { // BUG ??? wParam should be fMostRecentOnly
  294. if (pfDir) {
  295. *pfDir = IsLFN(p);
  296. }
  297. LocalFree((HANDLE)p);
  298. return (INT_PTR)p;
  299. }
  300. }
  301. if (pfDir) {
  302. *pfDir = TRUE;
  303. }
  304. return (INT_PTR)p;
  305. }
  306. #undef pfDir
  307. }
  308. case FS_GETDIRECTORY:
  309. MSG("TreeWndProc", "FS_GETDIRECTORY");
  310. // wParam is the length of the string pointed to by lParam
  311. // returns in lParam ANSI directory string with
  312. // a trailing backslash. if you want to do a SetCurrentDirecotor()
  313. // you must first StripBackslash() the thing!
  314. GetMDIWindowText(hWnd, (LPSTR)lParam, (INT)wParam); // get the string
  315. StripFilespec((LPSTR)lParam); // Remove the trailing extention
  316. AddBackslash((LPSTR)lParam); // terminate with a backslash
  317. break;
  318. case FS_GETFILESPEC:
  319. MSG("TreeWndProc", "FS_GETFILESPEC");
  320. // returns the current filespec (from View.Include...). this is
  321. // an uppercase ANSI string
  322. GetMDIWindowText(hWnd, (LPSTR)lParam, (INT)wParam);
  323. StripPath((LPSTR)lParam);
  324. break;
  325. // redirect these messages to the drive icons to get the same result as
  326. // dropping on the active drive.
  327. // this is especially useful when we are minimized
  328. case WM_DRAGSELECT:
  329. case WM_QUERYDROPOBJECT:
  330. case WM_DROPOBJECT:
  331. MSG("TreeWndProc", "WM..OBJECT");
  332. // Do nothing
  333. return(TRUE);
  334. if (hwndDrives = HasDrivesWindow(hWnd)) {
  335. return SendMessage(hwndDrives, wMsg, wParam, lParam);
  336. }
  337. if (hwndDir = HasDirWindow(hWnd)) {
  338. return SendMessage(hwndDir, wMsg, wParam, lParam);
  339. }
  340. break;
  341. case FS_GETDRIVE:
  342. MSG("TreeWndProc", "FS_GETDRIVE");
  343. GetTreeWindows(hWnd, &hwndTree, &hwndDir, NULL);
  344. if (hwndTree)
  345. return SendMessage(hwndTree, wMsg, wParam, lParam);
  346. else
  347. return SendMessage(hwndDir, wMsg, wParam, lParam);
  348. break;
  349. case WM_CREATE:
  350. TRACE(BF_WM_CREATE, "TreeWndProc - WM_CREATE");
  351. {
  352. INT dxSplit;
  353. WORD wDrive;
  354. // lpcs->lpszName is the path we are opening the
  355. // window for (has extension stuff "*.*")
  356. #define lpcs ((LPCREATESTRUCT)lParam)
  357. #define lpmdics ((LPMDICREATESTRUCT)(lpcs->lpCreateParams))
  358. wDrive = lpcs->lpszName[0];
  359. if (wDrive >= 'a')
  360. wDrive -= 'a';
  361. else
  362. wDrive -= 'A';
  363. SetWindowLong(hWnd, GWL_TYPE, wDrive);
  364. dxSplit = (SHORT)LOWORD(lpmdics->lParam);
  365. // if dxSplit is negative we split in the middle
  366. if (dxSplit < 0)
  367. dxSplit = lpcs->cx / 2;
  368. SetWindowLong(hWnd, GWL_SPLIT, dxSplit);
  369. SetWindowLongPtr(hWnd, GWLP_LASTFOCUS, 0);
  370. SetWindowLong(hWnd, GWL_FSCFLAG, FALSE);
  371. if (!ResizeSplit(hWnd, dxSplit))
  372. return -1;
  373. GetTreeWindows(hWnd, &hwndTree, &hwndDir, NULL);
  374. SetWindowLongPtr(hWnd, GWLP_LASTFOCUS, (LONG_PTR)(hwndTree ? hwndTree : hwndDir));
  375. break;
  376. }
  377. case WM_CLOSE:
  378. MSG("TreeWndProc", "WM_CLOSE");
  379. // don't allow the last MDI child to be closed!
  380. if (hwndTree = HasTreeWindow(hWnd)) {
  381. // don't close if we are reading the tree
  382. if (GetWindowLong(hwndTree, GWL_READLEVEL))
  383. break;
  384. }
  385. // don't leve current dir on floppies
  386. GetSystemDirectory(szDir, sizeof(szDir));
  387. SheSetCurDrive(DRIVEID(szDir));
  388. if (!IsLastWindow())
  389. goto DEF_MDI_PROC; // this will close this window
  390. break;
  391. case WM_MDIACTIVATE:
  392. MSG("TreeWndProc", "WM_MDIACTIVATE");
  393. if (GET_WM_MDIACTIVATE_FACTIVATE(hWnd, wParam, lParam)) { // we are receiving the activation
  394. lFreeSpace = -1L;
  395. UpdateStatus(hWnd);
  396. hwndFocus = (HWND)GetWindowLongPtr(hWnd, GWLP_LASTFOCUS);
  397. SetFocus(hwndFocus);
  398. } else if (hwndDrives = HasDrivesWindow(hWnd))
  399. SendMessage(hwndDrives,wMsg,wParam,lParam);
  400. break;
  401. case WM_SETFOCUS:
  402. MSG("TreeWndProc", "WM_SETFOCUS");
  403. hwndFocus = (HWND)GetWindowLongPtr(hWnd, GWLP_LASTFOCUS);
  404. SetFocus(hwndFocus);
  405. break;
  406. case WM_INITMENUPOPUP:
  407. MSG("TreeWndProc", "WM_INITMENUPOPUP");
  408. if (HIWORD(lParam)) {
  409. EnableMenuItem((HMENU)wParam, SC_CLOSE,
  410. IsLastWindow() ? MF_BYCOMMAND | MF_DISABLED | MF_GRAYED :
  411. MF_ENABLED);
  412. }
  413. break;
  414. case WM_SYSCOMMAND:
  415. MSG("TreeWndProc", "WM_SYSCOMMAND");
  416. if (wParam != SC_SPLIT)
  417. goto DEF_MDI_PROC;
  418. GetClientRect(hWnd, &rc);
  419. lParam = MAKELONG(rc.right / 2, 0);
  420. // fall through
  421. case WM_LBUTTONDOWN:
  422. MSG("TreeWndProc", "WM_LBUTTONDOWN");
  423. {
  424. MSG msg;
  425. INT x, y, dx, dy;
  426. if (IsIconic(hWnd))
  427. break;
  428. if (hwndDrives = GetDlgItem(hWnd, IDCW_DRIVES)) {
  429. GetClientRect(hwndDrives, &rc);
  430. y = rc.bottom;
  431. } else {
  432. y = 0;
  433. }
  434. x = LOWORD(lParam);
  435. GetClientRect(hWnd, &rc);
  436. dx = 4;
  437. dy = rc.bottom - y; // the height of the client less the drives window
  438. hdc = GetDC(hWnd);
  439. // split bar loop
  440. PatBlt(hdc, x - dx / 2, y, dx, dy, PATINVERT);
  441. SetCapture(hWnd);
  442. while (GetMessage(&msg, NULL, 0, 0)) {
  443. if (msg.message == WM_KEYDOWN || msg.message == WM_SYSKEYDOWN ||
  444. (msg.message >= WM_MOUSEFIRST && msg.message <= WM_MOUSELAST)) {
  445. if (msg.message == WM_LBUTTONUP || msg.message == WM_LBUTTONDOWN)
  446. break;
  447. if (msg.message == WM_KEYDOWN) {
  448. if (msg.wParam == VK_LEFT) {
  449. msg.message = WM_MOUSEMOVE;
  450. msg.pt.x -= 2;
  451. } else if (msg.wParam == VK_RIGHT) {
  452. msg.message = WM_MOUSEMOVE;
  453. msg.pt.x += 2;
  454. } else if (msg.wParam == VK_RETURN ||
  455. msg.wParam == VK_ESCAPE) {
  456. break;
  457. }
  458. SetCursorPos(msg.pt.x, msg.pt.y);
  459. }
  460. if (msg.message == WM_MOUSEMOVE) {
  461. // erase old
  462. PatBlt(hdc, x - dx / 2, y, dx, dy, PATINVERT);
  463. ScreenToClient(hWnd, &msg.pt);
  464. x = msg.pt.x;
  465. // put down new
  466. PatBlt(hdc, x - dx / 2, y, dx, dy, PATINVERT);
  467. }
  468. } else {
  469. DispatchMessage(&msg);
  470. }
  471. }
  472. ReleaseCapture();
  473. // erase old
  474. PatBlt(hdc, x - dx / 2, y, dx, dy, PATINVERT);
  475. ReleaseDC(hWnd, hdc);
  476. if (msg.wParam != VK_ESCAPE) {
  477. if (ResizeSplit(hWnd, x))
  478. SendMessage(hWnd, WM_SIZE, SIZENOMDICRAP, MAKELONG(rc.right, rc.bottom));
  479. }
  480. break;
  481. }
  482. case WM_QUERYDRAGICON:
  483. MSG("TreeWndProc", "WM_QUERYDRAGICON");
  484. return (INT_PTR)GetTreeIcon(hWnd);
  485. break;
  486. case WM_ERASEBKGND:
  487. MSG("TreeWndProc", "WM_ERASEBKGND");
  488. if (IsIconic(hWnd)) {
  489. // this paints the background of the icon properly, doing
  490. // brush allignment and other nasty stuff
  491. DefWindowProc(hWnd, WM_ICONERASEBKGND, wParam, 0L);
  492. } else {
  493. goto DEF_MDI_PROC;
  494. }
  495. break;
  496. case WM_PAINT:
  497. MSG("TreeWndProc", "WM_PAINT");
  498. {
  499. PAINTSTRUCT ps;
  500. hdc = BeginPaint(hWnd, &ps);
  501. if (IsIconic(hWnd)) {
  502. DrawIcon(hdc, 0, 0, GetTreeIcon(hWnd));
  503. } else {
  504. RECT rc2;
  505. GetClientRect(hWnd, &rc);
  506. rc.left = GetSplit(hWnd);
  507. if (rc.left >= rc.right)
  508. rc.left = 0;
  509. rc.right = rc.left + dxFrame;
  510. GetClientRect(HasDrivesWindow(hWnd), &rc2);
  511. rc2.top = rc2.bottom;
  512. rc2.bottom += dyBorder;
  513. rc2.left = rc.left;
  514. rc2.right = rc.right;
  515. FillRect(hdc, &rc2, GetStockObject(BLACK_BRUSH));
  516. // draw the black pane handle
  517. rc.top = rc.bottom - GetSystemMetrics(SM_CYHSCROLL);
  518. FillRect(hdc, &rc, GetStockObject(BLACK_BRUSH));
  519. }
  520. EndPaint(hWnd, &ps);
  521. break;
  522. }
  523. case WM_SIZE:
  524. if (wParam != SIZEICONIC)
  525. ResizeWindows(hWnd,LOWORD(lParam),HIWORD(lParam));
  526. // if wParam is SIZENOMDICRAP this WM_SIZE was generated by us.
  527. // don't let this through to the DefMDIChildProc().
  528. // that might change the min/max state, (show parameter)
  529. if (wParam == SIZENOMDICRAP)
  530. break;
  531. /*** FALL THRU ***/
  532. default:
  533. DEF_MDI_PROC:
  534. return DefMDIChildProc(hWnd, wMsg, wParam, lParam);
  535. }
  536. return 0L;
  537. }
  538. VOID
  539. ResizeWindows(
  540. HWND hwndParent,
  541. WORD dxWindow,
  542. WORD dyWindow
  543. )
  544. {
  545. INT dy, split;
  546. INT cDriveRows, cDrivesPerRow;
  547. DWORD dw;
  548. HWND hwndTree,hwndDir,hwndDrives;
  549. GetTreeWindows(hwndParent, &hwndTree, &hwndDir, &hwndDrives);
  550. split = GetSplit(hwndParent);
  551. // user has been fixed to do this right
  552. dy = dyWindow + dyBorder;
  553. if (hwndTree) {
  554. if (!hwndDir)
  555. MoveWindow(hwndTree, dxFrame, 0, dxWindow - dxFrame + dyBorder, dy, TRUE);
  556. else
  557. MoveWindow(hwndTree, -dyBorder, 0, split + dyBorder, dy, TRUE);
  558. }
  559. if (hwndDir) {
  560. if (!hwndTree)
  561. MoveWindow(hwndDir, dxFrame, 0, dxWindow - dxFrame + dyBorder, dy, TRUE);
  562. else
  563. MoveWindow(hwndDir, split + dxFrame, 0,
  564. dxWindow - split - dxFrame + dyBorder, dy, TRUE);
  565. }
  566. }