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.

720 lines
20 KiB

  1. //Copyright (c) 1998 - 1999 Microsoft Corporation
  2. /*******************************************************************************
  3. *
  4. * winsvw.cpp
  5. *
  6. * implementation of the CWinStationView class
  7. *
  8. *
  9. *******************************************************************************/
  10. #include "stdafx.h"
  11. #include "resource.h"
  12. #include "winsvw.h"
  13. #include "admindoc.h"
  14. #ifdef _DEBUG
  15. #define new DEBUG_NEW
  16. #undef THIS_FILE
  17. static char THIS_FILE[] = __FILE__;
  18. #endif
  19. //////////////////////////
  20. // MESSAGE MAP: CWinStationView
  21. //
  22. IMPLEMENT_DYNCREATE(CWinStationView, CView)
  23. BEGIN_MESSAGE_MAP(CWinStationView, CView)
  24. //{{AFX_MSG_MAP(CWinStationView)
  25. ON_WM_SIZE()
  26. ON_WM_CREATE()
  27. //}}AFX_MSG_MAP
  28. ON_MESSAGE(WM_ADMIN_UPDATE_PROCESSES, OnAdminUpdateProcesses)
  29. ON_MESSAGE(WM_ADMIN_REMOVE_PROCESS, OnAdminRemoveProcess)
  30. ON_MESSAGE(WM_ADMIN_REDISPLAY_PROCESSES, OnAdminRedisplayProcesses)
  31. ON_NOTIFY(TCN_SELCHANGE, IDC_WINSTATION_TABS, OnTabSelChange)
  32. ON_MESSAGE( WM_ADMIN_TABBED_VIEW , OnTabbed )
  33. ON_MESSAGE( WM_ADMIN_SHIFTTABBED_VIEW , OnShiftTabbed )
  34. ON_MESSAGE( WM_ADMIN_CTRLTABBED_VIEW , OnCtrlTabbed )
  35. ON_MESSAGE( WM_ADMIN_CTRLSHIFTTABBED_VIEW , OnCtrlShiftTabbed )
  36. ON_MESSAGE( WM_ADMIN_NEXTPANE_VIEW , OnNextPane )
  37. END_MESSAGE_MAP()
  38. PageDef CWinStationView::pages[NUMBER_OF_WINS_PAGES] = {
  39. { NULL, RUNTIME_CLASS( CWinStationProcessesPage ), IDS_TAB_PROCESSES, PAGE_WS_PROCESSES, NULL },
  40. { NULL, RUNTIME_CLASS( CWinStationInfoPage ), IDS_TAB_INFORMATION,PAGE_WS_INFO, NULL },
  41. { NULL, RUNTIME_CLASS( CWinStationModulesPage ), IDS_TAB_MODULES, PAGE_WS_MODULES, PF_PICASSO_ONLY },
  42. { NULL, RUNTIME_CLASS( CWinStationCachePage ), IDS_TAB_CACHE, PAGE_WS_CACHE, PF_PICASSO_ONLY },
  43. { NULL, RUNTIME_CLASS( CWinStationNoInfoPage ), 0, PAGE_WS_NO_INFO, PF_NO_TAB },
  44. };
  45. ///////////////////////
  46. // F'N: CWinStationView ctor
  47. //
  48. CWinStationView::CWinStationView()
  49. {
  50. m_pTabs = NULL;
  51. m_pTabFont = NULL;
  52. m_CurrPage = PAGE_WS_PROCESSES;
  53. } // end CWinStationView ctor
  54. ///////////////////////
  55. // F'N: CWinStationView dtor
  56. //
  57. CWinStationView::~CWinStationView()
  58. {
  59. if(m_pTabs) delete m_pTabs;
  60. if(m_pTabFont) delete m_pTabFont;
  61. } // end CWinStationView dtor
  62. #ifdef _DEBUG
  63. ///////////////////////////////
  64. // F'N: CWinStationView::AssertValid
  65. //
  66. void CWinStationView::AssertValid() const
  67. {
  68. CView::AssertValid();
  69. } // end CWinStationView::AssertValid
  70. ////////////////////////
  71. // F'N: CWinStationView::Dump
  72. //
  73. void CWinStationView::Dump(CDumpContext& dc) const
  74. {
  75. CView::Dump(dc);
  76. } // end CWinStationView::Dump
  77. #endif //_DEBUG
  78. ////////////////////////////
  79. // F'N: CWinStationView::OnCreate
  80. //
  81. int CWinStationView::OnCreate(LPCREATESTRUCT lpCreateStruct)
  82. {
  83. if (CView::OnCreate(lpCreateStruct) == -1)
  84. return -1;
  85. return 0;
  86. } // end CWinStationView::OnCreate
  87. ///////////////////////////////////
  88. // F'N: CWinStationView::OnInitialUpdate
  89. //
  90. // - pointers to the pages of the sheet are obtained
  91. //
  92. void CWinStationView::OnInitialUpdate()
  93. {
  94. // create the tab control
  95. m_pTabs = new CMyTabCtrl;
  96. if(!m_pTabs) return;
  97. m_pTabs->Create(WS_CHILD | WS_VISIBLE | WS_TABSTOP, CRect(0,0,0,0), this, IDC_WINSTATION_TABS);
  98. m_pTabFont = new CFont;
  99. if(m_pTabFont) {
  100. m_pTabFont->CreateStockObject(DEFAULT_GUI_FONT);
  101. m_pTabs->SetFont(m_pTabFont, TRUE);
  102. }
  103. TCHAR szTemp[40];
  104. CString tabString;
  105. int index = 0;
  106. for(int i = 0; i < NUMBER_OF_WINS_PAGES; i++) {
  107. // If the page is shown under Picasso only and we're not running
  108. // under Picasso, skip to the next one
  109. if((pages[i].flags & PF_PICASSO_ONLY) && !((CWinAdminApp*)AfxGetApp())->IsPicasso()) continue;
  110. if(!(pages[i].flags & PF_NO_TAB)) {
  111. tabString.LoadString(pages[i].tabStringID);
  112. lstrcpyn(szTemp, tabString, sizeof(szTemp) / sizeof(TCHAR));
  113. AddTab(index, szTemp, i);
  114. index++;
  115. }
  116. pages[i].m_pPage = (CAdminPage*)pages[i].m_pRuntimeClass->CreateObject();
  117. pages[i].m_pPage->Create(NULL, NULL, WS_CHILD, CRect(0, 0, 0, 0), this, i, NULL);
  118. GetDocument()->AddView(pages[i].m_pPage);
  119. }
  120. m_pTabs->SetCurSel(0);
  121. m_CurrPage = PAGE_WS_PROCESSES;
  122. ((CWinAdminDoc*)GetDocument())->SetCurrentPage(PAGE_WS_PROCESSES);
  123. OnChangePage(NULL, NULL);
  124. } // end CWinStationView::OnInitialUpdate
  125. //////////////////////////
  126. // F'N: CWinStationView::OnSize
  127. //
  128. // - size the pages to fill the entire view
  129. //
  130. void CWinStationView::OnSize(UINT nType, int cx, int cy)
  131. {
  132. RECT rect;
  133. GetClientRect(&rect);
  134. if(m_pTabs->GetSafeHwnd()) { // make sure the tabs object is valid
  135. m_pTabs->MoveWindow(&rect, TRUE); // size the tabs
  136. // for the next part (sizing of pages), we might want to add a member var
  137. // that keeps track of which page/tab is current... this way we could
  138. // only actually do a redraw (MoveWindow second parm == TRUE) for the
  139. // guy who is currently visible--DJM
  140. // we want to size the pages, too
  141. m_pTabs->AdjustRect(FALSE, &rect);
  142. for(int i = 0; i < NUMBER_OF_WINS_PAGES; i++) {
  143. if(pages[i].m_pPage && pages[i].m_pPage->GetSafeHwnd())
  144. pages[i].m_pPage->MoveWindow(&rect, TRUE);
  145. }
  146. }
  147. } // end CWinStationView::OnSize
  148. //////////////////////////
  149. // F'N: CWinStationView::OnDraw
  150. //
  151. // - the CWinStationView and it's pages draw themselves, so there isn't anything
  152. // to do here...
  153. //
  154. void CWinStationView::OnDraw(CDC* pDC)
  155. {
  156. CDocument* pDoc = GetDocument();
  157. // TODO: add draw code here
  158. } // end CWinStationView::OnDraw
  159. /////////////////////////
  160. // F'N: CWinStationView::Reset
  161. //
  162. // - 'resets' the view by taking a pointer to a CWinStation object and filling in
  163. // the various property pages with info appropriate to that WinStation
  164. //
  165. void CWinStationView::Reset(void *pWinStation)
  166. {
  167. if(!((CWinStation*)pWinStation)->AdditionalDone()) ((CWinStation*)pWinStation)->QueryAdditionalInformation();
  168. for(int i = 0; i < NUMBER_OF_WINS_PAGES; i++) {
  169. if(pages[i].m_pPage)
  170. pages[i].m_pPage->Reset((CWinStation*)pWinStation);
  171. }
  172. if(((CWinAdminApp*)AfxGetApp())->IsPicasso()) {
  173. if((((CWinStation*)pWinStation)->GetState() == State_Disconnected
  174. || !((CWinStation*)pWinStation)->GetExtendedInfo())
  175. && !((CWinStation*)pWinStation)->IsSystemConsole()) {
  176. // Delete the 'Cache' tab
  177. m_pTabs->DeleteItem(3);
  178. // Delete the 'Modules' tab
  179. m_pTabs->DeleteItem(2);
  180. // If the 'Cache' tab was current, make the 'Processes' tab current
  181. if(m_pTabs->GetCurSel() == 0xFFFFFFFF) {
  182. m_pTabs->SetCurSel(0);
  183. OnChangePage(0,0);
  184. }
  185. } else if(m_pTabs->GetItemCount() == 2) {
  186. TCHAR szTemp[40];
  187. CString tabString;
  188. tabString.LoadString(IDS_TAB_MODULES);
  189. lstrcpyn(szTemp, tabString, sizeof(szTemp) / sizeof(TCHAR));
  190. AddTab(2, szTemp, 2);
  191. tabString.LoadString(IDS_TAB_CACHE);
  192. lstrcpyn(szTemp, tabString, sizeof(szTemp) / sizeof(TCHAR));
  193. AddTab(3, szTemp, 3);
  194. }
  195. }
  196. ((CWinAdminDoc*)GetDocument())->SetCurrentPage(m_CurrPage);
  197. // We want to fake a ChangePage if we are on page 1 or page 2
  198. if(m_pTabs->GetCurSel() > 0)
  199. OnChangePage(0,0);
  200. } // end CWinStationView::Reset
  201. //////////////////////////
  202. // F'N: CWinStationView::AddTab
  203. //
  204. void CWinStationView::AddTab(int index, TCHAR* text, ULONG pageindex)
  205. {
  206. TC_ITEM tc;
  207. tc.mask = TCIF_TEXT | TCIF_PARAM;
  208. tc.pszText = text;
  209. tc.lParam = pageindex;
  210. m_pTabs->InsertItem(index, &tc);
  211. } // end CWinStationView::AddTab
  212. ////////////////////////////////
  213. // F'N: CWinStationView::OnChangePage
  214. //
  215. // - changes to a new WinStation page based on currently selected tab
  216. // - OnChangePage needs to force recalculation of scroll bars!!!--DJM
  217. //
  218. LRESULT CWinStationView::OnChangePage(WPARAM wParam, LPARAM lParam)
  219. {
  220. // find out which tab is now selected
  221. int index = m_pTabs->GetCurSel();
  222. int newpage = index;
  223. CWinStation *pWinStation =
  224. (CWinStation*)((CWinAdminDoc*)GetDocument())->GetCurrentSelectedNode();
  225. if(index != PAGE_WS_PROCESSES && pWinStation->IsSystemConsole()) {
  226. newpage = PAGE_WS_NO_INFO;
  227. }
  228. // hide the current page
  229. pages[m_CurrPage].m_pPage->ModifyStyle(WS_VISIBLE, WS_DISABLED);
  230. m_CurrPage = newpage;
  231. if( pages[ newpage ].flags != PF_NO_TAB )
  232. {
  233. CWinAdminDoc *pDoc = (CWinAdminDoc*)GetDocument();
  234. pDoc->RegisterLastFocus( TAB_CTRL );
  235. }
  236. ((CWinAdminDoc*)GetDocument())->SetCurrentPage(newpage);
  237. // show the new page
  238. pages[newpage].m_pPage->ModifyStyle(WS_DISABLED, WS_VISIBLE);
  239. pages[newpage].m_pPage->ScrollToPosition(CPoint(0,0));
  240. pages[newpage].m_pPage->Invalidate();
  241. // pages[newpage].m_pPage->SetFocus();
  242. return 0;
  243. } // end CWinStationView::OnChangeview
  244. //////////////////////////
  245. // F'N: CWinStationView::OnTabSelChange
  246. //
  247. void CWinStationView::OnTabSelChange(NMHDR* pNMHDR, LRESULT* pResult)
  248. {
  249. OnChangePage( 0, 0);
  250. *pResult = 0;
  251. } // end CWinStationView::OnTabSelChange
  252. //////////////////////////
  253. // F'N: CWinStationView::OnAdminUpdateProcesses
  254. //
  255. LRESULT CWinStationView::OnAdminUpdateProcesses(WPARAM wParam, LPARAM lParam)
  256. {
  257. ((CWinStationProcessesPage*)pages[PAGE_WS_PROCESSES].m_pPage)->UpdateProcesses();
  258. return 0;
  259. } // end CWinStationView::OnAdminUpdateProcesses
  260. //////////////////////////
  261. // F'N: CWinStationView::OnAdminRemoveProcess
  262. //
  263. LRESULT CWinStationView::OnAdminRemoveProcess(WPARAM wParam, LPARAM lParam)
  264. {
  265. ((CWinStationProcessesPage*)pages[PAGE_WS_PROCESSES].m_pPage)->RemoveProcess((CProcess*)lParam);
  266. return 0;
  267. } // end CWinStationView::OnAdminRemoveProcess
  268. //////////////////////////
  269. // F'N: CWinStationView::OnAdminRedisplayProcesses
  270. //
  271. LRESULT CWinStationView::OnAdminRedisplayProcesses(WPARAM wParam, LPARAM lParam)
  272. {
  273. ((CWinStationProcessesPage*)pages[PAGE_WS_PROCESSES].m_pPage)->DisplayProcesses();
  274. return 0;
  275. } // end CWinStationView::OnAdminRedisplayProcesses
  276. LRESULT CWinStationView::OnTabbed( WPARAM wp , LPARAM lp )
  277. {
  278. ODS( L"CWinStationView::OnTabbed " );
  279. if( m_pTabs != NULL )
  280. {
  281. CWinAdminDoc *pDoc = (CWinAdminDoc*)GetDocument();
  282. if( pDoc != NULL )
  283. {
  284. FOCUS_STATE nFocus = pDoc->GetLastRegisteredFocus( );
  285. if( nFocus == TREE_VIEW )
  286. {
  287. ODS( L"from tree to tab\n" );
  288. int nTab = m_pTabs->GetCurSel();
  289. m_pTabs->SetFocus( );
  290. m_pTabs->SetCurFocus( nTab );
  291. if( pages[ m_CurrPage ].flags == PF_NO_TAB )
  292. {
  293. pDoc->RegisterLastFocus( PAGED_ITEM );
  294. }
  295. else
  296. {
  297. pDoc->RegisterLastFocus( TAB_CTRL );
  298. }
  299. }
  300. else if( nFocus == TAB_CTRL )
  301. {
  302. ODS( L"from tab to item\n" );
  303. // set focus to item in page
  304. if( pages[ m_CurrPage ].flags == PF_NO_TAB )
  305. {
  306. CFrameWnd *p = (CFrameWnd*)pDoc->GetMainWnd();
  307. p->SendMessage( WM_FORCE_TREEVIEW_FOCUS , 0 , 0 );
  308. pDoc->RegisterLastFocus( TREE_VIEW );
  309. }
  310. else
  311. {
  312. pages[ m_CurrPage ].m_pPage->SetFocus( );
  313. pDoc->RegisterLastFocus( PAGED_ITEM );
  314. }
  315. }
  316. else
  317. {
  318. ODS( L"from item to treeview\n" );
  319. // set focus back to treeview
  320. CFrameWnd *p = (CFrameWnd*)pDoc->GetMainWnd();
  321. p->SendMessage( WM_FORCE_TREEVIEW_FOCUS , 0 , 0 );
  322. pDoc->RegisterLastFocus( TREE_VIEW );
  323. }
  324. pDoc->SetPrevFocus( nFocus );
  325. }
  326. }
  327. return 0;
  328. }
  329. //=-------------------------------------------------------------------------
  330. // OnShiftTabbed is called when the user wants to go back one
  331. // this code is duplicated in all view classes
  332. LRESULT CWinStationView::OnShiftTabbed( WPARAM , LPARAM )
  333. {
  334. ODS( L"CWinStationView::OnShiftTabbed " );
  335. if( m_pTabs != NULL )
  336. {
  337. CWinAdminDoc *pDoc = (CWinAdminDoc*)GetDocument();
  338. if( pDoc != NULL )
  339. {
  340. FOCUS_STATE nFocus = pDoc->GetLastRegisteredFocus( );
  341. switch( nFocus )
  342. {
  343. case TREE_VIEW:
  344. if( pages[ m_CurrPage].flags == PF_NO_TAB )
  345. {
  346. ODS( L"going back from tree to noinfo tab\n" );
  347. int nTab = m_pTabs->GetCurSel();
  348. m_pTabs->SetFocus( );
  349. m_pTabs->SetCurFocus( nTab );
  350. pDoc->RegisterLastFocus( TAB_CTRL );
  351. }
  352. else
  353. {
  354. ODS( L"going back from tree to paged item\n" );
  355. pages[ m_CurrPage ].m_pPage->SetFocus( );
  356. pDoc->RegisterLastFocus( PAGED_ITEM );
  357. }
  358. break;
  359. case TAB_CTRL:
  360. {
  361. ODS( L"going back from tab to treeview\n" );
  362. CFrameWnd *p = (CFrameWnd*)pDoc->GetMainWnd();
  363. p->SendMessage( WM_FORCE_TREEVIEW_FOCUS , 0 , 0 );
  364. pDoc->RegisterLastFocus( TREE_VIEW );
  365. }
  366. break;
  367. case PAGED_ITEM:
  368. {
  369. ODS( L"going back from paged item to tab\n" );
  370. int nTab = m_pTabs->GetCurSel();
  371. m_pTabs->SetFocus( );
  372. m_pTabs->SetCurFocus( nTab );
  373. pDoc->RegisterLastFocus( TAB_CTRL );
  374. }
  375. break;
  376. }
  377. pDoc->SetPrevFocus( nFocus );
  378. }
  379. }
  380. return 0;
  381. }
  382. //=-------------------------------------------------------------------------
  383. // ctrl + tab works the same as tab but because of our unorthodox ui
  384. // when under a tab control it will cycle over the tabs and back to the treeview
  385. //
  386. LRESULT CWinStationView::OnCtrlTabbed( WPARAM , LPARAM )
  387. {
  388. ODS( L"CWinStationView::OnCtrlTabbed " );
  389. int nTab;
  390. int nMaxTab;
  391. if( m_pTabs != NULL )
  392. {
  393. CWinAdminDoc *pDoc = (CWinAdminDoc*)GetDocument();
  394. if( pDoc != NULL )
  395. {
  396. FOCUS_STATE nFocus = pDoc->GetLastRegisteredFocus( );
  397. if( nFocus == TREE_VIEW )
  398. {
  399. ODS( L"from tree to tab\n" );
  400. nTab = m_pTabs->GetCurSel();
  401. nMaxTab = m_pTabs->GetItemCount( );
  402. if( nTab >= nMaxTab - 1 )
  403. {
  404. m_pTabs->SetCurSel( 0 );
  405. OnChangePage( 0 , 0 );
  406. nTab = 0;
  407. }
  408. m_pTabs->SetFocus( );
  409. m_pTabs->SetCurFocus( nTab );
  410. pDoc->RegisterLastFocus( TAB_CTRL );
  411. }
  412. else
  413. {
  414. nTab = m_pTabs->GetCurSel();
  415. nMaxTab = m_pTabs->GetItemCount( );
  416. if( nTab >= nMaxTab - 1 )
  417. {
  418. ODS( L"...back to treeview\n" );
  419. CFrameWnd *p = (CFrameWnd*)pDoc->GetMainWnd();
  420. p->SendMessage( WM_FORCE_TREEVIEW_FOCUS , 0 , 0 );
  421. pDoc->RegisterLastFocus( TREE_VIEW );
  422. }
  423. else
  424. {
  425. ODS( L" ...next tab...\n" );
  426. m_pTabs->SetCurSel( nTab + 1 );
  427. OnChangePage( 0 , 0 );
  428. m_pTabs->SetFocus( );
  429. m_pTabs->SetCurFocus( nTab + 1 );
  430. }
  431. }
  432. pDoc->SetPrevFocus( nFocus );
  433. }
  434. }
  435. return 0;
  436. }
  437. //=----------------------------------------------------------------------------
  438. // same as OnCtrlTab but we focus on moving in the other direction
  439. // tree_view to last tab -- current tab to ct - 1
  440. //
  441. LRESULT CWinStationView::OnCtrlShiftTabbed( WPARAM , LPARAM )
  442. {
  443. ODS( L"CWinStationView::OnCtrlShiftTabbed " );
  444. int nTab;
  445. int nMaxTab;
  446. if( m_pTabs != NULL )
  447. {
  448. CWinAdminDoc *pDoc = (CWinAdminDoc*)GetDocument();
  449. if( pDoc != NULL )
  450. {
  451. FOCUS_STATE nFocus = pDoc->GetLastRegisteredFocus( );
  452. if( nFocus == TREE_VIEW )
  453. {
  454. ODS( L"from tree to tab\n" );
  455. nMaxTab = m_pTabs->GetItemCount( );
  456. m_pTabs->SetCurSel( nMaxTab - 1 );
  457. OnChangePage( 0 , 0 );
  458. m_pTabs->SetFocus( );
  459. m_pTabs->SetCurFocus( nMaxTab - 1 );
  460. pDoc->RegisterLastFocus( TAB_CTRL );
  461. }
  462. else
  463. {
  464. nTab = m_pTabs->GetCurSel();
  465. nMaxTab = m_pTabs->GetItemCount( );
  466. if( nTab > 0 )
  467. {
  468. ODS( L" ...next tab...\n" );
  469. m_pTabs->SetCurSel( nTab - 1 );
  470. OnChangePage( 0 , 0 );
  471. m_pTabs->SetFocus( );
  472. m_pTabs->SetCurFocus( nTab - 1 );
  473. }
  474. else
  475. {
  476. ODS( L"...back to treeview\n" );
  477. CFrameWnd *p = (CFrameWnd*)pDoc->GetMainWnd();
  478. p->SendMessage( WM_FORCE_TREEVIEW_FOCUS , 0 , 0 );
  479. pDoc->RegisterLastFocus( TREE_VIEW );
  480. }
  481. }
  482. pDoc->SetPrevFocus( nFocus );
  483. }
  484. }
  485. return 0;
  486. }
  487. //=----------------------------------------------------------------------------
  488. // When the user hits F6 we need to switch between pains
  489. LRESULT CWinStationView::OnNextPane( WPARAM , LPARAM )
  490. {
  491. ODS( L"CWinStationView::OnNextPane\n" );
  492. int nTab;
  493. int nMaxTab;
  494. if( m_pTabs != NULL )
  495. {
  496. CWinAdminDoc *pDoc = (CWinAdminDoc*)GetDocument();
  497. if( pDoc != NULL )
  498. {
  499. FOCUS_STATE nFocus = pDoc->GetLastRegisteredFocus( );
  500. FOCUS_STATE nPrevFocus = pDoc->GetPrevFocus( );
  501. if( nFocus == TREE_VIEW )
  502. {
  503. if( nPrevFocus == TAB_CTRL )
  504. {
  505. nTab = m_pTabs->GetCurSel();
  506. m_pTabs->SetFocus( );
  507. m_pTabs->SetCurFocus( nTab );
  508. pDoc->RegisterLastFocus( TAB_CTRL );
  509. }
  510. else
  511. {
  512. pages[ m_CurrPage ].m_pPage->SetFocus( );
  513. pDoc->RegisterLastFocus( PAGED_ITEM );
  514. }
  515. }
  516. else
  517. {
  518. CFrameWnd *p = (CFrameWnd*)pDoc->GetMainWnd();
  519. p->SendMessage( WM_FORCE_TREEVIEW_FOCUS , 0 , 0 );
  520. pDoc->RegisterLastFocus( TREE_VIEW );
  521. }
  522. pDoc->SetPrevFocus( nFocus );
  523. }
  524. }
  525. return 0;
  526. }