PathContextMenu.cpp 36 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175
  1. // Copyright 2017-2021 marynate. All Rights Reserved.
  2. #include "PathContextMenu.h"
  3. #include "ExtContentBrowser.h"
  4. #include "ExtContentBrowserSingleton.h"
  5. #include "ExtContentBrowserUtils.h"
  6. #include "ExtContentBrowserCommands.h"
  7. #include "Widgets/SExtPathView.h"
  8. #include "Misc/MessageDialog.h"
  9. #include "HAL/FileManager.h"
  10. #include "Misc/Paths.h"
  11. #include "Modules/ModuleManager.h"
  12. #include "UObject/ObjectRedirector.h"
  13. #include "Misc/PackageName.h"
  14. #include "Widgets/DeclarativeSyntaxSupport.h"
  15. #include "Widgets/SBoxPanel.h"
  16. #include "Widgets/SWindow.h"
  17. #include "Framework/Application/SlateApplication.h"
  18. #include "Textures/SlateIcon.h"
  19. #include "Framework/MultiBox/MultiBoxExtender.h"
  20. #include "Widgets/Input/SButton.h"
  21. #include "Widgets/Colors/SColorBlock.h"
  22. #include "EditorStyleSet.h"
  23. #include "AssetRegistry/AssetData.h"
  24. #include "Editor.h"
  25. #include "FileHelpers.h"
  26. #include "AssetRegistry/ARFilter.h"
  27. #include "AssetRegistry/AssetRegistryModule.h"
  28. #include "IAssetTools.h"
  29. #include "AssetToolsModule.h"
  30. #include "Framework/MultiBox/MultiBoxBuilder.h"
  31. #include "Widgets/Colors/SColorPicker.h"
  32. #include "Framework/Commands/GenericCommands.h"
  33. #include "Framework/Notifications/NotificationManager.h"
  34. #include "Widgets/Notifications/SNotificationList.h"
  35. #include "ContentBrowserModule.h"
  36. #include "Misc/FileHelper.h"
  37. #include "DesktopPlatformModule.h"
  38. #include "IDesktopPlatform.h"
  39. #include "Widgets/Input/SDirectoryPicker.h"
  40. #define LOCTEXT_NAMESPACE "ExtContentBrowser"
  41. FPathContextMenu::FPathContextMenu(const TWeakPtr<SWidget>& InParentContent)
  42. : ParentContent(InParentContent)
  43. , bCanExecuteRootDirsActions(false)
  44. , bHasSelectedPath(false)
  45. {
  46. }
  47. void FPathContextMenu::SetOnRenameFolderRequested(const FOnRenameFolderRequested& InOnRenameFolderRequested)
  48. {
  49. OnRenameFolderRequested = InOnRenameFolderRequested;
  50. }
  51. void FPathContextMenu::SetOnFolderDeleted(const FOnFolderDeleted& InOnFolderDeleted)
  52. {
  53. OnFolderDeleted = InOnFolderDeleted;
  54. }
  55. void FPathContextMenu::SetOnFolderFavoriteToggled(const FOnFolderFavoriteToggled& InOnFolderFavoriteToggled)
  56. {
  57. OnFolderFavoriteToggled = InOnFolderFavoriteToggled;
  58. }
  59. const TArray<FString>& FPathContextMenu::GetSelectedPaths() const
  60. {
  61. return SelectedPaths;
  62. }
  63. void FPathContextMenu::SetSelectedPaths(const TArray<FString>& InSelectedPaths)
  64. {
  65. SelectedPaths = InSelectedPaths;
  66. }
  67. TSharedRef<FExtender> FPathContextMenu::MakePathViewContextMenuExtender(const TArray<FString>& InSelectedPaths)
  68. {
  69. // Cache any vars that are used in determining if you can execute any actions.
  70. // Useful for actions whose "CanExecute" will not change or is expensive to calculate.
  71. CacheCanExecuteVars();
  72. // Get all menu extenders for this context menu from the content browser module
  73. FContentBrowserModule& ContentBrowserModule = FModuleManager::GetModuleChecked<FContentBrowserModule>( TEXT("ContentBrowser") );
  74. TArray<FContentBrowserMenuExtender_SelectedPaths> MenuExtenderDelegates = ContentBrowserModule.GetAllPathViewContextMenuExtenders();
  75. TArray<TSharedPtr<FExtender>> Extenders;
  76. for (int32 i = 0; i < MenuExtenderDelegates.Num(); ++i)
  77. {
  78. if (MenuExtenderDelegates[i].IsBound())
  79. {
  80. //Extenders.Add(MenuExtenderDelegates[i].Execute( InSelectedPaths ));
  81. }
  82. }
  83. TSharedPtr<FExtender> MenuExtender = FExtender::Combine(Extenders);
  84. MenuExtender->AddMenuExtension("FolderContext", EExtensionHook::After, TSharedPtr<FUICommandList>(), FMenuExtensionDelegate::CreateSP(this, &FPathContextMenu::MakePathViewContextMenu));
  85. return MenuExtender.ToSharedRef();
  86. }
  87. void FPathContextMenu::MakePathViewContextMenu(FMenuBuilder& MenuBuilder)
  88. {
  89. int32 NumAssetPaths = SelectedPaths.Num();
  90. int32 NumClassPaths = 0;
  91. // Only add something if at least one folder is selected
  92. if ( NumAssetPaths > 0 )
  93. {
  94. const bool bHasAssetPaths = NumAssetPaths > 0;
  95. const bool bHasClassPaths = NumClassPaths > 0;
  96. // Root folder operations section //
  97. if (CanExecuteRootDirsActions())
  98. {
  99. MenuBuilder.BeginSection("PathViewFolderOptions", LOCTEXT("PathViewRootOptionsMenuHeading", "Root Folder Options"));
  100. {
  101. #if ECB_FEA_RELOAD_ROOT_FOLDER
  102. // Reload content folder
  103. MenuBuilder.AddMenuEntry(
  104. LOCTEXT("Reload", "Reload"),
  105. LOCTEXT("ReloadRootFolderTooltip", "Reload selected root content folder"),
  106. FSlateIcon(),
  107. FUIAction(FExecuteAction::CreateSP(this, &FPathContextMenu::ExecuteReloadRootFolder))
  108. );
  109. #endif
  110. // Remove content folder
  111. MenuBuilder.AddMenuEntry(
  112. LOCTEXT("Remove", "Remove"),
  113. LOCTEXT("RemoveRootFolderTooltip", "Remove selected root content folder"),
  114. FSlateIcon(),
  115. FUIAction(FExecuteAction::CreateSP(this, &FPathContextMenu::ExecuteRemoveRootFolder))
  116. );
  117. }
  118. MenuBuilder.EndSection();
  119. }
  120. // Common operations section //
  121. MenuBuilder.BeginSection("PathViewFolderOptions", LOCTEXT("PathViewOptionsMenuHeading", "Folder Options") );
  122. {
  123. if(bHasAssetPaths)
  124. {
  125. FText NewAssetToolTip;
  126. if(SelectedPaths.Num() == 1)
  127. {
  128. if(CanCreateAsset())
  129. {
  130. NewAssetToolTip = FText::Format(LOCTEXT("NewAssetTooltip_CreateIn", "Create a new asset in {0}."), FText::FromString(SelectedPaths[0]));
  131. }
  132. else
  133. {
  134. NewAssetToolTip = FText::Format(LOCTEXT("NewAssetTooltip_InvalidPath", "Cannot create new assets in {0}."), FText::FromString(SelectedPaths[0]));
  135. }
  136. }
  137. else
  138. {
  139. NewAssetToolTip = LOCTEXT("NewAssetTooltip_InvalidNumberOfPaths", "Can only create assets when there is a single path selected.");
  140. }
  141. #if ECB_LEGACY
  142. // New Asset (submenu)
  143. MenuBuilder.AddSubMenu(
  144. LOCTEXT( "NewAssetLabel", "New Asset" ),
  145. NewAssetToolTip,
  146. FNewMenuDelegate::CreateRaw( this, &FPathContextMenu::MakeNewAssetSubMenu ),
  147. FUIAction(
  148. FExecuteAction(),
  149. FCanExecuteAction::CreateRaw( this, &FPathContextMenu::CanCreateAsset )
  150. ),
  151. NAME_None,
  152. EUserInterfaceActionType::Button,
  153. false,
  154. FSlateIcon()
  155. );
  156. #endif
  157. }
  158. #if ECB_LEGACY
  159. if(bHasClassPaths)
  160. {
  161. FText NewClassToolTip;
  162. if(SelectedPaths.Num() == 1)
  163. {
  164. if(CanCreateClass())
  165. {
  166. NewClassToolTip = FText::Format(LOCTEXT("NewClassTooltip_CreateIn", "Create a new class in {0}."), FText::FromString(SelectedPaths[0]));
  167. }
  168. else
  169. {
  170. NewClassToolTip = FText::Format(LOCTEXT("NewClassTooltip_InvalidPath", "Cannot create new classes in {0}."), FText::FromString(SelectedPaths[0]));
  171. }
  172. }
  173. else
  174. {
  175. NewClassToolTip = LOCTEXT("NewClassTooltip_InvalidNumberOfPaths", "Can only create classes when there is a single path selected.");
  176. }
  177. // New Class
  178. MenuBuilder.AddMenuEntry(
  179. LOCTEXT("NewClassLabel", "New C++ Class..."),
  180. NewClassToolTip,
  181. FSlateIcon(FAppStyle::GetStyleSetName(), "MainFrame.AddCodeToProject"),
  182. FUIAction(
  183. FExecuteAction::CreateRaw( this, &FPathContextMenu::ExecuteCreateClass ),
  184. FCanExecuteAction::CreateRaw( this, &FPathContextMenu::CanCreateClass )
  185. )
  186. );
  187. }
  188. #endif
  189. // Explore
  190. MenuBuilder.AddMenuEntry(
  191. ExtContentBrowserUtils::GetExploreFolderText(),
  192. LOCTEXT("ExploreTooltip", "Finds this folder on disk."),
  193. FSlateIcon(),
  194. FUIAction( FExecuteAction::CreateSP( this, &FPathContextMenu::ExecuteExplore ) )
  195. );
  196. #if ECB_FEA_VALIDATE
  197. MenuBuilder.AddMenuEntry(
  198. LOCTEXT("ValidateAssetsInFolder", "Validate Assets"),
  199. LOCTEXT("ValidateAssetsInFolderTooltip", "Validate all assets in current folder."),
  200. FSlateIcon(),
  201. FUIAction(FExecuteAction::CreateSP(this, &FPathContextMenu::ExecuteValidateAssetsInFolder))
  202. );
  203. #endif
  204. // Rescan folder
  205. if (CanExecuteRescanFolder())
  206. {
  207. MenuBuilder.AddMenuEntry(
  208. LOCTEXT("RescanFolders", "Rescan Folders"),
  209. LOCTEXT("RescanFoldersTooltip", "Rescan folders in current selected folder recursively"),
  210. FSlateIcon(),
  211. FUIAction(FExecuteAction::CreateSP(this, &FPathContextMenu::ExecuteRescanFolder))
  212. );
  213. }
  214. #if 0
  215. // Rescan Assets Recursively
  216. if (CanExecuteRescanFolder())
  217. {
  218. MenuBuilder.AddMenuEntry(
  219. LOCTEXT("RescanAssets", "Rescan Assets"),
  220. LOCTEXT("RescanAssetsTooltip", "Rescan assets in current selected folder recursively"),
  221. FSlateIcon(),
  222. FUIAction(FExecuteAction::CreateSP(this, &FPathContextMenu::ExecuteCacheAllAssetSAsync))
  223. );
  224. }
  225. #endif
  226. // Import folder
  227. if (CanExecuteImportFolder())
  228. {
  229. MenuBuilder.AddMenuEntry(
  230. LOCTEXT("ImportAssets", "Import Assets"),
  231. LOCTEXT("ImportAssetsTooltip", "Import assets in current selected folder"),
  232. FSlateIcon(),
  233. FUIAction(FExecuteAction::CreateSP(this, &FPathContextMenu::ExecuteImportFolder))
  234. );
  235. #if ECB_WIP
  236. MenuBuilder.AddMenuEntry(
  237. LOCTEXT("ImportAssetsRecursively", "Import Assets Recursively"),
  238. LOCTEXT("ImportAssetsRecursivelyTooltip", "Refresh assets in current selected folder and it's children folders recursively"),
  239. FSlateIcon(),
  240. FUIAction(FExecuteAction::CreateSP(this, &FPathContextMenu::ExecuteImportFolder))
  241. );
  242. #endif
  243. }
  244. #if ECB_LEGACY
  245. MenuBuilder.AddMenuEntry(FGenericCommands::Get().Rename, NAME_None,
  246. LOCTEXT("RenameFolder", "Rename"),
  247. LOCTEXT("RenameFolderTooltip", "Rename the selected folder.")
  248. );
  249. // If any colors have already been set, display color options as a sub menu
  250. if ( ContentBrowserUtils::HasCustomColors() )
  251. {
  252. // Set Color (submenu)
  253. MenuBuilder.AddSubMenu(
  254. LOCTEXT("SetColor", "Set Color"),
  255. LOCTEXT("SetColorTooltip", "Sets the color this folder should appear as."),
  256. FNewMenuDelegate::CreateRaw( this, &FPathContextMenu::MakeSetColorSubMenu ),
  257. false,
  258. FSlateIcon()
  259. );
  260. }
  261. else
  262. {
  263. // Set Color
  264. MenuBuilder.AddMenuEntry(
  265. LOCTEXT("SetColor", "Set Color"),
  266. LOCTEXT("SetColorTooltip", "Sets the color this folder should appear as."),
  267. FSlateIcon(),
  268. FUIAction( FExecuteAction::CreateSP( this, &FPathContextMenu::ExecutePickColor ) )
  269. );
  270. }
  271. // If this folder is already favorited, show the option to remove from favorites
  272. if (ContentBrowserUtils::IsFavoriteFolder(SelectedPaths[0]))
  273. {
  274. // Remove from favorites
  275. MenuBuilder.AddMenuEntry(
  276. LOCTEXT("RemoveFromFavorites", "Remove From Favorites"),
  277. LOCTEXT("RemoveFromFavoritesTooltip", "Removes this folder from the favorites section."),
  278. FSlateIcon(FAppStyle::GetStyleSetName(), "PropertyWindow.Favorites_Disabled"),
  279. FUIAction(FExecuteAction::CreateSP(this, &FPathContextMenu::ExecuteFavorite))
  280. );
  281. }
  282. else
  283. {
  284. // Add to favorites
  285. MenuBuilder.AddMenuEntry(
  286. LOCTEXT("AddToFavorites", "Add To Favorites"),
  287. LOCTEXT("AddToFavoritesTooltip", "Adds this folder to the favorites section for easy access."),
  288. FSlateIcon(FAppStyle::GetStyleSetName(), "PropertyWindow.Favorites_Enabled"),
  289. FUIAction(FExecuteAction::CreateSP(this, &FPathContextMenu::ExecuteFavorite))
  290. );
  291. }
  292. #endif
  293. }
  294. MenuBuilder.EndSection();
  295. if(bHasAssetPaths)
  296. {
  297. // Bulk operations section //
  298. MenuBuilder.BeginSection("PathContextBulkOperations", LOCTEXT("AssetTreeBulkMenuHeading", "Bulk Operations") );
  299. {
  300. #if ECB_LEGACY
  301. // Save
  302. MenuBuilder.AddMenuEntry(FContentBrowserCommands::Get().SaveAllCurrentFolder, NAME_None,
  303. LOCTEXT("ImportFolder", "Import All"),
  304. LOCTEXT("SaveFolderTooltip", "Saves all modified assets in this folder.")
  305. );
  306. // Fix Up Redirectors in Folder
  307. MenuBuilder.AddMenuEntry(
  308. LOCTEXT("FixUpRedirectorsInFolder", "Fix Up Redirectors in Folder"),
  309. LOCTEXT("FixUpRedirectorsInFolderTooltip", "Finds referencers to all redirectors in the selected folders and resaves them if possible, then deletes any redirectors that had all their referencers fixed."),
  310. FSlateIcon(),
  311. FUIAction( FExecuteAction::CreateSP( this, &FPathContextMenu::ExecuteFixUpRedirectorsInFolder ) )
  312. );
  313. if ( NumAssetPaths == 1 && NumClassPaths == 0 )
  314. {
  315. // Migrate Folder
  316. MenuBuilder.AddMenuEntry(
  317. LOCTEXT("MigrateFolder", "Migrate..."),
  318. LOCTEXT("MigrateFolderTooltip", "Copies assets found in this folder and their dependencies to another game content folder."),
  319. FSlateIcon(),
  320. FUIAction( FExecuteAction::CreateSP( this, &FPathContextMenu::ExecuteMigrateFolder ) )
  321. );
  322. }
  323. #endif
  324. }
  325. MenuBuilder.EndSection();
  326. }
  327. }
  328. MenuBuilder.BeginSection("PathViewProjectOptions", LOCTEXT("PathViewProjectOptionsMenuHeading", "Project"));
  329. {
  330. if (CanExecuteApplyProjectFolderColors())
  331. {
  332. // Apply Project Folder Colors
  333. MenuBuilder.AddMenuEntry(
  334. LOCTEXT("ImportProjectColors", "Import Project Colors"),
  335. LOCTEXT("ImportProjectColorsTooltip", "Import folder colors from selected project in UAssetBrowser to current project.\n (Will override current project's folder color setting)"),
  336. FSlateIcon(),
  337. FUIAction(
  338. FExecuteAction::CreateSP(this, &FPathContextMenu::ExecuteApplyProjectFolderColors),
  339. FCanExecuteAction::CreateSP(this, &FPathContextMenu::CanExecuteApplyProjectFolderColors)
  340. )
  341. );
  342. }
  343. }
  344. MenuBuilder.EndSection();
  345. MenuBuilder.BeginSection("PathViewFolderListOptions", LOCTEXT("PathViewFolderListOptionsMenuHeading", "Folder List"));
  346. {
  347. // Save
  348. MenuBuilder.AddMenuEntry(
  349. LOCTEXT("Save", "Save"),
  350. LOCTEXT("SaveFolderListTooltip", "Save root content folder list"),
  351. FSlateIcon(),
  352. FUIAction(FExecuteAction::CreateSP(this, &FPathContextMenu::ExecuteExportRootFolderList))
  353. );
  354. // Load & Merge
  355. MenuBuilder.AddMenuEntry(
  356. LOCTEXT("LoadAndMergeFolderList", "Load & Merge"),
  357. LOCTEXT("LoadAndMergeFolderListTooltip", "Load root content folder list and merge with current folder list"),
  358. FSlateIcon(),
  359. FUIAction(FExecuteAction::CreateSP(this, &FPathContextMenu::ExecuteLoadAndMergeRootFolderList))
  360. );
  361. MenuBuilder.AddMenuEntry(
  362. LOCTEXT("LoadAndReplaceFolderList", "Load & Replace"),
  363. LOCTEXT("LoadAndReplaceFolderListTooltip", "Replace current root content folder with loaded list"),
  364. FSlateIcon(),
  365. FUIAction(FExecuteAction::CreateSP(this, &FPathContextMenu::ExecuteLoadAndReplaceRootFolderList))
  366. );
  367. }
  368. MenuBuilder.EndSection();
  369. }
  370. bool FPathContextMenu::CanCreateAsset() const
  371. {
  372. // We can only create assets when we have a single asset path selected
  373. return SelectedPaths.Num() == 1 && !ExtContentBrowserUtils::IsClassPath(SelectedPaths[0]);
  374. }
  375. void FPathContextMenu::MakeNewAssetSubMenu(FMenuBuilder& MenuBuilder)
  376. {
  377. #if ECB_LEGACY
  378. if ( SelectedPaths.Num() )
  379. {
  380. FNewAssetOrClassContextMenu::MakeContextMenu(
  381. MenuBuilder,
  382. SelectedPaths,
  383. OnNewAssetRequested,
  384. FNewAssetOrClassContextMenu::FOnNewClassRequested(),
  385. FNewAssetOrClassContextMenu::FOnNewFolderRequested(),
  386. OnImportAssetRequested,
  387. FNewAssetOrClassContextMenu::FOnGetContentRequested()
  388. );
  389. }
  390. #endif
  391. }
  392. void FPathContextMenu::MakeSetColorSubMenu(FMenuBuilder& MenuBuilder)
  393. {
  394. // New Color
  395. MenuBuilder.AddMenuEntry(
  396. LOCTEXT("NewColor", "New Color"),
  397. LOCTEXT("NewColorTooltip", "Changes the color this folder should appear as."),
  398. FSlateIcon(),
  399. FUIAction( FExecuteAction::CreateSP( this, &FPathContextMenu::ExecutePickColor ) )
  400. );
  401. // Clear Color (only required if any of the selection has one)
  402. if ( SelectedHasCustomColors() )
  403. {
  404. MenuBuilder.AddMenuEntry(
  405. LOCTEXT("ClearColor", "Clear Color"),
  406. LOCTEXT("ClearColorTooltip", "Resets the color this folder appears as."),
  407. FSlateIcon(),
  408. FUIAction( FExecuteAction::CreateSP( this, &FPathContextMenu::ExecuteResetColor ) )
  409. );
  410. }
  411. // Add all the custom colors the user has chosen so far
  412. TArray< FLinearColor > CustomColors;
  413. if ( ExtContentBrowserUtils::HasCustomColors( &CustomColors ) )
  414. {
  415. MenuBuilder.BeginSection("PathContextCustomColors", LOCTEXT("CustomColorsExistingColors", "Existing Colors") );
  416. {
  417. for ( int32 ColorIndex = 0; ColorIndex < CustomColors.Num(); ColorIndex++ )
  418. {
  419. const FLinearColor& Color = CustomColors[ ColorIndex ];
  420. MenuBuilder.AddWidget(
  421. SNew(SHorizontalBox)
  422. +SHorizontalBox::Slot()
  423. .AutoWidth()
  424. .Padding(2, 0, 0, 0)
  425. [
  426. SNew(SButton)
  427. .ButtonStyle( FAppStyle::Get(), "Menu.Button" )
  428. .OnClicked( this, &FPathContextMenu::OnColorClicked, Color )
  429. [
  430. SNew(SColorBlock)
  431. .Color( Color )
  432. .Size( FVector2D(77,16) )
  433. ]
  434. ],
  435. FText::GetEmpty(),
  436. /*bNoIndent=*/true
  437. );
  438. }
  439. }
  440. MenuBuilder.EndSection();
  441. }
  442. }
  443. void FPathContextMenu::ExecuteMigrateFolder()
  444. {
  445. const FString& SourcesPath = GetFirstSelectedPath();
  446. if ( ensure(SourcesPath.Len()) )
  447. {
  448. // @todo Make sure the asset registry has completed discovering assets, or else GetAssetsByPath() will not find all the assets in the folder! Add some UI to wait for this with a cancel button
  449. FAssetRegistryModule& AssetRegistryModule = FModuleManager::Get().LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
  450. if ( AssetRegistryModule.Get().IsLoadingAssets() )
  451. {
  452. FMessageDialog::Open(EAppMsgType::Ok, LOCTEXT( "MigrateFolderAssetsNotDiscovered", "You must wait until asset discovery is complete to migrate a folder" ));
  453. return;
  454. }
  455. // Get a list of package names for input into MigratePackages
  456. TArray<FExtAssetData> AssetDataList;
  457. TArray<FName> PackageNames;
  458. ExtContentBrowserUtils::GetAssetsInPaths(SelectedPaths, AssetDataList);
  459. for ( auto AssetIt = AssetDataList.CreateConstIterator(); AssetIt; ++AssetIt )
  460. {
  461. PackageNames.Add((*AssetIt).PackageName);
  462. }
  463. // Load all the assets in the selected paths
  464. FAssetToolsModule& AssetToolsModule = FModuleManager::Get().LoadModuleChecked<FAssetToolsModule>("AssetTools");
  465. AssetToolsModule.Get().MigratePackages( PackageNames );
  466. }
  467. }
  468. void FPathContextMenu::ExecuteExplore()
  469. {
  470. for (int32 PathIdx = 0; PathIdx < SelectedPaths.Num(); ++PathIdx)
  471. {
  472. const FString& Path = SelectedPaths[PathIdx];
  473. FString FilePath = Path;
  474. if (!FilePath.IsEmpty())
  475. {
  476. // If the folder has not yet been created, make is right before we try to explore to it
  477. #if 0
  478. if (!IFileManager::Get().DirectoryExists(*FilePath))
  479. {
  480. IFileManager::Get().MakeDirectory(*FilePath, /*Tree=*/true);
  481. }
  482. #endif
  483. FPlatformProcess::ExploreFolder(*FilePath);
  484. }
  485. }
  486. }
  487. void FPathContextMenu::ExecuteApplyProjectFolderColors()
  488. {
  489. const FString& SourcesPath = GetFirstSelectedPath();
  490. if (SourcesPath.Len())
  491. {
  492. FExtAssetImporter::ImportProjectFolderColors(SourcesPath);
  493. }
  494. }
  495. void FPathContextMenu::ExecuteValidateAssetsInFolder()
  496. {
  497. TArray<FExtAssetData*> AssetsInFolder;
  498. for (int32 PathIdx = 0; PathIdx < SelectedPaths.Num(); ++PathIdx)
  499. {
  500. const FString& Path = SelectedPaths[PathIdx];
  501. TArray<FExtAssetData*> Assets = FExtContentBrowserSingleton::GetAssetRegistry().GetCachedAssetsByFolder(*Path);
  502. AssetsInFolder.Append(Assets);
  503. }
  504. if (AssetsInFolder.Num() > 0)
  505. {
  506. FString ValidateResult;
  507. FExtAssetValidator::ValidateDependency(AssetsInFolder, &ValidateResult, /*bProgress*/ true);
  508. ExtContentBrowserUtils::NotifyMessage(ValidateResult);
  509. }
  510. }
  511. bool FPathContextMenu::CanExecuteRescanFolder() const
  512. {
  513. return bHasSelectedPath;
  514. }
  515. bool FPathContextMenu::CanExecuteImportFolder() const
  516. {
  517. return bHasSelectedPath;
  518. }
  519. bool FPathContextMenu::CanExecuteApplyProjectFolderColors() const
  520. {
  521. const FString& SourcesPath = GetFirstSelectedPath();
  522. if (SourcesPath.Len())
  523. {
  524. // FString AssetContentDir;
  525. // if (FExtContentBrowserSingleton::GetAssetRegistry().GetAssetContentRootDir(SourcesPath, AssetContentDir))
  526. // {
  527. // return FExtContentBrowserSingleton::GetAssetRegistry().GetCachedAssetContentRootConfigDirs().Contains(*AssetContentDir);
  528. // }
  529. TArray<FName> ContentRootHosts;
  530. FExtContentBrowserSingleton::GetAssetRegistry().GetCachedAssetContentRootHostDirs().GenerateKeyArray(ContentRootHosts);
  531. if (ContentRootHosts.Contains(*SourcesPath))
  532. {
  533. return true;
  534. }
  535. FExtAssetData::EContentType ContentType;
  536. if (FExtContentBrowserSingleton::GetAssetRegistry().IsRootFolder(SourcesPath)
  537. && FExtContentBrowserSingleton::GetAssetRegistry().QueryRootContentPathInfo(SourcesPath, nullptr, &ContentType)
  538. && (ContentType == FExtAssetData::EContentType::Project || ContentType == FExtAssetData::EContentType::Plugin)
  539. )
  540. {
  541. return true;
  542. }
  543. }
  544. return false;
  545. }
  546. void FPathContextMenu::ExecuteRescanFolder()
  547. {
  548. if (SelectedPaths.Num() > 0)
  549. {
  550. FExtContentBrowserSingleton::GetAssetRegistry().ReGatheringFolders(SelectedPaths);
  551. }
  552. }
  553. void FPathContextMenu::ExecuteImportFolder()
  554. {
  555. TArray<FExtAssetData*> AssetsInFolder;
  556. for (int32 PathIdx = 0; PathIdx < SelectedPaths.Num(); ++PathIdx)
  557. {
  558. const FString& Path = SelectedPaths[PathIdx];
  559. TArray<FExtAssetData*> Assets = FExtContentBrowserSingleton::GetAssetRegistry().GetCachedAssetsByFolder(*Path);
  560. AssetsInFolder.Append(Assets);
  561. }
  562. if (AssetsInFolder.Num() > 0)
  563. {
  564. TArray<FExtAssetData> AssetsToImport;
  565. for (FExtAssetData* AssetPtr : AssetsInFolder)
  566. {
  567. AssetsToImport.Emplace(*AssetPtr);
  568. }
  569. FExtAssetImporter::ImportAssets(AssetsToImport, FUAssetImportSetting::GetSavedImportSetting());
  570. }
  571. }
  572. bool FPathContextMenu::CanExecuteRootDirsActions() const
  573. {
  574. return bCanExecuteRootDirsActions;
  575. }
  576. void FPathContextMenu::ExecuteReloadRootFolder()
  577. {
  578. TArray<FString> Reloaded;
  579. if (FExtContentBrowserSingleton::GetAssetRegistry().ReloadRootFolders(GetSelectedPaths(), &Reloaded))
  580. {
  581. FString ReloadMessage = Reloaded.Num() == 0 ? TEXT("")
  582. : (Reloaded.Num() == 1
  583. ? FString::Printf(TEXT("%s reloaded. "), *Reloaded[0])
  584. : FString::Printf(TEXT("%s and other %d reloaded. "), *Reloaded[0], Reloaded.Num()));
  585. ExtContentBrowserUtils::NotifyMessage(FText::FromString(ReloadMessage), false, 3.f);
  586. }
  587. }
  588. void FPathContextMenu::ExecuteRemoveRootFolder()
  589. {
  590. TArray<FString> Removed;
  591. if (FExtContentBrowserSingleton::GetAssetRegistry().RemoveRootFolders(GetSelectedPaths(), &Removed))
  592. {
  593. FString RemovedMessage = Removed.Num() == 0 ? TEXT("")
  594. : (Removed.Num() == 1
  595. ? FString::Printf(TEXT("%s removed. "), *Removed[0])
  596. : FString::Printf(TEXT("%s and other %d removed. "), *Removed[0], Removed.Num()));
  597. ExtContentBrowserUtils::NotifyMessage(FText::FromString(RemovedMessage), false, 3.f);
  598. }
  599. }
  600. void FPathContextMenu::ExecuteAddRootFolder()
  601. {
  602. bool bFolderSelected = false;
  603. IDesktopPlatform* DesktopPlatform = FDesktopPlatformModule::Get();
  604. if (DesktopPlatform)
  605. {
  606. void* TopWindowHandle = FSlateApplication::Get().GetActiveTopLevelWindow().IsValid() ? FSlateApplication::Get().GetActiveTopLevelWindow()->GetNativeWindow()->GetOSWindowHandle() : nullptr;
  607. FString DefaultPath;
  608. FString FolderName;
  609. bFolderSelected = DesktopPlatform->OpenDirectoryDialog(
  610. TopWindowHandle,
  611. /*Title=*/TEXT("Select root content folder"),
  612. DefaultPath,
  613. FolderName
  614. );
  615. if (bFolderSelected)
  616. {
  617. ECB_LOG(Display, TEXT("Select a folder as root content folder: %s"), *FolderName);
  618. if (FExtContentBrowserSingleton::GetAssetRegistry().IsRootFolder(FolderName))
  619. {
  620. FString AlreadyExist = FString::Printf(TEXT("%s already exist. skip."), *FolderName);
  621. ExtContentBrowserUtils::NotifyMessage(FText::FromString(AlreadyExist), false, 3.f);
  622. return;
  623. }
  624. TArray<FString> Added;
  625. TArray<FString> Combined;
  626. if (FExtContentBrowserSingleton::GetAssetRegistry().AddRootFolder(FolderName, &Added, &Combined))
  627. {
  628. FString AddedMessage = Added.Num() == 0 ? TEXT("")
  629. : ( Added.Num() == 1
  630. ? FString::Printf(TEXT("%s added. "), *Added[0])
  631. : FString::Printf(TEXT("%s and other %d added. "), *Added[0], Added.Num()));
  632. FString CombinedMessage = Combined.Num() == 0 ? TEXT("")
  633. : (Combined.Num() == 1
  634. ? FString::Printf(TEXT("%s consolidated. "), *Combined[0])
  635. : FString::Printf(TEXT("%s and other %d consolidated. "), *Combined[0], Combined.Num()));
  636. AddedMessage = AddedMessage + CombinedMessage;
  637. ExtContentBrowserUtils::NotifyMessage(FText::FromString(AddedMessage), false, 3.f);
  638. }
  639. }
  640. }
  641. }
  642. void FPathContextMenu::ExecuteExportRootFolderList()
  643. {
  644. TArray<FString> RootContentPaths;
  645. FExtContentBrowserSingleton::GetAssetRegistry().QueryRootContentPaths(RootContentPaths);
  646. if (RootContentPaths.Num() == 0)
  647. {
  648. return;
  649. }
  650. IDesktopPlatform* DesktopPlatform = FDesktopPlatformModule::Get();
  651. if (DesktopPlatform)
  652. {
  653. const void* TopWindowHandle = FSlateApplication::Get().FindBestParentWindowHandleForDialogs(nullptr);
  654. TArray<FString> OutFilenames;
  655. FString DefaultPath;
  656. FString DefaultFile(TEXT("UAssetBrowserFolderList.txt"));
  657. const FString FileTypes = TEXT("Text Files (*.txt)|*.txt");
  658. const bool bSelected = DesktopPlatform->SaveFileDialog(
  659. TopWindowHandle,
  660. TEXT("Save root content folder list"),
  661. DefaultPath,
  662. DefaultFile,
  663. FileTypes,
  664. EFileDialogFlags::None,
  665. OutFilenames);
  666. if (bSelected && OutFilenames.Num() > 0)
  667. {
  668. FFileHelper::SaveStringArrayToFile(RootContentPaths, *OutFilenames[0]);
  669. }
  670. }
  671. }
  672. void FPathContextMenu::LoadRootFolderList(bool bReplaceCurrent)
  673. {
  674. IDesktopPlatform* DesktopPlatform = FDesktopPlatformModule::Get();
  675. if (DesktopPlatform)
  676. {
  677. const void* ParentWindowWindowHandle = FSlateApplication::Get().FindBestParentWindowHandleForDialogs(nullptr);
  678. const FText Title =
  679. bReplaceCurrent
  680. ? LOCTEXT("LoadRootFolderList", "Load root content folder list and replace current folder list")
  681. : LOCTEXT("LoadAndMergeRootFolderList", "Load and merge root content folder list");
  682. const FString FileTypes = TEXT("Text Files (*.txt)|*.txt");
  683. FString DefaultPath;
  684. FString DefaultFile(TEXT(""));
  685. TArray<FString> OutFilenames;
  686. DesktopPlatform->OpenFileDialog(
  687. ParentWindowWindowHandle,
  688. Title.ToString(),
  689. DefaultPath,
  690. DefaultFile,
  691. FileTypes,
  692. EFileDialogFlags::None,
  693. OutFilenames
  694. );
  695. if (OutFilenames.Num() == 1)
  696. {
  697. TArray<FString> FileContentLines;
  698. FString Message;
  699. if (!FFileHelper::LoadFileToStringArray(FileContentLines, *OutFilenames[0]))
  700. {
  701. Message = FString::Printf(TEXT("Couldn't read folder list file: %s"), *OutFilenames[0]);
  702. ECB_LOG(Error, TEXT("%s"), *Message);
  703. }
  704. else
  705. {
  706. if (bReplaceCurrent)
  707. {
  708. Message = FString::Printf(TEXT("%s loaded."), *OutFilenames[0]);
  709. FExtContentBrowserSingleton::GetAssetRegistry().ReplaceRootContentPathsWith(FileContentLines);
  710. }
  711. else
  712. {
  713. Message = FString::Printf(TEXT("%s loaded and merged."), *OutFilenames[0]);
  714. FExtContentBrowserSingleton::GetAssetRegistry().MergeRootContentPathsWith(FileContentLines, /*bReplace*/ false);
  715. }
  716. }
  717. ExtContentBrowserUtils::NotifyMessage(Message);
  718. }
  719. }
  720. }
  721. void FPathContextMenu::ExecuteLoadAndReplaceRootFolderList()
  722. {
  723. LoadRootFolderList(/*bReplaceCurrent*/ true);
  724. }
  725. void FPathContextMenu::ExecuteLoadAndMergeRootFolderList()
  726. {
  727. LoadRootFolderList(/*bReplaceCurrent*/ false);
  728. }
  729. bool FPathContextMenu::CanExecuteRename() const
  730. {
  731. return false;// ContentBrowserUtils::CanRenameFromPathView(SelectedPaths);
  732. }
  733. void FPathContextMenu::ExecuteRename()
  734. {
  735. check(SelectedPaths.Num() == 1);
  736. if (OnRenameFolderRequested.IsBound())
  737. {
  738. OnRenameFolderRequested.Execute(SelectedPaths[0]);
  739. }
  740. }
  741. void FPathContextMenu::ExecuteResetColor()
  742. {
  743. ResetColors();
  744. }
  745. void FPathContextMenu::ExecutePickColor()
  746. {
  747. if (SelectedPaths.Num() == 0)
  748. {
  749. return;
  750. }
  751. // Spawn a color picker, so the user can select which color they want
  752. FLinearColor InitialColor = ExtContentBrowserUtils::GetDefaultColor();
  753. if ( SelectedPaths.Num() > 0 )
  754. {
  755. // Make sure an color entry exists for all the paths, otherwise they won't update in realtime with the widget color
  756. for (int32 PathIdx = SelectedPaths.Num() - 1; PathIdx >= 0; --PathIdx)
  757. {
  758. const FString& Path = SelectedPaths[PathIdx];
  759. TSharedPtr<FLinearColor> Color = ExtContentBrowserUtils::LoadColor( Path );
  760. if ( Color.IsValid() )
  761. {
  762. // Default the color to the first valid entry
  763. InitialColor = *Color.Get();
  764. break;
  765. }
  766. }
  767. }
  768. FColorPickerArgs PickerArgs;
  769. PickerArgs.InitialColor = InitialColor;
  770. PickerArgs.bIsModal = false;
  771. PickerArgs.ParentWidget = ParentContent.Pin();
  772. PickerArgs.OnColorPickerWindowClosed = FOnWindowClosed::CreateSP(this, &FPathContextMenu::NewColorComplete);
  773. OpenColorPicker(PickerArgs);
  774. }
  775. void FPathContextMenu::ExecuteFavorite()
  776. {
  777. OnFolderFavoriteToggled.ExecuteIfBound(SelectedPaths);
  778. }
  779. void FPathContextMenu::NewColorComplete(const TSharedRef<SWindow>& Window)
  780. {
  781. // Save the colors back in the config (ptr should have already updated by the widget)
  782. for (int32 PathIdx = 0; PathIdx < SelectedPaths.Num(); ++PathIdx)
  783. {
  784. const FString& Path = SelectedPaths[PathIdx];
  785. const TSharedPtr<FLinearColor> Color = ExtContentBrowserUtils::LoadColor( Path );
  786. check( Color.IsValid() );
  787. ExtContentBrowserUtils::SaveColor( Path, Color );
  788. }
  789. }
  790. FReply FPathContextMenu::OnColorClicked( const FLinearColor InColor )
  791. {
  792. // Make sure an color entry exists for all the paths, otherwise it can't save correctly
  793. for (int32 PathIdx = 0; PathIdx < SelectedPaths.Num(); ++PathIdx)
  794. {
  795. const FString& Path = SelectedPaths[PathIdx];
  796. TSharedPtr<FLinearColor> Color = ExtContentBrowserUtils::LoadColor( Path );
  797. if ( !Color.IsValid() )
  798. {
  799. Color = MakeShareable( new FLinearColor() );
  800. }
  801. *Color.Get() = InColor;
  802. ExtContentBrowserUtils::SaveColor( Path, Color );
  803. }
  804. // Dismiss the menu here, as we can't make the 'clear' option appear if a folder has just had a color set for the first time
  805. FSlateApplication::Get().DismissAllMenus();
  806. return FReply::Handled();
  807. }
  808. void FPathContextMenu::ResetColors()
  809. {
  810. // Clear the custom colors for all the selected paths
  811. for (int32 PathIdx = 0; PathIdx < SelectedPaths.Num(); ++PathIdx)
  812. {
  813. const FString& Path = SelectedPaths[PathIdx];
  814. ExtContentBrowserUtils::SaveColor( Path, NULL );
  815. }
  816. }
  817. void FPathContextMenu::ExecuteSaveFolder()
  818. {
  819. // Get a list of package names in the selected paths
  820. TArray<FString> PackageNames;
  821. GetPackageNamesInSelectedPaths(PackageNames);
  822. // Form a list of packages from the assets
  823. TArray<UPackage*> Packages;
  824. for (int32 PackageIdx = 0; PackageIdx < PackageNames.Num(); ++PackageIdx)
  825. {
  826. UPackage* Package = FindPackage(NULL, *PackageNames[PackageIdx]);
  827. // Only save loaded and dirty packages
  828. if ( Package != NULL && Package->IsDirty() )
  829. {
  830. Packages.Add(Package);
  831. }
  832. }
  833. // Save all packages that were found
  834. if ( Packages.Num() )
  835. {
  836. ExtContentBrowserUtils::SavePackages(Packages);
  837. }
  838. }
  839. void FPathContextMenu::ExecuteResaveFolder()
  840. {
  841. // Get a list of package names in the selected paths
  842. TArray<FString> PackageNames;
  843. GetPackageNamesInSelectedPaths(PackageNames);
  844. TArray<UPackage*> Packages;
  845. for (const FString& PackageName : PackageNames)
  846. {
  847. UPackage* Package = FindPackage(nullptr, *PackageName);
  848. if (!Package)
  849. {
  850. Package = LoadPackage(nullptr, *PackageName, LOAD_None);
  851. }
  852. if (Package)
  853. {
  854. Packages.Add(Package);
  855. }
  856. }
  857. if (Packages.Num())
  858. {
  859. ExtContentBrowserUtils::SavePackages(Packages);
  860. }
  861. }
  862. bool FPathContextMenu::CanExecuteDelete() const
  863. {
  864. return false;// ContentBrowserUtils::CanDeleteFromPathView(SelectedPaths);
  865. }
  866. void FPathContextMenu::ExecuteDelete()
  867. {
  868. // Don't allow asset deletion during PIE
  869. if (GIsEditor)
  870. {
  871. UEditorEngine* Editor = GEditor;
  872. FWorldContext* PIEWorldContext = GEditor->GetPIEWorldContext();
  873. if (PIEWorldContext)
  874. {
  875. FNotificationInfo Notification(LOCTEXT("CannotDeleteAssetInPIE", "Assets cannot be deleted while in PIE."));
  876. Notification.ExpireDuration = 3.0f;
  877. FSlateNotificationManager::Get().AddNotification(Notification);
  878. return;
  879. }
  880. }
  881. check(SelectedPaths.Num() > 0);
  882. if (ParentContent.IsValid())
  883. {
  884. FText Prompt;
  885. if ( SelectedPaths.Num() == 1 )
  886. {
  887. Prompt = FText::Format(LOCTEXT("FolderDeleteConfirm_Single", "Delete folder '{0}'?"), FText::FromString(SelectedPaths[0]));
  888. }
  889. else
  890. {
  891. Prompt = FText::Format(LOCTEXT("FolderDeleteConfirm_Multiple", "Delete {0} folders?"), FText::AsNumber(SelectedPaths.Num()));
  892. }
  893. // Spawn a confirmation dialog since this is potentially a highly destructive operation
  894. FOnClicked OnYesClicked = FOnClicked::CreateSP( this, &FPathContextMenu::ExecuteDeleteFolderConfirmed );
  895. ExtContentBrowserUtils::DisplayConfirmationPopup(
  896. Prompt,
  897. LOCTEXT("FolderDeleteConfirm_Yes", "Delete"),
  898. LOCTEXT("FolderDeleteConfirm_No", "Cancel"),
  899. ParentContent.Pin().ToSharedRef(),
  900. OnYesClicked);
  901. }
  902. }
  903. void FPathContextMenu::ExecuteFixUpRedirectorsInFolder()
  904. {
  905. FAssetRegistryModule& AssetRegistryModule = FModuleManager::Get().LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
  906. // Form a filter from the paths
  907. FARFilter Filter;
  908. Filter.bRecursivePaths = true;
  909. for (const auto& Path : SelectedPaths)
  910. {
  911. Filter.PackagePaths.Emplace(*Path);
  912. Filter.ClassPaths.Emplace(FTopLevelAssetPath(TEXT("/Script/CoreUObject"), TEXT("ObjectRedirector")));
  913. }
  914. // Query for a list of assets in the selected paths
  915. TArray<FAssetData> AssetList;
  916. AssetRegistryModule.Get().GetAssets(Filter, AssetList);
  917. if (AssetList.Num() > 0)
  918. {
  919. TArray<FString> ObjectPaths;
  920. for (const auto& Asset : AssetList)
  921. {
  922. ObjectPaths.Add(Asset.GetSoftObjectPath().ToString());
  923. }
  924. TArray<UObject*> Objects;
  925. const bool bAllowedToPromptToLoadAssets = true;
  926. const bool bLoadRedirects = true;
  927. if (ExtContentBrowserUtils::LoadAssetsIfNeeded(ObjectPaths, Objects, bAllowedToPromptToLoadAssets, bLoadRedirects))
  928. {
  929. // Transform Objects array to ObjectRedirectors array
  930. TArray<UObjectRedirector*> Redirectors;
  931. for (auto Object : Objects)
  932. {
  933. auto Redirector = CastChecked<UObjectRedirector>(Object);
  934. Redirectors.Add(Redirector);
  935. }
  936. // Load the asset tools module
  937. FAssetToolsModule& AssetToolsModule = FModuleManager::LoadModuleChecked<FAssetToolsModule>(TEXT("AssetTools"));
  938. AssetToolsModule.Get().FixupReferencers(Redirectors);
  939. }
  940. }
  941. }
  942. FReply FPathContextMenu::ExecuteDeleteFolderConfirmed()
  943. {
  944. if ( ExtContentBrowserUtils::DeleteFolders(SelectedPaths) )
  945. {
  946. ResetColors();
  947. if (OnFolderDeleted.IsBound())
  948. {
  949. OnFolderDeleted.Execute();
  950. }
  951. }
  952. return FReply::Handled();
  953. }
  954. void FPathContextMenu::CacheCanExecuteVars()
  955. {
  956. bCanExecuteRootDirsActions = FExtContentBrowserSingleton::GetAssetRegistry().IsRootFolders(SelectedPaths);
  957. bHasSelectedPath = SelectedPaths.Num() > 0;
  958. #if ECB_LEGACY
  959. // Cache whether we can execute any of the source control commands
  960. bCanExecuteSCCCheckOut = false;
  961. bCanExecuteSCCOpenForAdd = false;
  962. bCanExecuteSCCCheckIn = false;
  963. ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider();
  964. if ( SourceControlProvider.IsEnabled() && SourceControlProvider.IsAvailable() )
  965. {
  966. TArray<FString> PackageNames;
  967. GetPackageNamesInSelectedPaths(PackageNames);
  968. // Check the SCC state for each package in the selected paths
  969. for ( auto PackageIt = PackageNames.CreateConstIterator(); PackageIt; ++PackageIt )
  970. {
  971. FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(SourceControlHelpers::PackageFilename(*PackageIt), EStateCacheUsage::Use);
  972. if(SourceControlState.IsValid())
  973. {
  974. if ( SourceControlState->CanCheckout() )
  975. {
  976. bCanExecuteSCCCheckOut = true;
  977. }
  978. else if ( !SourceControlState->IsSourceControlled() )
  979. {
  980. bCanExecuteSCCOpenForAdd = true;
  981. }
  982. else if ( SourceControlState->CanCheckIn() )
  983. {
  984. bCanExecuteSCCCheckIn = true;
  985. }
  986. }
  987. if ( bCanExecuteSCCCheckOut && bCanExecuteSCCOpenForAdd && bCanExecuteSCCCheckIn )
  988. {
  989. // All SCC options are available, no need to keep iterating
  990. break;
  991. }
  992. }
  993. }
  994. #endif
  995. }
  996. void FPathContextMenu::GetPackageNamesInSelectedPaths(TArray<FString>& OutPackageNames) const
  997. {
  998. // Load the asset registry module
  999. FAssetRegistryModule& AssetRegistryModule = FModuleManager::Get().LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
  1000. // Form a filter from the paths
  1001. FARFilter Filter;
  1002. Filter.bRecursivePaths = true;
  1003. for (int32 PathIdx = 0; PathIdx < SelectedPaths.Num(); ++PathIdx)
  1004. {
  1005. const FString& Path = SelectedPaths[PathIdx];
  1006. new (Filter.PackagePaths) FName(*Path);
  1007. }
  1008. // Query for a list of assets in the selected paths
  1009. TArray<FAssetData> AssetList;
  1010. AssetRegistryModule.Get().GetAssets(Filter, AssetList);
  1011. // Form a list of unique package names from the assets
  1012. TSet<FName> UniquePackageNames;
  1013. for (int32 AssetIdx = 0; AssetIdx < AssetList.Num(); ++AssetIdx)
  1014. {
  1015. UniquePackageNames.Add(AssetList[AssetIdx].PackageName);
  1016. }
  1017. // Add all unique package names to the output
  1018. for ( auto PackageIt = UniquePackageNames.CreateConstIterator(); PackageIt; ++PackageIt )
  1019. {
  1020. OutPackageNames.Add( (*PackageIt).ToString() );
  1021. }
  1022. }
  1023. FString FPathContextMenu::GetFirstSelectedPath() const
  1024. {
  1025. return SelectedPaths.Num() > 0 ? SelectedPaths[0] : TEXT("");
  1026. }
  1027. bool FPathContextMenu::SelectedHasCustomColors() const
  1028. {
  1029. for (int32 PathIdx = 0; PathIdx < SelectedPaths.Num(); ++PathIdx)
  1030. {
  1031. // Ignore any that are the default color
  1032. const FString& Path = SelectedPaths[PathIdx];
  1033. const TSharedPtr<FLinearColor> Color = ExtContentBrowserUtils::LoadColor( Path );
  1034. if ( Color.IsValid() && !Color->Equals( ExtContentBrowserUtils::GetDefaultColor() ) )
  1035. {
  1036. return true;
  1037. }
  1038. }
  1039. return false;
  1040. }
  1041. #undef LOCTEXT_NAMESPACE