QueueController.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697
  1. //---------------------------------------------------------------------------
  2. #include <WinPCH.h>
  3. #pragma hdrstop
  4. #include <Queue.h>
  5. #include "QueueController.h"
  6. //---------------------------------------------------------------------------
  7. __fastcall TQueueController::TQueueController(TCustomListView * ListView)
  8. {
  9. FListView = static_cast<TListView *>(ListView);
  10. DebugAssert(FListView != NULL);
  11. DebugAssert(FListView->OnDblClick == NULL);
  12. FListView->OnDblClick = QueueViewDblClick;
  13. DebugAssert(FListView->OnKeyDown == NULL);
  14. FListView->OnKeyDown = QueueViewKeyDown;
  15. DebugAssert(FListView->OnCustomDrawItem == NULL);
  16. FListView->OnCustomDrawItem = QueueViewCustomDrawItem;
  17. FQueueStatus = NULL;
  18. FOnChange = NULL;
  19. RememberConfiguration();
  20. }
  21. //---------------------------------------------------------------------------
  22. __fastcall TQueueController::~TQueueController()
  23. {
  24. DebugAssert(FListView->OnDblClick == QueueViewDblClick);
  25. FListView->OnDblClick = NULL;
  26. DebugAssert(FListView->OnKeyDown == QueueViewKeyDown);
  27. FListView->OnKeyDown = NULL;
  28. DebugAssert(FListView->OnCustomDrawItem == QueueViewCustomDrawItem);
  29. FListView->OnCustomDrawItem = NULL;
  30. }
  31. //---------------------------------------------------------------------------
  32. TQueueItemProxy * __fastcall TQueueController::QueueViewItemToQueueItem(TListItem * Item)
  33. {
  34. // previously this method was based on ActiveCount and DoneCount,
  35. // as if we were inconfident about validity of Item->Data pointers,
  36. // not sure why
  37. return static_cast<TQueueItemProxy *>(Item->Data);
  38. }
  39. //---------------------------------------------------------------------------
  40. TQueueOperation __fastcall TQueueController::DefaultOperation()
  41. {
  42. TQueueItemProxy * QueueItem;
  43. if (FListView->ItemFocused != NULL)
  44. {
  45. QueueItem = QueueViewItemToQueueItem(FListView->ItemFocused);
  46. switch (QueueItem->Status)
  47. {
  48. case TQueueItem::qsPending:
  49. return qoItemExecute;
  50. case TQueueItem::qsQuery:
  51. return qoItemQuery;
  52. case TQueueItem::qsError:
  53. return qoItemError;
  54. case TQueueItem::qsPrompt:
  55. return qoItemPrompt;
  56. case TQueueItem::qsProcessing:
  57. return qoItemPause;
  58. case TQueueItem::qsPaused:
  59. return qoItemResume;
  60. }
  61. }
  62. return qoNone;
  63. }
  64. //---------------------------------------------------------------------------
  65. bool __fastcall TQueueController::AllowOperation(
  66. TQueueOperation Operation, unsigned long * Param)
  67. {
  68. TQueueItemProxy * QueueItem = NULL;
  69. if (FListView->ItemFocused != NULL)
  70. {
  71. QueueItem = QueueViewItemToQueueItem(FListView->ItemFocused);
  72. }
  73. switch (Operation)
  74. {
  75. case qoItemUserAction:
  76. return (QueueItem != NULL) && TQueueItem::IsUserActionStatus(QueueItem->Status);
  77. case qoItemQuery:
  78. return (QueueItem != NULL) && (QueueItem->Status == TQueueItem::qsQuery);
  79. case qoItemError:
  80. return (QueueItem != NULL) && (QueueItem->Status == TQueueItem::qsError);
  81. case qoItemPrompt:
  82. return (QueueItem != NULL) && (QueueItem->Status == TQueueItem::qsPrompt);
  83. case qoItemDelete:
  84. return (QueueItem != NULL);
  85. case qoItemExecute:
  86. return (QueueItem != NULL) && (QueueItem->Status == TQueueItem::qsPending);
  87. case qoItemUp:
  88. return (QueueItem != NULL) &&
  89. (QueueItem->Status == TQueueItem::qsPending) &&
  90. // it's not first pending item,
  91. // this is based on assumption that pending items occupy single line always
  92. (FListView->ItemFocused->Index > 0) &&
  93. (QueueViewItemToQueueItem(FListView->Items->Item[FListView->ItemFocused->Index - 1])->Status == TQueueItem::qsPending);
  94. case qoItemDown:
  95. return (QueueItem != NULL) &&
  96. (QueueItem->Status == TQueueItem::qsPending) &&
  97. (FListView->ItemFocused->Index < (FListView->Items->Count - 1));
  98. case qoItemPause:
  99. return (QueueItem != NULL) &&
  100. (QueueItem->Status == TQueueItem::qsProcessing);
  101. case qoItemResume:
  102. return (QueueItem != NULL) &&
  103. (QueueItem->Status == TQueueItem::qsPaused);
  104. case qoItemSpeed:
  105. {
  106. bool Result =
  107. (QueueItem != NULL) && (QueueItem->Status != TQueueItem::qsDone) &&
  108. TFileOperationProgressType::IsTransferOperation(QueueItem->Info->Operation);
  109. if (Result && (Param != NULL))
  110. {
  111. Result = QueueItem->GetCPSLimit(*Param);
  112. }
  113. return Result;
  114. }
  115. case qoPauseAll:
  116. case qoResumeAll:
  117. {
  118. TQueueItem::TStatus Status =
  119. (Operation == qoPauseAll) ? TQueueItem::qsProcessing : TQueueItem::qsPaused;
  120. bool Result = false;
  121. // can be NULL when action update is triggered while disconnecting
  122. if (FQueueStatus != NULL)
  123. {
  124. for (int i = FQueueStatus->DoneCount; !Result && (i < FQueueStatus->DoneAndActiveCount); i++)
  125. {
  126. QueueItem = FQueueStatus->Items[i];
  127. Result = (QueueItem->Status == Status);
  128. }
  129. }
  130. return Result;
  131. }
  132. case qoDeleteAllDone:
  133. return (FQueueStatus != NULL) && (FQueueStatus->DoneCount > 0);
  134. case qoDeleteAll:
  135. return (FQueueStatus != NULL) && (FQueueStatus->Count > 0);
  136. default:
  137. DebugFail();
  138. return false;
  139. }
  140. }
  141. //---------------------------------------------------------------------------
  142. void __fastcall TQueueController::ExecuteOperation(TQueueOperation Operation,
  143. unsigned long Param)
  144. {
  145. TQueueItemProxy * QueueItem = NULL;
  146. if (FListView->ItemFocused != NULL)
  147. {
  148. QueueItem = QueueViewItemToQueueItem(FListView->ItemFocused);
  149. }
  150. switch (Operation)
  151. {
  152. case qoItemUserAction:
  153. case qoItemQuery:
  154. case qoItemError:
  155. case qoItemPrompt:
  156. if (QueueItem != NULL)
  157. {
  158. QueueItem->ProcessUserAction();
  159. }
  160. break;
  161. case qoItemExecute:
  162. if (QueueItem != NULL)
  163. {
  164. QueueItem->ExecuteNow();
  165. }
  166. break;
  167. case qoItemUp:
  168. case qoItemDown:
  169. if (QueueItem != NULL)
  170. {
  171. QueueItem->Move(Operation == qoItemUp);
  172. }
  173. break;
  174. case qoItemDelete:
  175. if (QueueItem != NULL)
  176. {
  177. QueueItem->Delete();
  178. }
  179. break;
  180. case qoItemPause:
  181. if (QueueItem != NULL)
  182. {
  183. QueueItem->Pause();
  184. }
  185. break;
  186. case qoItemResume:
  187. if (QueueItem != NULL)
  188. {
  189. QueueItem->Resume();
  190. }
  191. break;
  192. case qoItemSpeed:
  193. if (QueueItem != NULL)
  194. {
  195. QueueItem->SetCPSLimit(Param);
  196. }
  197. break;
  198. case qoPauseAll:
  199. case qoResumeAll:
  200. {
  201. for (int i = FQueueStatus->DoneCount; i < FQueueStatus->DoneAndActiveCount; i++)
  202. {
  203. QueueItem = FQueueStatus->Items[i];
  204. if ((Operation == qoPauseAll) && (QueueItem->Status == TQueueItem::qsProcessing))
  205. {
  206. QueueItem->Pause();
  207. }
  208. else if ((Operation == qoResumeAll) && (QueueItem->Status == TQueueItem::qsPaused))
  209. {
  210. QueueItem->Resume();
  211. }
  212. }
  213. }
  214. break;
  215. case qoDeleteAllDone:
  216. case qoDeleteAll:
  217. {
  218. int Count = (Operation == qoDeleteAll) ? FQueueStatus->Count : FQueueStatus->DoneCount;
  219. for (int i = 0; i < Count; i++)
  220. {
  221. QueueItem = FQueueStatus->Items[i];
  222. QueueItem->Delete();
  223. }
  224. }
  225. break;
  226. default:
  227. DebugFail();
  228. break;
  229. }
  230. }
  231. //---------------------------------------------------------------------------
  232. static UnicodeString GetTime(TFileOperationProgressType * ProgressData)
  233. {
  234. UnicodeString Result;
  235. if (ProgressData->TotalSizeSet)
  236. {
  237. Result = FormatDateTimeSpan(ProgressData->TotalTimeLeft());
  238. }
  239. else
  240. {
  241. Result = FormatDateTimeSpan(ProgressData->TimeElapsed());
  242. }
  243. return Result;
  244. }
  245. //---------------------------------------------------------------------------
  246. static UnicodeString GetOverallProgress(TFileOperationProgressType * ProgressData)
  247. {
  248. return FORMAT(L"%d%%", (ProgressData->OverallProgress()));
  249. }
  250. //---------------------------------------------------------------------------
  251. void __fastcall TQueueController::FillQueueViewItem(TListItem * Item,
  252. TQueueItemProxy * QueueItem, bool Detail, bool OnlyLine)
  253. {
  254. DebugAssert(!Detail || (QueueItem->Status != TQueueItem::qsPending));
  255. DebugAssert((Item->Data == NULL) || (Item->Data == QueueItem));
  256. Item->Data = QueueItem;
  257. UnicodeString ProgressStr;
  258. int Image = -1;
  259. switch (QueueItem->Status)
  260. {
  261. case TQueueItem::qsDone:
  262. ProgressStr = LoadStr(QUEUE_DONE);
  263. break;
  264. case TQueueItem::qsPending:
  265. ProgressStr = LoadStr(QUEUE_PENDING);
  266. break;
  267. case TQueueItem::qsConnecting:
  268. ProgressStr = LoadStr(QUEUE_CONNECTING);
  269. break;
  270. case TQueueItem::qsQuery:
  271. ProgressStr = LoadStr(QUEUE_QUERY);
  272. Image = 4;
  273. break;
  274. case TQueueItem::qsError:
  275. ProgressStr = LoadStr(QUEUE_ERROR);
  276. Image = 5;
  277. break;
  278. case TQueueItem::qsPrompt:
  279. ProgressStr = LoadStr(QUEUE_PROMPT);
  280. Image = 6;
  281. break;
  282. case TQueueItem::qsPaused:
  283. ProgressStr = LoadStr(QUEUE_PAUSED);
  284. Image = 7;
  285. break;
  286. }
  287. bool BlinkHide = QueueItemNeedsFrequentRefresh(QueueItem) &&
  288. !QueueItem->ProcessingUserAction &&
  289. ((GetTickCount() % MSecsPerSec) >= (MSecsPerSec/2));
  290. int State = -1;
  291. UnicodeString Values[6];
  292. TFileOperationProgressType * ProgressData = QueueItem->ProgressData;
  293. TQueueItem::TInfo * Info = QueueItem->Info;
  294. if (SimpleOperation(QueueItem))
  295. {
  296. DebugAssert(!Detail && Info->Primary);
  297. State = 8;
  298. if ((ProgressData != NULL) && (QueueItem->Status != TQueueItem::qsDone))
  299. {
  300. Values[0] = ProgressData->FileName;
  301. Values[3] = GetTime(ProgressData);
  302. if (ProgressStr.IsEmpty())
  303. {
  304. if (ProgressData->Count > 1)
  305. {
  306. ProgressStr = GetOverallProgress(ProgressData);
  307. }
  308. else
  309. {
  310. ProgressStr = LoadStr(QUEUE_DELETING);
  311. }
  312. }
  313. }
  314. else
  315. {
  316. Values[0] = Info->Source;
  317. }
  318. Values[5] = ProgressStr;
  319. }
  320. else if (!Detail && Info->Primary)
  321. {
  322. switch (Info->Operation)
  323. {
  324. case foCopy:
  325. State = ((Info->Side == osLocal) ? 2 : 0);
  326. break;
  327. case foMove:
  328. State = ((Info->Side == osLocal) ? 3 : 1);
  329. break;
  330. default:
  331. DebugFail();
  332. }
  333. if (!OnlyLine)
  334. {
  335. Image = -1;
  336. ProgressStr = L"";
  337. }
  338. // If both are empty, it's bootstrap item => do not show anything
  339. if (!Info->Source.IsEmpty() || !Info->Destination.IsEmpty())
  340. {
  341. // cannot use ProgressData->Temp as it is set only after the transfer actually starts
  342. Values[0] = Info->Source.IsEmpty() ? LoadStr(PROGRESS_TEMP_DIR) : Info->Source;
  343. Values[1] = Info->Destination.IsEmpty() ? LoadStr(PROGRESS_TEMP_DIR) : Info->Destination;
  344. }
  345. __int64 TotalTransferred = QueueItem->TotalTransferred;
  346. if (TotalTransferred >= 0)
  347. {
  348. Values[2] =
  349. FormatPanelBytes(TotalTransferred, WinConfiguration->FormatSizeBytes);
  350. }
  351. if (ProgressData != NULL)
  352. {
  353. if (ProgressData->Operation == Info->Operation)
  354. {
  355. if (QueueItem->Status != TQueueItem::qsDone)
  356. {
  357. Values[3] = GetTime(ProgressData);
  358. Values[4] = FORMAT(L"%s/s", (FormatBytes(ProgressData->CPS())));
  359. }
  360. if (ProgressStr.IsEmpty())
  361. {
  362. ProgressStr = GetOverallProgress(ProgressData);
  363. }
  364. }
  365. else if (ProgressData->Operation == foCalculateSize)
  366. {
  367. ProgressStr = LoadStr(QUEUE_LISTING);
  368. }
  369. }
  370. Values[5] = ProgressStr;
  371. }
  372. else
  373. {
  374. if (ProgressData != NULL)
  375. {
  376. if ((Info->Side == osRemote) || !ProgressData->Temp)
  377. {
  378. Values[0] = ProgressData->FileName;
  379. }
  380. else
  381. {
  382. Values[0] = ExtractFileName(ProgressData->FileName);
  383. }
  384. if (ProgressData->Operation == Info->Operation)
  385. {
  386. Values[2] =
  387. FormatPanelBytes(ProgressData->TransferredSize, WinConfiguration->FormatSizeBytes);
  388. if (ProgressStr.IsEmpty())
  389. {
  390. ProgressStr = FORMAT(L"%d%%", (ProgressData->TransferProgress()));
  391. }
  392. }
  393. }
  394. Values[5] = ProgressStr;
  395. }
  396. Item->StateIndex = (!BlinkHide ? State : -1);
  397. Item->ImageIndex = (!BlinkHide ? Image : -1);
  398. for (size_t Index = 0; Index < LENOF(Values); Index++)
  399. {
  400. if (Index < static_cast<size_t>(Item->SubItems->Count))
  401. {
  402. Item->SubItems->Strings[Index] = Values[Index];
  403. }
  404. else
  405. {
  406. Item->SubItems->Add(Values[Index]);
  407. }
  408. }
  409. }
  410. //---------------------------------------------------------------------------
  411. TListItem * __fastcall TQueueController::InsertItemFor(TQueueItemProxy * QueueItem, int Index)
  412. {
  413. TListItem * Item;
  414. if (Index == FListView->Items->Count)
  415. {
  416. Item = FListView->Items->Add();
  417. }
  418. else if (FListView->Items->Item[Index]->Data != QueueItem)
  419. {
  420. Item = FListView->Items->Insert(Index);
  421. }
  422. else
  423. {
  424. Item = FListView->Items->Item[Index];
  425. DebugAssert(Item->Data == QueueItem);
  426. }
  427. return Item;
  428. }
  429. //---------------------------------------------------------------------------
  430. void __fastcall TQueueController::UpdateQueueStatus(
  431. TTerminalQueueStatus * QueueStatus)
  432. {
  433. FQueueStatus = QueueStatus;
  434. if (FQueueStatus != NULL)
  435. {
  436. TQueueItemProxy * QueueItem;
  437. TListItem * Item;
  438. int Index = 0;
  439. for (int ItemIndex = 0; ItemIndex < FQueueStatus->Count; ItemIndex++)
  440. {
  441. QueueItem = FQueueStatus->Items[ItemIndex];
  442. int Index2 = Index;
  443. while ((Index2 < FListView->Items->Count) &&
  444. (FListView->Items->Item[Index2]->Data != QueueItem))
  445. {
  446. Index2++;
  447. }
  448. if (Index2 < FListView->Items->Count)
  449. {
  450. while (Index < Index2)
  451. {
  452. FListView->Items->Delete(Index);
  453. Index2--;
  454. }
  455. }
  456. Item = InsertItemFor(QueueItem, Index);
  457. bool HasDetailsLine = UseDetailsLine(ItemIndex, QueueItem);
  458. FillQueueViewItem(Item, QueueItem, false, !HasDetailsLine);
  459. Index++;
  460. DebugAssert((QueueItem->Status != TQueueItem::qsPending) ==
  461. (ItemIndex < FQueueStatus->DoneAndActiveCount));
  462. if (HasDetailsLine)
  463. {
  464. Item = InsertItemFor(QueueItem, Index);
  465. FillQueueViewItem(Item, QueueItem, true, false);
  466. Index++;
  467. }
  468. }
  469. while (Index < FListView->Items->Count)
  470. {
  471. FListView->Items->Delete(Index);
  472. }
  473. }
  474. else
  475. {
  476. FListView->Items->Clear();
  477. }
  478. DoChange();
  479. }
  480. //---------------------------------------------------------------------------
  481. bool TQueueController::SimpleOperation(TQueueItemProxy * QueueItem)
  482. {
  483. return (QueueItem->Info->Operation == foDelete); // basically any non-transfer, but we support delete now only
  484. }
  485. //---------------------------------------------------------------------------
  486. bool __fastcall TQueueController::UseDetailsLine(int ItemIndex, TQueueItemProxy * QueueItem)
  487. {
  488. return
  489. (ItemIndex >= FQueueStatus->DoneCount) &&
  490. (ItemIndex < FQueueStatus->DoneAndActiveCount) &&
  491. QueueItem->Info->Primary &&
  492. !QueueItem->Info->SingleFile &&
  493. !SimpleOperation(QueueItem) &&
  494. ((QueueItem->ProgressData == NULL) || !QueueItem->ProgressData->Done);
  495. }
  496. //---------------------------------------------------------------------------
  497. void __fastcall TQueueController::RefreshQueueItem(TQueueItemProxy * QueueItem)
  498. {
  499. TListItem * NextListItem = NULL;
  500. TListItem * ListItem;
  501. ListItem = FListView->FindData(0, QueueItem, true, false);
  502. DebugAssert(ListItem != NULL);
  503. int Index = ListItem->Index;
  504. if (Index + 1 < FListView->Items->Count)
  505. {
  506. NextListItem = FListView->Items->Item[Index + 1];
  507. if (NextListItem->Data != QueueItem)
  508. {
  509. NextListItem = NULL;
  510. }
  511. }
  512. bool HasDetailsLine = UseDetailsLine(QueueItem->Index, QueueItem);
  513. FillQueueViewItem(ListItem, QueueItem, false, !HasDetailsLine);
  514. if (HasDetailsLine)
  515. {
  516. if (NextListItem == NULL)
  517. {
  518. NextListItem = FListView->Items->Insert(Index + 1);
  519. }
  520. FillQueueViewItem(NextListItem, QueueItem, true, false);
  521. }
  522. else
  523. {
  524. if (NextListItem != NULL)
  525. {
  526. NextListItem->Delete();
  527. }
  528. }
  529. DoChange();
  530. }
  531. //---------------------------------------------------------------------------
  532. bool __fastcall TQueueController::QueueItemNeedsFrequentRefresh(
  533. TQueueItemProxy * QueueItem)
  534. {
  535. return
  536. (TQueueItem::IsUserActionStatus(QueueItem->Status) ||
  537. (QueueItem->Status == TQueueItem::qsPaused));
  538. }
  539. //---------------------------------------------------------------------------
  540. void __fastcall TQueueController::DoChange()
  541. {
  542. if (FOnChange != NULL)
  543. {
  544. FOnChange(NULL);
  545. }
  546. }
  547. //---------------------------------------------------------------------------
  548. void __fastcall TQueueController::QueueViewDblClick(TObject * /*Sender*/)
  549. {
  550. TQueueOperation Operation = DefaultOperation();
  551. if (Operation != qoNone)
  552. {
  553. ExecuteOperation(Operation);
  554. }
  555. }
  556. //---------------------------------------------------------------------------
  557. void __fastcall TQueueController::QueueViewKeyDown(TObject * /*Sender*/,
  558. WORD & Key, TShiftState /*Shift*/)
  559. {
  560. if (Key == VK_RETURN)
  561. {
  562. TQueueOperation Operation = DefaultOperation();
  563. if (Operation != qoNone)
  564. {
  565. ExecuteOperation(Operation);
  566. }
  567. Key = 0;
  568. }
  569. else if (Key == VK_DELETE)
  570. {
  571. ExecuteOperation(qoItemDelete);
  572. Key = 0;
  573. }
  574. }
  575. //---------------------------------------------------------------------------
  576. void __fastcall TQueueController::QueueViewCustomDrawItem(TCustomListView * Sender,
  577. TListItem * Item, TCustomDrawState /*State*/, bool & /*DefaultDraw*/)
  578. {
  579. TQueueItemProxy * QueueItem = QueueViewItemToQueueItem(Item);
  580. if (QueueItem->Status == TQueueItem::qsDone)
  581. {
  582. Sender->Canvas->Font->Color = clGrayText;
  583. }
  584. }
  585. //---------------------------------------------------------------------------
  586. bool __fastcall TQueueController::GetEmpty()
  587. {
  588. return (FQueueStatus == NULL) || (FQueueStatus->Count == 0);
  589. }
  590. //---------------------------------------------------------------------------
  591. void __fastcall TQueueController::RememberConfiguration()
  592. {
  593. FFormatSizeBytes = WinConfiguration->FormatSizeBytes;
  594. }
  595. //---------------------------------------------------------------------------
  596. bool __fastcall TQueueController::NeedRefresh()
  597. {
  598. bool Result = (WinConfiguration->FormatSizeBytes != FFormatSizeBytes);
  599. RememberConfiguration();
  600. return Result;
  601. }
  602. //---------------------------------------------------------------------------
  603. TQueueItemProxy * __fastcall TQueueController::GetFocusedPrimaryItem()
  604. {
  605. TQueueItemProxy * Result = NULL;
  606. TListItem * PrimaryItemOfFocused = FListView->ItemFocused;
  607. if (PrimaryItemOfFocused != NULL)
  608. {
  609. while (!QueueViewItemToQueueItem(PrimaryItemOfFocused)->Info->Primary &&
  610. DebugAlwaysTrue(PrimaryItemOfFocused->Index > 0))
  611. {
  612. PrimaryItemOfFocused = FListView->Items->Item[PrimaryItemOfFocused->Index - 1];
  613. }
  614. Result = QueueViewItemToQueueItem(PrimaryItemOfFocused);
  615. }
  616. return Result;
  617. }