/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ // Original file Copyright Crytek GMBH or its affiliates, used under license. #pragma once #include <QtCore/QObject> #include <vector> #include <map> #include <memory> #include <functional> #include "Strings.h" #include "Pointers.h" #include "functor.h" #include <ActionOutput.h> #include <SaveUtilities/AsyncSaveRunner.h> class QMimeData; namespace Serialization { class IArchive; struct SStruct; } struct SDBATable; namespace CharacterTool { using std::vector; using std::map; using std::unique_ptr; class AnimationList; class DependencyManager; class EditorDBATable; class EditorCompressionPresetTable; class SkeletonList; class CUndoStack; struct ActionContext; enum EExplorerSubtree { SUBTREE_CHARACTERS, SUBTREE_SKELETONS, SUBTREE_PHYSICS, SUBTREE_RIGS, SUBTREE_ANIMATIONS, SUBTREE_COMPRESSION, SUBTREE_SOURCE_ASSETS, NUM_SUBTREES }; enum EExplorerEntryType { ENTRY_NONE, ENTRY_SUBTREE_ROOT, ENTRY_GROUP, ENTRY_ANIMATION, ENTRY_SKELETON, ENTRY_CHARACTER, ENTRY_CHARACTER_SKELETON, ENTRY_PHYSICS, ENTRY_RIG, ENTRY_DBA_TABLE, ENTRY_COMPRESSION_PRESETS, ENTRY_SKELETON_LIST, ENTRY_SOURCE_ASSET, ENTRY_LOADING }; struct ExplorerEntry : _i_reference_target_t { EExplorerEntryType type; EExplorerSubtree subtree; string name; string path; bool modified; unsigned int id; const char* icon; bool isDragEnabled; ExplorerEntry* parent; vector<ExplorerEntry*> children; unique_ptr<CUndoStack> history; vector<unsigned int> columnValues; ExplorerEntry(EExplorerSubtree subtree, EExplorerEntryType type, unsigned int id); void Serialize(Serialization::IArchive& ar); void SetColumnValue(int column, unsigned int value); unsigned int GetColumnValue(int column) const; }; typedef vector<_smart_ptr<ExplorerEntry> > ExplorerEntries; typedef map<string, ExplorerEntry*, less_stricmp<string> > EntriesByPath; typedef map<unsigned int, ExplorerEntry*> EntriesById; struct FolderSubtree { _smart_ptr<ExplorerEntry> root; ExplorerEntries entries; ExplorerEntries groups; ExplorerEntries helpers; _smart_ptr<ExplorerEntry> loadingEntry; EntriesByPath groupsByPath; EntriesByPath entriesByPath; EntriesById entriesById; string commonPrefix; void Clear() { entries.clear(); groups.clear(); groupsByPath.clear(); entriesByPath.clear(); entriesById.clear(); commonPrefix.clear(); } }; struct ExplorerColumnValue { const char* tooltip; const char* icon; }; struct ExplorerColumn { enum Format { TEXT, FRAMES, FILESIZE, ICON }; string label; bool visibleByDefault; Format format; vector<ExplorerColumnValue> values; ExplorerColumn() : visibleByDefault(false) , format(TEXT) {} }; struct EntryModifiedEvent; struct ExplorerAction; typedef vector<ExplorerAction> ExplorerActions; class IExplorerEntryProvider; struct ExplorerEntryId; enum { ENTRY_PART_STATUS_COLUMNS = 1 << 0, ENTRY_PART_CONTENT = 1 << 1 }; struct ExplorerEntryModifyEvent { ExplorerEntry* entry; int entryParts; bool continuousChange; vector<ExplorerEntry*> dependentEntries; ExplorerEntryModifyEvent() : entry() , entryParts() , continuousChange(false) { } }; // Explorer aggregates available asset information in a uniform tree class Explorer : public QObject { Q_OBJECT public: Explorer(); ~Explorer(); void AddProvider(int subtree, IExplorerEntryProvider* provider); int AddColumn(const char* label, ExplorerColumn::Format format, bool visibleByDefault, const ExplorerColumnValue* values = 0, size_t numValues = 0); int FindColumn(const char* label) const; void Populate(); ExplorerEntry* GetRoot() { return m_root.get(); } int GetColumnCount() const; const char* GetColumnLabel(int column) const; const ExplorerColumn* GetColumn(int column) const; bool GetSerializerForEntry(Serialization::SStruct* out, ExplorerEntry* entry); void GetActionsForEntry(ExplorerActions* actions, ExplorerEntry* entry); void GetCommonActions(ExplorerActions* actions, const ExplorerEntries& entries); void SetEntryColumn(const ExplorerEntryId& id, int column, unsigned int value, bool notify); QMimeData* GetMimeDataForEntries(const std::vector<ExplorerEntry*> entries); void LoadEntries(const ExplorerEntries& currentEntries); void CheckIfModified(ExplorerEntry* entry, const char* reason, bool continuousChange); string GetFilePathForEntry(const ExplorerEntry* entry); bool IsEntryAvailableLocally(const ExplorerEntry* entry); ExplorerEntry* FindEntryById(const ExplorerEntryId& id); ExplorerEntry* FindEntryById(EExplorerSubtree subtree, unsigned int id); ExplorerEntry* FindEntryByPath(EExplorerSubtree subtree, const char* path); void FindEntriesByPath(vector<ExplorerEntry*>* entries, const char* path); string GetCanonicalPath(EExplorerSubtree subtree, const char* path) const; bool HasProvider(EExplorerSubtree subtree) const; void GetDependingEntries(vector<ExplorerEntry*>* entries, ExplorerEntry* entry); static const char* IconForEntry(EExplorerEntryType type, const ExplorerEntry* entry); void Revert(ExplorerEntry* entry); // Saves an entry and marks it for edit in source control. Because the source control operations are asynchronous, the // callback passed in will be triggered to after everything is complete and will tell you if the source control operation // and the save operation were both successful. void SaveEntry(ExplorerEntry* entry); void SaveEntry(ExplorerEntry* entry, const AZStd::shared_ptr<AZ::ActionOutput>& errorInfo); void SaveEntry(ExplorerEntry* entry, AZ::SaveCompleteCallback onSaveComplete); void SaveEntry(ExplorerEntry* entry, const AZStd::shared_ptr<AZ::ActionOutput>& errorInfo, AZ::SaveCompleteCallback onSaveComplete); // Saves all entries and marks them for edit in source control. Because the source control operations are asynchronous, the // callback passed in will be triggered to after everything is complete and will tell you if the source control operations // and the save operations were successful for every entry that needed to be saved. void SaveAll(); void SaveAll(const AZStd::shared_ptr<AZ::ActionOutput>& errorInfo); void SaveAll(AZ::SaveCompleteCallback onSaveComplete); void SaveAll(const AZStd::shared_ptr<AZ::ActionOutput>& errorInfo, AZ::SaveCompleteCallback onSaveComplete); void GetUnsavedEntries(ExplorerEntries* unsavedEntries); void GetSaveFilenames(std::vector<string>* filenames, const ExplorerEntries& entries) const; bool HasAtLeastOneUndoAvailable(const ExplorerEntries& explorerEntries); void UndoInOrder(const ExplorerEntries& entries); void RedoInOrder(const ExplorerEntries& entries); void Undo(const ExplorerEntries& entries, int count); bool CanUndo(const ExplorerEntries& entries) const; void Redo(const ExplorerEntries& entries); bool CanRedo(const ExplorerEntries& entries) const; void GetUndoActions(vector<string>* actionNames, int maxActionCount, const ExplorerEntries& entries) const; void ActionRevert(ActionContext& x); void ActionSave(ActionContext& x); void ActionShowInExplorer(ActionContext& x); void BeginBatchChange(int subtree) { SignalBeginBatchChange(subtree); } void EndBatchChange(int subtree) { SignalEndBatchChange(subtree); } signals: void SignalEntryModified(ExplorerEntryModifyEvent& ev); void SignalEntryLoaded(ExplorerEntry* entry); void SignalBeginAddEntry(ExplorerEntry* entry); void SignalEndAddEntry(); void SignalBeginRemoveEntry(ExplorerEntry* entry); void SignalEndRemoveEntry(); void SignalEntryImported(ExplorerEntry* entry, ExplorerEntry* oldEntry); void SignalRefreshFilter(); void SignalBeginBatchChange(int subtree); void SignalEndBatchChange(int subtree); void SignalEntrySavedAs(const char* oldPath, const char* newPath); void SignalSelectedEntryClicked(ExplorerEntry* entry); protected slots: void OnProviderSubtreeReset(int subtree); void OnProviderSubtreeLoadingFinished(int subtree); void OnProviderEntryModified(EntryModifiedEvent&); void OnProviderEntryAdded(int subtree, unsigned int id); void OnProviderEntryRemoved(int subtree, unsigned int id); void OnProviderBeginBatchChange(int subtree); void OnProviderEndBatchChange(int subtree); private: void EntryModified(ExplorerEntry* entry, bool continuousChange); void ResetSubtree(FolderSubtree* subtree, EExplorerSubtree subtreeIndex, const char* text, bool addLoadingEntry); void CreateGroups(ExplorerEntry* globalRoot, FolderSubtree* subtree, EExplorerSubtree subtreeIndex, EExplorerEntryType rootType); void UpdateSingleLevelGroups(FolderSubtree* subtree, EExplorerSubtree subtreeIndex); void UpdateGroups(); ExplorerEntry* CreateGroupsForPath(FolderSubtree* subtree, const char* animationPath, const char* commonPathPrefix); void RemoveEmptyGroups(FolderSubtree* subtree); bool UnlinkEntryFromParent(ExplorerEntry* entry); void RemoveEntry(ExplorerEntry* entry); void RemoveChildren(ExplorerEntry* entry); void LinkEntryToParent(ExplorerEntry* parent, ExplorerEntry* entry); void SetEntryState(ExplorerEntry* entry, const vector<char>& state); void GetEntryState(vector<char>* state, ExplorerEntry* entry); _smart_ptr<ExplorerEntry> m_root; FolderSubtree m_subtrees[NUM_SUBTREES]; unsigned long long m_undoCounter; vector<IExplorerEntryProvider*> m_providers; vector<IExplorerEntryProvider*> m_providerBySubtree; vector<ExplorerColumn> m_columns; unique_ptr<DependencyManager> m_dependencyManager; }; enum { ACTION_DISABLED = 1 << 0, ACTION_IMPORTANT = 1 << 1, ACTION_NOT_STACKABLE = 1 << 2 }; struct ActionContext { bool isAsync; AZStd::shared_ptr<AZ::ActionOutput> output; std::vector<ExplorerEntry*> entries; QWidget* window; ActionContext() : output(AZStd::make_shared<AZ::ActionOutput>()) , window() , isAsync(false) { } }; struct ExplorerAction { const char* icon; const char* text; const char* description; int flags; typedef AZStd::function<void(ActionContext&)> ActionFunction; ActionFunction func; ExplorerAction() : icon("") , text("") , description("") , flags(0) { } ExplorerAction(const char* text, int actionFlags, const ActionFunction& function, const char* icon = "", const char* description = "") : text(text) , flags(actionFlags) , icon(icon) , description(description) , func(function) { } }; class ExplorerActionHandler : public QObject { Q_OBJECT public: ExplorerActionHandler(const ExplorerAction& action) : m_action(action) { } public slots: void OnTriggered() { if (m_action.func) { SignalAction(m_action); } } signals: void SignalAction(const ExplorerAction& action); private: ExplorerAction m_action; }; }