FileFind.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615
  1. //---------------------------------------------------------------------------
  2. #include <vcl.h>
  3. #pragma hdrstop
  4. #include <Common.h>
  5. #include <WinInterface.h>
  6. #include <VCLCommon.h>
  7. #include <TextsWin.h>
  8. #include <WinConfiguration.h>
  9. #include <CoreMain.h>
  10. #include <Tools.h>
  11. #include <BaseUtils.hpp>
  12. #include <Terminal.h>
  13. #include "FileFind.h"
  14. //---------------------------------------------------------------------------
  15. #pragma package(smart_init)
  16. #pragma link "HistoryComboBox"
  17. #pragma link "IEListView"
  18. #pragma link "NortonLikeListView"
  19. #pragma link "PngImageList"
  20. #ifndef NO_RESOURCES
  21. #pragma resource "*.dfm"
  22. #endif
  23. //---------------------------------------------------------------------------
  24. TFileFindDialog * FileFindDialog = NULL;
  25. //---------------------------------------------------------------------------
  26. void __fastcall ShowFileFindDialog(
  27. TTerminal * Terminal, UnicodeString Directory, TFindEvent OnFind, TFocusFileEvent OnFocusFile,
  28. TFileListOperationEvent OnDeleteFiles, TFileListOperationEvent OnDownloadFiles)
  29. {
  30. if (FileFindDialog == NULL)
  31. {
  32. FileFindDialog = new TFileFindDialog(Application);
  33. }
  34. FileFindDialog->Init(Terminal, Directory, OnFind, OnFocusFile, OnDeleteFiles, OnDownloadFiles);
  35. FileFindDialog->Show();
  36. }
  37. //---------------------------------------------------------------------------
  38. void __fastcall HideFileFindDialog()
  39. {
  40. if (FileFindDialog != NULL)
  41. {
  42. delete FileFindDialog;
  43. }
  44. }
  45. //---------------------------------------------------------------------------
  46. __fastcall TFileFindDialog::TFileFindDialog(TComponent * Owner)
  47. : TForm(Owner)
  48. {
  49. UseSystemSettings(this);
  50. FState = ffInit;
  51. FixComboBoxResizeBug(MaskEdit);
  52. FixComboBoxResizeBug(RemoteDirectoryEdit);
  53. HintLabel(MaskHintText,
  54. FORMAT(L"%s\n \n%s\n \n%s\n \n%s\n \n%s\n \n%s", (LoadStr(MASK_HINT2),
  55. LoadStr(FILE_MASK_EX_HINT), LoadStr(COMBINING_MASKS_HINT),
  56. LoadStr(PATH_MASK_HINT2), LoadStr(DIRECTORY_MASK_HINT),
  57. LoadStr(MASK_HELP))));
  58. FSystemImageList = SharedSystemImageList(false);
  59. FileView->SmallImages = FSystemImageList;
  60. FileView->ShowColumnIcon = false;
  61. UseDesktopFont(FileView);
  62. UseDesktopFont(StatusBar);
  63. FFrameAnimation.Init(AnimationPaintBox, L"Find");
  64. FixFormIcons(this);
  65. }
  66. //---------------------------------------------------------------------------
  67. __fastcall TFileFindDialog::~TFileFindDialog()
  68. {
  69. TFindFileConfiguration FormConfiguration = CustomWinConfiguration->FindFile;
  70. FormConfiguration.ListParams = FileView->ColProperties->ParamsStr;
  71. UnicodeString WindowParams = StoreFormSize(this);
  72. // this is particularly to prevent saving the form state
  73. // for the first time, keeping default positioning by a system
  74. if (!FWindowParams.IsEmpty() && (FWindowParams != WindowParams))
  75. {
  76. FormConfiguration.WindowParams = WindowParams;
  77. }
  78. CustomWinConfiguration->FindFile = FormConfiguration;
  79. Clear();
  80. delete FSystemImageList;
  81. DebugAssert(FileFindDialog == this);
  82. FileFindDialog = NULL;
  83. }
  84. //---------------------------------------------------------------------------
  85. bool __fastcall TFileFindDialog::IsFinding()
  86. {
  87. return (FState == ffFinding) || (FState == ffAborting);
  88. }
  89. //---------------------------------------------------------------------------
  90. void __fastcall TFileFindDialog::UpdateControls()
  91. {
  92. bool Finding = IsFinding();
  93. Caption = FORMAT("%s - %s", (LoadStr(Finding ? FIND_FILE_FINDING : FIND_FILE_TITLE), FTerminalName));
  94. UnicodeString StartStopCaption;
  95. if (Finding)
  96. {
  97. EnableControl(StartStopButton, true);
  98. StartStopCaption = LoadStr(FIND_FILE_STOP);
  99. }
  100. else
  101. {
  102. EnableControl(StartStopButton, !RemoteDirectoryEdit->Text.IsEmpty());
  103. StartStopCaption = LoadStr(FIND_FILE_START);
  104. }
  105. StartStopButton->Caption = StartStopCaption;
  106. EnableControl(FilterGroup, !Finding);
  107. FocusAction->Enabled = (FileView->ItemFocused != NULL);
  108. bool EnableFileOperations = !Finding && (FileView->SelCount > 0);
  109. DeleteAction->Enabled = EnableFileOperations;
  110. DownloadAction->Enabled = EnableFileOperations;
  111. CopyAction->Enabled = (FileView->Items->Count > 0);
  112. SelectAllAction->Enabled = (FileView->SelCount < FileView->Items->Count);
  113. switch (FState)
  114. {
  115. case ffInit:
  116. StatusBar->SimpleText = L"";
  117. break;
  118. case ffFinding:
  119. case ffAborting:
  120. if (!FFindingInDirectory.IsEmpty())
  121. {
  122. StatusBar->SimpleText = FMTLOAD(FIND_FILE_IN_DIRECTORY, (FFindingInDirectory));
  123. }
  124. else
  125. {
  126. StatusBar->SimpleText = L"";
  127. }
  128. break;
  129. case ffAborted:
  130. StatusBar->SimpleText = LoadStr(FIND_FILE_ABORTED);
  131. break;
  132. case ffDone:
  133. StatusBar->SimpleText = LoadStr(FIND_FILE_DONE);
  134. break;
  135. default:
  136. DebugFail();
  137. break;
  138. }
  139. FocusButton->Default = FileView->Focused() && (FState != ffInit);
  140. StartStopButton->Default = !FocusButton->Default;
  141. }
  142. //---------------------------------------------------------------------------
  143. void __fastcall TFileFindDialog::ControlChange(TObject * /*Sender*/)
  144. {
  145. UpdateControls();
  146. }
  147. //---------------------------------------------------------------------------
  148. void __fastcall TFileFindDialog::Init(
  149. TTerminal * Terminal, UnicodeString Directory, TFindEvent OnFind, TFocusFileEvent OnFocusFile,
  150. TFileListOperationEvent OnDeleteFiles, TFileListOperationEvent OnDownloadFiles)
  151. {
  152. if (FTerminal != Terminal)
  153. {
  154. FTerminal = Terminal;
  155. FTerminalName = Terminal->SessionData->SessionName;
  156. Clear();
  157. FState = ffInit;
  158. ActiveControl = MaskEdit;
  159. }
  160. FOnFind = OnFind;
  161. FOnFocusFile = OnFocusFile;
  162. FOnDeleteFiles = OnDeleteFiles;
  163. FOnDownloadFiles = OnDownloadFiles;
  164. MaskEdit->Text = WinConfiguration->SelectMask;
  165. RemoteDirectoryEdit->Text = UnixExcludeTrailingBackslash(Directory);
  166. UpdateControls();
  167. }
  168. //---------------------------------------------------------------------------
  169. void __fastcall TFileFindDialog::CreateParams(TCreateParams & Params)
  170. {
  171. TForm::CreateParams(Params);
  172. Params.WndParent = GetDesktopWindow();
  173. }
  174. //---------------------------------------------------------------------------
  175. void __fastcall TFileFindDialog::ClearItem(TListItem * Item)
  176. {
  177. TRemoteFile * File = static_cast<TRemoteFile *>(Item->Data);
  178. Item->Data = NULL;
  179. delete File;
  180. }
  181. //---------------------------------------------------------------------------
  182. void __fastcall TFileFindDialog::Clear()
  183. {
  184. for (int Index = 0; Index < FileView->Items->Count; Index++)
  185. {
  186. ClearItem(FileView->Items->Item[Index]);
  187. }
  188. FileView->Items->Clear();
  189. }
  190. //---------------------------------------------------------------------------
  191. void __fastcall TFileFindDialog::Start()
  192. {
  193. if (MaskEdit->Focused())
  194. {
  195. MaskEditExit(NULL);
  196. }
  197. RemoteDirectoryEdit->SaveToHistory();
  198. CustomWinConfiguration->History[L"RemoteDirectory"] = RemoteDirectoryEdit->Items;
  199. MaskEdit->SaveToHistory();
  200. WinConfiguration->History[L"Mask"] = MaskEdit->Items;
  201. WinConfiguration->SelectMask = MaskEdit->Text;
  202. DebugAssert(FState != ffFinding);
  203. FState = ffFinding;
  204. try
  205. {
  206. FFrameAnimation.Start();
  207. UpdateControls();
  208. Repaint();
  209. TOperationVisualizer Visualizer;
  210. DebugAssert(FOnFind != NULL);
  211. UnicodeString Directory = UnixExcludeTrailingBackslash(RemoteDirectoryEdit->Text);
  212. FDirectory = Directory;
  213. if (FDirectory == ROOTDIRECTORY)
  214. {
  215. FDirectory = UnicodeString();
  216. }
  217. FOnFind(FTerminal, Directory, MaskEdit->Text, FileFound, FindingFile);
  218. }
  219. __finally
  220. {
  221. FFindingInDirectory = L"";
  222. if (FState == ffFinding)
  223. {
  224. FState = ffDone;
  225. }
  226. if (FState == ffAborting)
  227. {
  228. FState = ffAborted;
  229. }
  230. FFrameAnimation.Stop();
  231. if (WindowState == wsMinimized)
  232. {
  233. ShowNotification(
  234. NULL, MainInstructions(LoadStr(BALLOON_OPERATION_COMPLETE)),
  235. qtInformation);
  236. }
  237. if (!FFocusPath.IsEmpty())
  238. {
  239. UnicodeString FocusPath = FFocusPath;
  240. FFocusPath = L"";
  241. DoFocusFile(FocusPath);
  242. }
  243. UpdateControls();
  244. }
  245. }
  246. //---------------------------------------------------------------------------
  247. void __fastcall TFileFindDialog::FileFound(TTerminal * /*Terminal*/,
  248. const UnicodeString FileName, const TRemoteFile * AFile, bool & Cancel)
  249. {
  250. TListItem * Item = FileView->Items->Add();
  251. TRemoteFile * File = AFile->Duplicate(true);
  252. Item->Data = File;
  253. Item->ImageIndex = File->IconIndex;
  254. UnicodeString Caption = File->FileName;
  255. if (File->IsDirectory)
  256. {
  257. Caption = UnixIncludeTrailingBackslash(Caption);
  258. }
  259. Item->Caption = Caption;
  260. UnicodeString Directory = UnixExtractFilePath(File->FullFileName);
  261. if (AnsiSameText(FDirectory, Directory.SubString(1, FDirectory.Length())))
  262. {
  263. Directory = L"." + Directory.SubString(FDirectory.Length() + 1, Directory.Length() - FDirectory.Length());
  264. }
  265. else
  266. {
  267. DebugFail();
  268. }
  269. Item->SubItems->Add(Directory);
  270. if (File->IsDirectory)
  271. {
  272. Item->SubItems->Add(L"");
  273. }
  274. else
  275. {
  276. Item->SubItems->Add(
  277. FormatPanelBytes(File->Size, WinConfiguration->FormatSizeBytes));
  278. }
  279. Item->SubItems->Add(UserModificationStr(File->Modification, File->ModificationFmt));
  280. UpdateControls();
  281. Cancel = (FState == ffAborting);
  282. Application->ProcessMessages();
  283. }
  284. //---------------------------------------------------------------------------
  285. void __fastcall TFileFindDialog::FindingFile(TTerminal * /*Terminal*/,
  286. const UnicodeString Directory, bool & Cancel)
  287. {
  288. if (!Directory.IsEmpty() && (FFindingInDirectory != Directory))
  289. {
  290. FFindingInDirectory = Directory;
  291. UpdateControls();
  292. }
  293. Cancel = (FState == ffAborting);
  294. Application->ProcessMessages();
  295. }
  296. //---------------------------------------------------------------------------
  297. void __fastcall TFileFindDialog::StartStopButtonClick(TObject * /*Sender*/)
  298. {
  299. if (IsFinding())
  300. {
  301. Stop();
  302. }
  303. else
  304. {
  305. Clear();
  306. if (ActiveControl->Parent == FilterGroup)
  307. {
  308. FileView->SetFocus();
  309. }
  310. Start();
  311. }
  312. }
  313. //---------------------------------------------------------------------------
  314. void __fastcall TFileFindDialog::StopButtonClick(TObject * /*Sender*/)
  315. {
  316. Stop();
  317. }
  318. //---------------------------------------------------------------------------
  319. void __fastcall TFileFindDialog::Stop()
  320. {
  321. FState = ffAborting;
  322. UpdateControls();
  323. }
  324. //---------------------------------------------------------------------------
  325. void __fastcall TFileFindDialog::FormShow(TObject * /*Sender*/)
  326. {
  327. InstallPathWordBreakProc(MaskEdit);
  328. InstallPathWordBreakProc(RemoteDirectoryEdit);
  329. // have to set history after value, to prevent autocompletition
  330. MaskEdit->Items = WinConfiguration->History[L"Mask"];
  331. RemoteDirectoryEdit->Items = CustomWinConfiguration->History[L"RemoteDirectory"];
  332. UpdateFormPosition(this, poOwnerFormCenter);
  333. RestoreFormSize(CustomWinConfiguration->FindFile.WindowParams, this);
  334. FileView->ColProperties->ParamsStr = CustomWinConfiguration->FindFile.ListParams;
  335. DebugAssert(FWindowParams.IsEmpty());
  336. if (FWindowParams.IsEmpty())
  337. {
  338. FWindowParams = StoreFormSize(this);
  339. }
  340. UpdateControls();
  341. }
  342. //---------------------------------------------------------------------------
  343. bool __fastcall TFileFindDialog::StopIfFinding()
  344. {
  345. bool Result = IsFinding();
  346. if (Result)
  347. {
  348. Stop();
  349. }
  350. return Result;
  351. }
  352. //---------------------------------------------------------------------------
  353. void __fastcall TFileFindDialog::FormCloseQuery(TObject * /*Sender*/,
  354. bool & /*CanClose*/)
  355. {
  356. StopIfFinding();
  357. }
  358. //---------------------------------------------------------------------------
  359. void __fastcall TFileFindDialog::HelpButtonClick(TObject * /*Sender*/)
  360. {
  361. FormHelp(this);
  362. }
  363. //---------------------------------------------------------------------------
  364. void __fastcall TFileFindDialog::Dispatch(void * Message)
  365. {
  366. TMessage * M = reinterpret_cast<TMessage*>(Message);
  367. if (M->Msg == CM_DIALOGKEY)
  368. {
  369. CMDialogKey(*((TWMKeyDown *)Message));
  370. }
  371. else
  372. {
  373. TForm::Dispatch(Message);
  374. }
  375. }
  376. //---------------------------------------------------------------------------
  377. void __fastcall TFileFindDialog::CMDialogKey(TWMKeyDown & Message)
  378. {
  379. // Handling VK_ESCAPE in FormKeyDown causes a beep when any "edit" has focus.
  380. // Moreover FormKeyDown is called when the the "esc" is pressed while drop down list is unrolled.
  381. if (Message.CharCode == VK_ESCAPE)
  382. {
  383. if (!StopIfFinding())
  384. {
  385. Close();
  386. }
  387. Message.Result = 1;
  388. return;
  389. }
  390. TForm::Dispatch(&Message);
  391. }
  392. //---------------------------------------------------------------------------
  393. void __fastcall TFileFindDialog::FormKeyDown(TObject * /*Sender*/, WORD & Key,
  394. TShiftState Shift)
  395. {
  396. if ((Key == L'C') && Shift.Contains(ssCtrl) &&
  397. (dynamic_cast<TCustomCombo *>(ActiveControl) == NULL))
  398. {
  399. CopyToClipboard();
  400. Key = 0;
  401. }
  402. else if ((Key == VK_F10) && Shift.Empty())
  403. {
  404. Key = 0;
  405. StopIfFinding();
  406. Close();
  407. }
  408. }
  409. //---------------------------------------------------------------------------
  410. void __fastcall TFileFindDialog::MaskEditExit(TObject * /*Sender*/)
  411. {
  412. ValidateMaskEdit(MaskEdit);
  413. }
  414. //---------------------------------------------------------------------------
  415. void __fastcall TFileFindDialog::DoFocusFile(const UnicodeString & Path)
  416. {
  417. FOnFocusFile(FTerminal, Path);
  418. }
  419. //---------------------------------------------------------------------------
  420. void __fastcall TFileFindDialog::FocusFile()
  421. {
  422. UnicodeString Path = static_cast<TRemoteFile *>(FileView->ItemFocused->Data)->FullFileName;
  423. // To make focussing directories work,
  424. // otherwise it would try to focus "empty-named file" in the Path
  425. Path = UnixExcludeTrailingBackslash(Path);
  426. if (StopIfFinding())
  427. {
  428. FFocusPath = Path;
  429. }
  430. else
  431. {
  432. DoFocusFile(Path);
  433. }
  434. }
  435. //---------------------------------------------------------------------------
  436. void __fastcall TFileFindDialog::FileViewDblClick(TObject * /*Sender*/)
  437. {
  438. if (FileView->ItemFocused != NULL)
  439. {
  440. FocusFile();
  441. }
  442. }
  443. //---------------------------------------------------------------------------
  444. void __fastcall TFileFindDialog::FocusActionExecute(TObject * /*Sender*/)
  445. {
  446. FocusFile();
  447. }
  448. //---------------------------------------------------------------------------
  449. void __fastcall TFileFindDialog::FileViewSelectItem(TObject * /*Sender*/,
  450. TListItem * /*Item*/, bool /*Selected*/)
  451. {
  452. UpdateControls();
  453. }
  454. //---------------------------------------------------------------------------
  455. void __fastcall TFileFindDialog::MaskButtonClick(TObject * /*Sender*/)
  456. {
  457. TFileMasks Masks = MaskEdit->Text;
  458. if (DoEditMaskDialog(Masks))
  459. {
  460. MaskEdit->Text = Masks.Masks;
  461. }
  462. }
  463. //---------------------------------------------------------------------------
  464. void __fastcall TFileFindDialog::CopyToClipboard()
  465. {
  466. TInstantOperationVisualizer Visualizer;
  467. std::unique_ptr<TStrings> Strings(new TStringList());
  468. for (int Index = 0; Index < FileView->Items->Count; Index++)
  469. {
  470. TListItem * Item = FileView->Items->Item[Index];
  471. TRemoteFile * File = static_cast<TRemoteFile *>(Item->Data);
  472. Strings->Add(File->FullFileName);
  473. }
  474. ::CopyToClipboard(Strings.get());
  475. }
  476. //---------------------------------------------------------------------------
  477. void __fastcall TFileFindDialog::CopyActionExecute(TObject * /*Sender*/)
  478. {
  479. CopyToClipboard();
  480. }
  481. //---------------------------------------------------------------------------
  482. void __fastcall TFileFindDialog::FormClose(TObject * /*Sender*/, TCloseAction & Action)
  483. {
  484. StopIfFinding();
  485. Action = caFree;
  486. }
  487. //---------------------------------------------------------------------------
  488. void __fastcall TFileFindDialog::FileViewContextPopup(TObject * Sender, TPoint & MousePos, bool & Handled)
  489. {
  490. // to update source popup menu before TBX menu is created
  491. UpdateControls();
  492. MenuPopup(Sender, MousePos, Handled);
  493. }
  494. //---------------------------------------------------------------------------
  495. TListItem * __fastcall TFileFindDialog::FileOperationFinished(const UnicodeString & FileName)
  496. {
  497. TFileItemMap::iterator I = FFileItemMap.find(FileName);
  498. TListItem * Result = NULL;
  499. if (DebugAlwaysTrue(I != FFileItemMap.end()))
  500. {
  501. Result = I->second;
  502. FileView->MakeProgressVisible(Result);
  503. FFileItemMap.erase(I);
  504. }
  505. return Result;
  506. }
  507. //---------------------------------------------------------------------------
  508. void __fastcall TFileFindDialog::FileDeleteFinished(const UnicodeString & FileName, bool Success)
  509. {
  510. // Delete in queue not supported
  511. DebugAssert(!FileName.IsEmpty());
  512. TListItem * Item = FileOperationFinished(FileName);
  513. if (DebugAlwaysTrue(Item != NULL) && Success)
  514. {
  515. ClearItem(Item);
  516. Item->Delete();
  517. }
  518. }
  519. //---------------------------------------------------------------------------
  520. void __fastcall TFileFindDialog::FileDownloadFinished(const UnicodeString & FileName, bool Success)
  521. {
  522. if (FileName.IsEmpty())
  523. {
  524. DebugAssert(Success);
  525. // Moved to queue, see call in TCustomScpExplorerForm::CopyParamDialog
  526. FileView->SelectAll(smNone);
  527. }
  528. else
  529. {
  530. TListItem * Item = FileOperationFinished(FileName);
  531. if (DebugAlwaysTrue(Item != NULL) && Success)
  532. {
  533. Item->Selected = false;
  534. }
  535. }
  536. }
  537. //---------------------------------------------------------------------------
  538. void __fastcall TFileFindDialog::FileListOperation(
  539. TFileListOperationEvent Operation, TFileOperationFinishedEvent OnFileOperationFinished)
  540. {
  541. std::unique_ptr<TStrings> FileList(new TStringList());
  542. DebugAssert(FFileItemMap.empty());
  543. TListItem * Item = FileView->Selected;
  544. while (Item != NULL)
  545. {
  546. TRemoteFile * File = static_cast<TRemoteFile *>(Item->Data);
  547. FileList->AddObject(File->FullFileName, File);
  548. FFileItemMap.insert(std::make_pair(File->FullFileName, Item));
  549. Item = FileView->GetNextItem(Item, sdAll, TItemStates() << isSelected);
  550. }
  551. try
  552. {
  553. Operation(FTerminal, FileList.get(), OnFileOperationFinished);
  554. }
  555. __finally
  556. {
  557. // can be non-empty only when not all files were processed
  558. FFileItemMap.clear();
  559. }
  560. UpdateControls();
  561. }
  562. //---------------------------------------------------------------------------
  563. void __fastcall TFileFindDialog::DeleteActionExecute(TObject * /*Sender*/)
  564. {
  565. FileListOperation(FOnDeleteFiles, FileDeleteFinished);
  566. }
  567. //---------------------------------------------------------------------------
  568. void __fastcall TFileFindDialog::SelectAllActionExecute(TObject * /*Sender*/)
  569. {
  570. FileView->SelectAll(smAll);
  571. }
  572. //---------------------------------------------------------------------------
  573. void __fastcall TFileFindDialog::DownloadActionExecute(TObject * /*Sender*/)
  574. {
  575. FileListOperation(FOnDownloadFiles, FileDownloadFinished);
  576. }
  577. //---------------------------------------------------------------------------