Properties.cpp 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997
  1. //---------------------------------------------------------------------
  2. #include <vcl.h>
  3. #pragma hdrstop
  4. #include "WinInterface.h"
  5. #include "Properties.h"
  6. #include <VCLCommon.h>
  7. #include <Common.h>
  8. #include <Terminal.h>
  9. #include <TextsWin.h>
  10. #include <GUITools.h>
  11. #include <CoreMain.h>
  12. #include <Tools.h>
  13. #include <BaseUtils.hpp>
  14. //---------------------------------------------------------------------
  15. #pragma link "PathLabel"
  16. #pragma link "Rights"
  17. #pragma resource "*.dfm"
  18. //---------------------------------------------------------------------
  19. bool __fastcall DoPropertiesDialog(TStrings * FileList,
  20. const UnicodeString Directory, const TRemoteTokenList * GroupList,
  21. const TRemoteTokenList * UserList, TStrings * ChecksumAlgs,
  22. TRemoteProperties * Properties,
  23. int AllowedChanges, int Options, TCalculateSizeEvent OnCalculateSize,
  24. TCalculateChecksumEvent OnCalculateChecksum)
  25. {
  26. bool Result;
  27. TPropertiesDialog * PropertiesDialog = new TPropertiesDialog(Application,
  28. FileList, Directory, GroupList, UserList, ChecksumAlgs, AllowedChanges, Options,
  29. OnCalculateSize, OnCalculateChecksum);
  30. try
  31. {
  32. Result = PropertiesDialog->Execute(*Properties);
  33. }
  34. __finally
  35. {
  36. delete PropertiesDialog;
  37. }
  38. return Result;
  39. }
  40. //---------------------------------------------------------------------
  41. __fastcall TPropertiesDialog::TPropertiesDialog(TComponent* AOwner,
  42. TStrings * FileList, const UnicodeString Directory,
  43. const TRemoteTokenList * GroupList, const TRemoteTokenList * UserList,
  44. TStrings * ChecksumAlgs,
  45. int AllowedChanges, int Options, TCalculateSizeEvent OnCalculateSize,
  46. TCalculateChecksumEvent OnCalculateChecksum)
  47. : TForm(AOwner)
  48. {
  49. FOnCalculateSize = OnCalculateSize;
  50. FOnCalculateChecksum = OnCalculateChecksum;
  51. RightsFrame->OnChange = ControlChange;
  52. FFileList = new TStringList();
  53. FFileList->Assign(FileList);
  54. FAllowedChanges = AllowedChanges;
  55. if (FLAGSET(FAllowedChanges, cpAcl))
  56. {
  57. DebugAssert(FLAGCLEAR(FAllowedChanges, cpMode));
  58. RightsLabel->Caption = LoadStr(PROPERTIES_ACL);
  59. RightsFrame->DisplayAsAcl();
  60. }
  61. FOptions = Options;
  62. FAllowCalculateStats = false;
  63. FStatsNotCalculated = false;
  64. FChecksumLoaded = false;
  65. FMultipleChecksum = false;
  66. LocationLabel->Text = Directory;
  67. FGroupList = GroupList;
  68. FUserList = UserList;
  69. FChecksumAlgs = ChecksumAlgs;
  70. ReadOnlyControl(ChecksumEdit);
  71. ReadOnlyControl(OwnerView);
  72. ReadOnlyControl(GroupView);
  73. ReadOnlyControl(FileLabel);
  74. ReadOnlyControl(LocationLabel);
  75. ReadOnlyControl(SizeLabel);
  76. ReadOnlyControl(LinksToLabel);
  77. ChecksumUnknownLabel->Caption = LoadStr(PROPERTIES_CHECKSUM_UNKNOWN);
  78. UseSystemSettings(this);
  79. LoadInfo();
  80. }
  81. //---------------------------------------------------------------------------
  82. __fastcall TPropertiesDialog::~TPropertiesDialog()
  83. {
  84. delete FFileList;
  85. FFileList = NULL;
  86. }
  87. //---------------------------------------------------------------------
  88. bool __fastcall TPropertiesDialog::Execute(TRemoteProperties & Properties)
  89. {
  90. SetFileProperties(Properties);
  91. PageControl->ActivePage = CommonSheet;
  92. if (OwnerComboBox->Visible && OwnerComboBox->Enabled)
  93. {
  94. ActiveControl = OwnerComboBox;
  95. }
  96. else if (GroupComboBox->Visible && GroupComboBox->Enabled)
  97. {
  98. ActiveControl = GroupComboBox;
  99. }
  100. else if (RightsFrame->Visible && RightsFrame->Enabled)
  101. {
  102. ActiveControl = RightsFrame;
  103. }
  104. else if (DebugAlwaysTrue(CancelButton->Visible && CancelButton->Enabled))
  105. {
  106. ActiveControl = CancelButton;
  107. }
  108. if (DebugAlwaysTrue(FChecksumAlgs != NULL))
  109. {
  110. ChecksumAlgEdit->Items->Assign(FChecksumAlgs);
  111. int ChecksumIndex = FChecksumAlgs->IndexOf(GUIConfiguration->ChecksumAlg);
  112. if (ChecksumIndex < 0)
  113. {
  114. ChecksumIndex = 0;
  115. }
  116. ChecksumAlgEdit->ItemIndex = ChecksumIndex;
  117. }
  118. ResetChecksum();
  119. UpdateControls();
  120. bool Result = (ShowModal() == DefaultResult());
  121. if (Result)
  122. {
  123. Properties = GetFileProperties();
  124. }
  125. return Result;
  126. }
  127. //---------------------------------------------------------------------------
  128. TModalResult __fastcall TPropertiesDialog::DefaultResult()
  129. {
  130. // Fallback when ChecksumButton is default
  131. return ::DefaultResult(this, OkButton);
  132. }
  133. //---------------------------------------------------------------------------
  134. UnicodeString __fastcall TPropertiesDialog::LoadRemoteToken(
  135. const TRemoteToken & Token)
  136. {
  137. UnicodeString Result;
  138. if (FLAGSET(FOptions, poUserGroupByID))
  139. {
  140. if (Token.IDValid)
  141. {
  142. if (Token.NameValid)
  143. {
  144. Result = FORMAT(L"%s [%d]", (Token.Name, int(Token.ID)));
  145. }
  146. else
  147. {
  148. Result = FORMAT(L"[%d]", (int(Token.ID)));
  149. }
  150. }
  151. else
  152. {
  153. // be it valid or not
  154. Result = Token.Name;
  155. }
  156. }
  157. else
  158. {
  159. // what if name is not filled in?
  160. Result = Token.Name;
  161. }
  162. return Result;
  163. }
  164. //---------------------------------------------------------------------------
  165. void __fastcall TPropertiesDialog::LoadRemoteToken(
  166. TComboBox * ComboBox, TEdit * View, TLabel * Label, bool Valid, const TRemoteToken & Token, int Change)
  167. {
  168. UnicodeString Value = Valid ? LoadRemoteToken(Token) : EmptyStr;
  169. ComboBox->Text = Value;
  170. View->Text = Value;
  171. bool AllowedChange = FLAGSET(FAllowedChanges, Change);
  172. ComboBox->Visible = AllowedChange;
  173. View->Visible = Valid && !AllowedChange;
  174. Label->FocusControl = AllowedChange ? static_cast<TWinControl *>(ComboBox) : static_cast<TWinControl *>(View);
  175. Label->Visible = Label->FocusControl->Visible;
  176. }
  177. //---------------------------------------------------------------------------
  178. void __fastcall TPropertiesDialog::LoadRemoteTokens(TComboBox * ComboBox,
  179. const TRemoteTokenList * List)
  180. {
  181. TStrings * Items = ComboBox->Items;
  182. Items->BeginUpdate();
  183. try
  184. {
  185. Items->Clear();
  186. if (List != NULL)
  187. {
  188. int Count = List->Count();
  189. for (int Index = 0; Index < Count; Index++)
  190. {
  191. Items->Add(LoadRemoteToken(*List->Token(Index)));
  192. }
  193. }
  194. }
  195. __finally
  196. {
  197. Items->EndUpdate();
  198. }
  199. }
  200. //---------------------------------------------------------------------------
  201. void __fastcall TPropertiesDialog::LoadInfo()
  202. {
  203. DebugAssert(FFileList->Count > 0);
  204. FMultiple = FFileList->Count > 1;
  205. FMultipleChecksum = FMultiple;
  206. FAllowCalculateStats = false;
  207. FStatsNotCalculated = false;
  208. __int64 FilesSize = 0;
  209. TCalculateSizeStats Stats;
  210. for (int Index = 0; Index < FFileList->Count; Index++)
  211. {
  212. TRemoteFile * File = (TRemoteFile *)(FFileList->Objects[Index]);
  213. if (File->IsDirectory)
  214. {
  215. Stats.Directories++;
  216. // we should use TTerminal::CanRecurseToDirectory instead
  217. if (!File->IsSymLink)
  218. {
  219. FAllowCalculateStats = true;
  220. FStatsNotCalculated = true;
  221. FMultipleChecksum = true;
  222. }
  223. }
  224. else
  225. {
  226. Stats.Files++;
  227. }
  228. if (File->IsSymLink)
  229. {
  230. Stats.SymLinks++;
  231. }
  232. FilesSize += File->Size;
  233. }
  234. // before it gets eventualy cleared if !FMultiple
  235. bool ShowTags = FLAGSET(FOptions, poTags) && (Stats.Files == 1) && (Stats.Directories == 0);
  236. LoadRemoteTokens(GroupComboBox, FGroupList);
  237. LoadRemoteTokens(OwnerComboBox, FUserList);
  238. FAnyDirectories = (Stats.Directories > 0);
  239. RightsFrame->AllowAddXToDirectories = FAnyDirectories;
  240. if (!FMultiple)
  241. {
  242. // Show only file name, if we have only single file/directory.
  243. // For directory, this changes, once "Calculate" button is pressed
  244. Stats = TCalculateSizeStats();
  245. }
  246. LoadStats(FilesSize, Stats);
  247. RightsFrame->AllowUndef = FMultiple;
  248. if (!FMultiple)
  249. {
  250. TRemoteFile * File = (TRemoteFile *)(FFileList->Objects[0]);
  251. DebugAssert(File);
  252. UpdateFileImage();
  253. LinksToLabelLabel->Visible = File->IsSymLink;
  254. LinksToLabel->Visible = File->IsSymLink;
  255. if (File->IsSymLink)
  256. {
  257. LinksToLabel->Text = File->LinkTo;
  258. }
  259. Caption = FMTLOAD(PROPERTIES_FILE_CAPTION, (File->FileName));
  260. }
  261. else
  262. {
  263. Caption = FMTLOAD(PROPERTIES_FILES_CAPTION, (FFileList->Strings[0]));
  264. LinksToLabelLabel->Hide();
  265. LinksToLabel->Hide();
  266. LoadDialogImage(FileIconImage, L"Multiple Files");
  267. }
  268. ChecksumGroup->Visible = !FMultipleChecksum;
  269. ChecksumView->Visible = FMultipleChecksum;
  270. TagsSheet->TabVisible = ShowTags;
  271. }
  272. //---------------------------------------------------------------------------
  273. void __fastcall TPropertiesDialog::UpdateFileImage()
  274. {
  275. TImageList * ImageList = ShellImageListForControl(this, ilsLarge);
  276. FileIconImage->Picture->Bitmap = NULL;
  277. TRemoteFile * File = (TRemoteFile *)(FFileList->Objects[0]);
  278. // shell image list does not have fixed large icon size
  279. // (it is probably 32x32 min, but can be larger, on xp it is 48x48 if
  280. // large icons are enabled, on vista can be even larger).
  281. // so we stretch (shrink) the icon to 32x32 here to be sure.
  282. Graphics::TBitmap * Bitmap = new Graphics::TBitmap;
  283. try
  284. {
  285. ImageList->GetBitmap(File->IconIndex, Bitmap);
  286. int Size = DialogImageSize(this);
  287. // Use exact DPI-scaled size, not approximate scaling by font size.
  288. // Otherwise we stretch icons unnecessarily because the canvas
  289. // is one or two pixels off the icon size
  290. FileIconImage->Width = Size;
  291. FileIconImage->Height = Size;
  292. FileIconImage->Picture->Bitmap->Width = Size;
  293. FileIconImage->Picture->Bitmap->Height = Size;
  294. FileIconImage->Picture->Bitmap->Canvas->StretchDraw(
  295. TRect(0, 0, Size, Size),
  296. Bitmap);
  297. }
  298. __finally
  299. {
  300. delete Bitmap;
  301. }
  302. }
  303. //---------------------------------------------------------------------------
  304. void __fastcall TPropertiesDialog::LoadStats(__int64 FilesSize,
  305. const TCalculateSizeStats & Stats)
  306. {
  307. UnicodeString SizeStr;
  308. UnicodeString FilesStr;
  309. if (FStatsNotCalculated)
  310. {
  311. SizeStr = LoadStr(PROPERTIES_UNKNOWN_SIZE);
  312. }
  313. else
  314. {
  315. SizeStr = FormatBytes(FilesSize);
  316. UnicodeString SizeUnorderedStr = FormatBytes(FilesSize, fbNone);
  317. if (SizeStr != SizeUnorderedStr)
  318. {
  319. SizeStr = FORMAT(L"%s (%s)", (SizeStr, SizeUnorderedStr));
  320. }
  321. }
  322. if (((Stats.Files + Stats.Directories) == 0) && !FMultiple)
  323. {
  324. TRemoteFile * File = (TRemoteFile *)(FFileList->Objects[0]);
  325. DebugAssert(File != NULL);
  326. FilesStr = File->FileName;
  327. }
  328. else
  329. {
  330. if (Stats.Files > 0)
  331. {
  332. FilesStr = (Stats.Files == 1) ? FMTLOAD(PROPERTIES_FILE, (Stats.Files)) :
  333. FMTLOAD(PROPERTIES_FILES, (Stats.Files));
  334. if (Stats.Directories > 0)
  335. {
  336. FilesStr = FORMAT(L"%s, ", (FilesStr));
  337. }
  338. }
  339. if (Stats.Directories > 0)
  340. {
  341. FilesStr += (Stats.Directories == 1) ? FMTLOAD(PROPERTIES_DIRECTORY, (Stats.Directories)) :
  342. FMTLOAD(PROPERTIES_DIRECTORIES, (Stats.Directories));
  343. }
  344. if (Stats.SymLinks > 0)
  345. {
  346. UnicodeString SymLinksStr;
  347. SymLinksStr = (Stats.SymLinks == 1) ? FMTLOAD(PROPERTIES_SYMLINK, (Stats.SymLinks)) :
  348. FMTLOAD(PROPERTIES_SYMLINKS, (Stats.SymLinks));
  349. FilesStr = FORMAT(L"%s (%s)", (FilesStr, SymLinksStr));
  350. }
  351. }
  352. SizeLabel->Text = SizeStr;
  353. FileLabel->Text = FilesStr;
  354. }
  355. //---------------------------------------------------------------------------
  356. void __fastcall TPropertiesDialog::SetFileProperties(const TRemoteProperties & value)
  357. {
  358. TValidProperties Valid;
  359. if (value.Valid.Contains(vpRights) && (FLAGSET(FAllowedChanges, cpMode) || FLAGSET(FAllowedChanges, cpAcl)))
  360. {
  361. Valid << vpRights;
  362. }
  363. if (value.Valid.Contains(vpOwner) && FLAGSET(FAllowedChanges, cpOwner))
  364. {
  365. Valid << vpOwner;
  366. }
  367. if (value.Valid.Contains(vpGroup) && FLAGSET(FAllowedChanges, cpGroup))
  368. {
  369. Valid << vpGroup;
  370. }
  371. FOrigProperties = value;
  372. FOrigProperties.Valid = Valid;
  373. FOrigProperties.Recursive = false;
  374. bool HasRights = value.Valid.Contains(vpRights);
  375. RightsFrame->Visible = HasRights;
  376. RightsLabel->Visible = RightsFrame->Visible;
  377. if (HasRights)
  378. {
  379. RightsFrame->Rights = value.Rights;
  380. RightsFrame->AddXToDirectories = value.AddXToDirectories;
  381. }
  382. else
  383. {
  384. RightsFrame->Rights = TRights();
  385. RightsFrame->AddXToDirectories = false;
  386. }
  387. // Not necesarily true, let's find the scenario when it is not and then decide how to render that (shift group up?)
  388. DebugAssert(value.Valid.Contains(vpOwner) || !value.Valid.Contains(vpGroup));
  389. LoadRemoteToken(GroupComboBox, GroupView, GroupLabel, value.Valid.Contains(vpGroup), value.Group, cpGroup);
  390. LoadRemoteToken(OwnerComboBox, OwnerView, OwnerLabel, value.Valid.Contains(vpOwner), value.Owner, cpOwner);
  391. bool HasAnything = GroupLabel->Visible || OwnerLabel->Visible || HasRights;
  392. // Not necesarily true, let's find the scenario when it is not and then decide how to render that (shift rights up?)
  393. DebugAssert((GroupLabel->Visible || OwnerLabel->Visible) || !HasRights);
  394. GroupOwnerRightsBevel->Visible = HasAnything;
  395. RecursiveCheck2->Checked = value.Recursive;
  396. RecursiveCheck2->Visible =
  397. (GroupComboBox->Visible ||
  398. OwnerComboBox->Visible ||
  399. // Recursion is always supported for permissions and never for ACL.
  400. // If this ever changes we will have to introduce respective capability check.
  401. (HasRights && !FLAGSET(FAllowedChanges, cpAcl))) &&
  402. FAnyDirectories;
  403. RecursiveBevel->Visible = RecursiveCheck2->Visible || HasRights;
  404. if (TagsSheet->TabVisible)
  405. {
  406. TagsView->Clear();
  407. if (value.Valid.Contains(vpTags))
  408. {
  409. std::unique_ptr<TStrings> Tags(TextToStringList(value.Tags));
  410. for (int Index = 0; Index < Tags->Count; Index += 2)
  411. {
  412. AddTag(Tags->Strings[Index], Tags->Strings[Index + 1]);
  413. }
  414. }
  415. AutoSizeTagsView();
  416. }
  417. UpdateControls();
  418. }
  419. //---------------------------------------------------------------------------
  420. void __fastcall TPropertiesDialog::StoreRemoteToken(unsigned int ID,
  421. const UnicodeString & Text, const TRemoteTokenList * List, TRemoteToken & Result)
  422. {
  423. DebugAssert(List != NULL);
  424. const TRemoteToken * Token = List->Find(ID);
  425. if (Token == NULL)
  426. {
  427. Result.ID = ID;
  428. Result.Name = Text;
  429. }
  430. else
  431. {
  432. Result = *Token;
  433. }
  434. }
  435. //---------------------------------------------------------------------------
  436. TRemoteToken __fastcall TPropertiesDialog::StoreRemoteToken(const TRemoteToken & Orig,
  437. UnicodeString Text, int Message, const TRemoteTokenList * List)
  438. {
  439. TRemoteToken Result;
  440. Text = Text.Trim();
  441. if (!Text.IsEmpty())
  442. {
  443. if (FLAGSET(FOptions, poUserGroupByID))
  444. {
  445. DebugAssert(List != NULL);
  446. int IDStart = Text.LastDelimiter(L"[");
  447. if (!Text.IsEmpty() && (IDStart >= 0) && (Text[Text.Length()] == L']'))
  448. {
  449. int ID;
  450. UnicodeString IDStr = Text.SubString(IDStart + 1, Text.Length() - IDStart - 1);
  451. if (!TryStrToInt(IDStr, ID))
  452. {
  453. throw Exception(Message);
  454. }
  455. else
  456. {
  457. StoreRemoteToken(ID, Text.SubString(1, IDStart - 1).Trim(), List, Result);
  458. }
  459. }
  460. else
  461. {
  462. const TRemoteToken * Token = List->Find(Text);
  463. if (Token == NULL)
  464. {
  465. int ID;
  466. if (TryStrToInt(Text, ID))
  467. {
  468. StoreRemoteToken(ID, Text, List, Result);
  469. }
  470. else
  471. {
  472. throw Exception(MainInstructions(FMTLOAD(PROPERTIES_UNKNOWN_TOKEN, (Text))));
  473. }
  474. }
  475. else
  476. {
  477. Result = *Token;
  478. }
  479. }
  480. }
  481. else
  482. {
  483. Result.Name = Text;
  484. }
  485. }
  486. if (LoadRemoteToken(Result) == LoadRemoteToken(Orig))
  487. {
  488. Result = Orig;
  489. }
  490. return Result;
  491. }
  492. //---------------------------------------------------------------------------
  493. void __fastcall TPropertiesDialog::StoreRemoteToken(TComboBox * ComboBox,
  494. int ChangeFlag, TValidProperty PropertyFlag, const TRemoteToken & Orig,
  495. TRemoteToken & Token, int Message, const TRemoteTokenList * List,
  496. TRemoteProperties & Properties)
  497. {
  498. UnicodeString Text = ComboBox->Text.Trim();
  499. if (FLAGSET(FAllowedChanges, ChangeFlag))
  500. {
  501. Token = StoreRemoteToken(Orig, Text, Message, List);
  502. if (Token.IsSet)
  503. {
  504. Properties.Valid << PropertyFlag;
  505. }
  506. }
  507. }
  508. //---------------------------------------------------------------------------
  509. TRemoteProperties __fastcall TPropertiesDialog::GetFileProperties()
  510. {
  511. TRemoteProperties Result;
  512. if (FLAGSET(FAllowedChanges, cpMode) || FLAGSET(FAllowedChanges, cpAcl))
  513. {
  514. Result.Valid << vpRights;
  515. Result.Rights = RightsFrame->Rights;
  516. Result.AddXToDirectories = RightsFrame->AddXToDirectories;
  517. }
  518. StoreRemoteToken(GroupComboBox, cpGroup, vpGroup, FOrigProperties.Group,
  519. Result.Group, PROPERTIES_INVALID_GROUP, FGroupList, Result);
  520. StoreRemoteToken(OwnerComboBox, cpOwner, vpOwner, FOrigProperties.Owner,
  521. Result.Owner, PROPERTIES_INVALID_OWNER, FUserList, Result);
  522. Result.Recursive = RecursiveCheck2->Checked;
  523. if (TagsSheet->TabVisible)
  524. {
  525. std::unique_ptr<TStrings> Tags(new TStringList());
  526. TagsView->HandleNeeded(); // Count does not work otherwise
  527. for (int Index = 0; Index < TagsView->Items->Count; Index++)
  528. {
  529. TListItem * Item = TagsView->Items->Item[Index];
  530. Tags->Add(Item->Caption);
  531. Tags->Add(Item->SubItems->Strings[0]);
  532. }
  533. Result.Tags = Tags->Text;
  534. Result.Valid << vpTags;
  535. }
  536. return Result;
  537. }
  538. //---------------------------------------------------------------------------
  539. void __fastcall TPropertiesDialog::ControlChange(TObject * /*Sender*/)
  540. {
  541. if (Visible)
  542. {
  543. UpdateControls();
  544. }
  545. }
  546. //---------------------------------------------------------------------------
  547. void __fastcall TPropertiesDialog::UpdateControls()
  548. {
  549. // No point enabling recursive check if there's no change allowed (supported),
  550. // i.e. with WebDAV.
  551. bool AnyAllowedChanges =
  552. FLAGSET(FAllowedChanges, cpGroup) || FLAGSET(FAllowedChanges, cpOwner) ||
  553. FLAGSET(FAllowedChanges, cpMode) || FLAGSET(FAllowedChanges, cpAcl);
  554. EnableControl(RecursiveCheck2, AnyAllowedChanges);
  555. bool Allow;
  556. try
  557. {
  558. Allow =
  559. !TRemoteProperties::ChangedProperties(FOrigProperties, GetFileProperties()).Valid.Empty() ||
  560. (RecursiveCheck2->Enabled && RecursiveCheck2->Checked);
  561. }
  562. catch(...)
  563. {
  564. // when properties are invalid allow submitting the form,
  565. // because that reveals the cause to the user, otherwise he/she
  566. // may not be able to tell what is wrong
  567. Allow = true;
  568. }
  569. EnableControl(OkButton, Allow);
  570. EnableControl(GroupComboBox, FLAGSET(FAllowedChanges, cpGroup));
  571. EnableControl(OwnerComboBox, FLAGSET(FAllowedChanges, cpOwner));
  572. EnableControl(RightsFrame, FLAGSET(FAllowedChanges, cpMode) || FLAGSET(FAllowedChanges, cpAcl));
  573. CalculateSizeButton->Visible = FAllowCalculateStats;
  574. if (!FMultiple)
  575. {
  576. // when setting properties for one file only, allow undef state
  577. // only when the input right explicitly requires it or
  578. // when "recursive" is on (possible for directory only).
  579. bool AllowUndef =
  580. (FOrigProperties.Valid.Contains(vpRights) &&
  581. FOrigProperties.Rights.AllowUndef) ||
  582. (RecursiveCheck2->Checked);
  583. if (!AllowUndef)
  584. {
  585. // when disallowing undef state, make sure, all undef are turned into unset
  586. RightsFrame->Rights = TRights(RightsFrame->Rights.NumberSet);
  587. }
  588. RightsFrame->AllowUndef = AllowUndef;
  589. }
  590. EnableControl(ChecksumSheet, ChecksumSupported());
  591. EnableControl(ChecksumButton, ChecksumSheet->Enabled &&
  592. !ChecksumAlgEdit->Text.IsEmpty());
  593. ChecksumEdit->Visible = !ChecksumEdit->Text.IsEmpty();
  594. ChecksumUnknownLabel->Visible = !ChecksumEdit->Visible;
  595. EnableControl(EditTagButton, (TagsView->ItemIndex >= 0));
  596. EnableControl(RemoveTagButton, (TagsView->ItemIndex >= 0));
  597. DefaultButton(ChecksumButton, ChecksumAlgEdit->Focused());
  598. DefaultButton(OkButton, !ChecksumAlgEdit->Focused());
  599. }
  600. //---------------------------------------------------------------------------
  601. void __fastcall TPropertiesDialog::FormCloseQuery(TObject * /*Sender*/,
  602. bool & /*CanClose*/)
  603. {
  604. if (ModalResult == DefaultResult())
  605. {
  606. ExitActiveControl(this);
  607. }
  608. }
  609. //---------------------------------------------------------------------------
  610. void __fastcall TPropertiesDialog::CalculateSizeButtonClick(
  611. TObject * /*Sender*/)
  612. {
  613. DebugAssert(FOnCalculateSize != NULL);
  614. bool DoClose = false;
  615. Enabled = false;
  616. try
  617. {
  618. __int64 Size;
  619. TCalculateSizeStats Stats;
  620. FOnCalculateSize(FFileList, Size, Stats, DoClose);
  621. FStatsNotCalculated = false;
  622. LoadStats(Size, Stats);
  623. }
  624. __finally
  625. {
  626. Enabled = true;
  627. if (DoClose)
  628. {
  629. Close();
  630. }
  631. }
  632. }
  633. //---------------------------------------------------------------------------
  634. void __fastcall TPropertiesDialog::HelpButtonClick(TObject * /*Sender*/)
  635. {
  636. FormHelp(this);
  637. }
  638. //---------------------------------------------------------------------------
  639. void __fastcall TPropertiesDialog::ResetChecksum()
  640. {
  641. ChecksumView->Items->Clear();
  642. ChecksumEdit->Text = UnicodeString();
  643. AutoSizeListColumnsWidth(ChecksumView);
  644. }
  645. //---------------------------------------------------------------------------
  646. void __fastcall TPropertiesDialog::CalculateChecksum()
  647. {
  648. DebugAssert(FOnCalculateChecksum != NULL);
  649. ResetChecksum();
  650. FChecksumLoaded = true;
  651. FAlgUsed = UnicodeString();
  652. bool DoClose = false;
  653. try
  654. {
  655. FOnCalculateChecksum(ChecksumAlgEdit->Text, FFileList, CalculatedChecksum, DoClose);
  656. }
  657. __finally
  658. {
  659. if (DoClose)
  660. {
  661. Close();
  662. }
  663. }
  664. // If we successfully used the selected checksum, remember it (in normalized form)
  665. if (!FAlgUsed.IsEmpty())
  666. {
  667. GUIConfiguration->ChecksumAlg = FAlgUsed;
  668. }
  669. AutoSizeListColumnsWidth(ChecksumView);
  670. }
  671. //---------------------------------------------------------------------------
  672. void __fastcall TPropertiesDialog::CalculatedChecksum(
  673. const UnicodeString & FileName, const UnicodeString & Alg,
  674. const UnicodeString & Hash)
  675. {
  676. if (FMultipleChecksum)
  677. {
  678. TListItem * Item = ChecksumView->Items->Add();
  679. Item->Caption = FileName;
  680. Item->SubItems->Add(Hash);
  681. // optimization
  682. int TopIndex = ListView_GetTopIndex(ChecksumView->Handle);
  683. int Index = Item->Index;
  684. if ((TopIndex <= Index) &&
  685. (Index <= TopIndex + ChecksumView->VisibleRowCount))
  686. {
  687. AutoSizeListColumnsWidth(ChecksumView);
  688. }
  689. }
  690. else
  691. {
  692. ChecksumEdit->Text = Hash;
  693. }
  694. FAlgUsed = Alg;
  695. UpdateControls();
  696. }
  697. //---------------------------------------------------------------------------
  698. void __fastcall TPropertiesDialog::NeedChecksum()
  699. {
  700. if (!FChecksumLoaded && ChecksumSupported())
  701. {
  702. CalculateChecksum();
  703. }
  704. }
  705. //---------------------------------------------------------------------------
  706. bool __fastcall TPropertiesDialog::ChecksumSupported()
  707. {
  708. return (FOnCalculateChecksum != NULL);
  709. }
  710. //---------------------------------------------------------------------------
  711. void __fastcall TPropertiesDialog::ChecksumButtonClick(TObject * /*Sender*/)
  712. {
  713. CalculateChecksum();
  714. }
  715. //---------------------------------------------------------------------------
  716. void __fastcall TPropertiesDialog::PageControlChange(TObject * /*Sender*/)
  717. {
  718. if (PageControl->ActivePage == ChecksumSheet)
  719. {
  720. NeedChecksum();
  721. }
  722. }
  723. //---------------------------------------------------------------------------
  724. void __fastcall TPropertiesDialog::ChecksumAlgEditChange(TObject * /*Sender*/)
  725. {
  726. ResetChecksum();
  727. UpdateControls();
  728. }
  729. //---------------------------------------------------------------------------
  730. void __fastcall TPropertiesDialog::CopyClick(TObject * Sender)
  731. {
  732. TInstantOperationVisualizer Visualizer;
  733. TListView * ListView = dynamic_cast<TListView *>(GetPopupComponent(Sender));
  734. DebugAssert(ListView != NULL);
  735. int Count = 0;
  736. UnicodeString SingleText;
  737. std::unique_ptr<TStrings> Lines(new TStringList());
  738. TListItem * Item = ListView->GetNextItem(NULL, sdAll, TItemStates() << isSelected);
  739. while (Item != NULL)
  740. {
  741. DebugAssert(Item->Selected);
  742. SingleText = Item->SubItems->Strings[0];
  743. UnicodeString Value = Item->SubItems->Strings[0];
  744. UnicodeString Entry = Item->Caption;
  745. if (!Value.IsEmpty())
  746. {
  747. Entry += FORMAT(L" = %s", (Value));
  748. }
  749. Lines->Add(Entry);
  750. Count++;
  751. Item = ListView->GetNextItem(Item, sdAll, TItemStates() << isSelected);
  752. }
  753. if ((ListView == ChecksumView) && (Count == 1))
  754. {
  755. CopyToClipboard(SingleText);
  756. }
  757. else
  758. {
  759. CopyToClipboard(Lines.get());
  760. }
  761. }
  762. //---------------------------------------------------------------------------
  763. void __fastcall TPropertiesDialog::ListViewContextPopup(
  764. TObject * Sender, TPoint & MousePos, bool & Handled)
  765. {
  766. MenuPopup(Sender, MousePos, Handled);
  767. }
  768. //---------------------------------------------------------------------------
  769. void __fastcall TPropertiesDialog::ValidateRemoteToken(
  770. const TRemoteToken & Orig, int Message, TComboBox * ComboBox,
  771. const TRemoteTokenList * List)
  772. {
  773. if (!IsCancelButtonBeingClicked(this))
  774. {
  775. try
  776. {
  777. ComboBox->Text =
  778. LoadRemoteToken(StoreRemoteToken(Orig, ComboBox->Text, Message, List));
  779. }
  780. catch(...)
  781. {
  782. ComboBox->SetFocus();
  783. throw;
  784. }
  785. }
  786. }
  787. //---------------------------------------------------------------------------
  788. void __fastcall TPropertiesDialog::GroupComboBoxExit(TObject * Sender)
  789. {
  790. ValidateRemoteToken(FOrigProperties.Group, PROPERTIES_INVALID_GROUP,
  791. dynamic_cast<TComboBox *>(Sender), FGroupList);
  792. }
  793. //---------------------------------------------------------------------------
  794. void __fastcall TPropertiesDialog::OwnerComboBoxExit(TObject * Sender)
  795. {
  796. ValidateRemoteToken(FOrigProperties.Owner, PROPERTIES_INVALID_OWNER,
  797. dynamic_cast<TComboBox *>(Sender), FUserList);
  798. }
  799. //---------------------------------------------------------------------------
  800. void __fastcall TPropertiesDialog::FormShow(TObject * /*Sender*/)
  801. {
  802. UpdateControls();
  803. }
  804. //---------------------------------------------------------------------------
  805. void __fastcall TPropertiesDialog::CMDpiChanged(TMessage & Message)
  806. {
  807. TForm::Dispatch(&Message);
  808. if (!FMultiple)
  809. {
  810. UpdateFileImage();
  811. }
  812. // WORKAROUND: Mere presence of the RightsFrame breaks automatic layout on DPI change in some situation, fixing it manually.
  813. // (opening on a secondary display with 100%, while primary display has 150%)
  814. SizeLabel->Width = CalculateSizeButton->Left - ScaleByTextHeight(this, 8) - SizeLabel->Left;
  815. Bevel1->Width = CommonSheet->ClientWidth - (Bevel1->Left * 2);
  816. Bevel2->Width = Bevel1->Width;
  817. GroupOwnerRightsBevel->Width = Bevel1->Width;
  818. RecursiveBevel->Width = Bevel1->Width;
  819. }
  820. //---------------------------------------------------------------------------
  821. void __fastcall TPropertiesDialog::Dispatch(void * Message)
  822. {
  823. TMessage * M = reinterpret_cast<TMessage*>(Message);
  824. if (M->Msg == CM_DPICHANGED)
  825. {
  826. CMDpiChanged(*M);
  827. }
  828. else
  829. {
  830. TForm::Dispatch(Message);
  831. }
  832. }
  833. //---------------------------------------------------------------------------
  834. void __fastcall TPropertiesDialog::TagsViewKeyDown(TObject *, WORD & Key, TShiftState)
  835. {
  836. if (RemoveTagButton->Enabled && (Key == VK_DELETE))
  837. {
  838. RemoveTagButton->OnClick(NULL);
  839. }
  840. if (AddTagButton->Enabled && (Key == VK_INSERT))
  841. {
  842. AddTagButton->OnClick(NULL);
  843. }
  844. }
  845. //---------------------------------------------------------------------------
  846. TListItem * TPropertiesDialog::AddTag(const UnicodeString & Key, const UnicodeString & Value)
  847. {
  848. TListItem * Item = TagsView->Items->Add();
  849. Item->Caption = Key;
  850. Item->SubItems->Add(Value);
  851. return Item;
  852. }
  853. //---------------------------------------------------------------------------
  854. void TPropertiesDialog::AutoSizeTagsView()
  855. {
  856. AutoSizeListColumnsWidth(TagsView, 1);
  857. }
  858. //---------------------------------------------------------------------------
  859. void TPropertiesDialog::AddEditTag(bool Add)
  860. {
  861. std::unique_ptr<TStrings> Tags(CreateSortedStringList(true));
  862. TListItem * ItemFocused = TagsView->ItemFocused;
  863. for (int Index = 0; Index < TagsView->Items->Count; Index++)
  864. {
  865. TListItem * Item = TagsView->Items->Item[Index];
  866. if (Add || (Item != ItemFocused))
  867. {
  868. Tags->Add(Item->Caption);
  869. }
  870. }
  871. UnicodeString Key, Value;
  872. if (!Add)
  873. {
  874. Key = ItemFocused->Caption;
  875. Value = ItemFocused->SubItems->Strings[0];
  876. }
  877. if (DoTagDialog(Add, Tags.get(), Key, Value))
  878. {
  879. if (Add)
  880. {
  881. TagsView->ItemFocused = AddTag(Key, Value);
  882. TagsView->ItemFocused->MakeVisible(false);
  883. }
  884. else
  885. {
  886. ItemFocused->Caption = Key;
  887. ItemFocused->SubItems->Strings[0] = Value;
  888. }
  889. AutoSizeTagsView();
  890. UpdateControls();
  891. }
  892. }
  893. //---------------------------------------------------------------------------
  894. void __fastcall TPropertiesDialog::AddTagButtonClick(TObject *)
  895. {
  896. AddEditTag(true);
  897. }
  898. //---------------------------------------------------------------------------
  899. void __fastcall TPropertiesDialog::TagsViewSelectItem(TObject *, TListItem *, bool Selected)
  900. {
  901. DebugUsedParam(Selected);
  902. UpdateControls();
  903. }
  904. //---------------------------------------------------------------------------
  905. void __fastcall TPropertiesDialog::EditTagButtonClick(TObject *)
  906. {
  907. AddEditTag(false);
  908. }
  909. //---------------------------------------------------------------------------
  910. void __fastcall TPropertiesDialog::RemoveTagButtonClick(TObject *)
  911. {
  912. int Index = TagsView->ItemIndex;
  913. TagsView->ItemFocused->Delete();
  914. int Count = TagsView->Items->Count;
  915. TagsView->ItemIndex = (Index < Count ? Index : Count - 1);
  916. AutoSizeTagsView();
  917. UpdateControls();
  918. }
  919. //---------------------------------------------------------------------------
  920. void __fastcall TPropertiesDialog::TagsViewDblClick(TObject *)
  921. {
  922. if (EditTagButton->Enabled)
  923. {
  924. EditTagButton->OnClick(NULL);
  925. }
  926. }
  927. //---------------------------------------------------------------------------