├── .gitignore
├── Art
├── AddedStatusIcon.png
├── AddedStatusIcon.svg
├── CleanStatusIcon.png
├── CleanStatusIcon.svg
├── Icon128.svg
├── MissingStatusIcon.png
├── MissingStatusIcon.svg
├── ModifiedStatusIcon.png
├── ModifiedStatusIcon.svg
├── NotTrackedStatusIcon.png
├── NotTrackedStatusIcon.svg
├── RemovedStatusIcon.png
└── RemovedStatusIcon.svg
├── Content
└── SlateBrushes
│ ├── AddedStatusIcon.uasset
│ ├── CleanStatusIcon.uasset
│ ├── MissingStatusIcon.uasset
│ ├── ModifiedStatusIcon.uasset
│ ├── NotTrackedStatusIcon.uasset
│ └── RemovedStatusIcon.uasset
├── LICENSE
├── MercurialSourceControl.uplugin
├── README.md
├── Resources
└── Icon128.png
└── Source
└── MercurialSourceControl
├── MercurialSourceControl.Build.cs
└── Private
├── IMercurialSourceControlWorker.h
├── MercurialSourceControlClient.cpp
├── MercurialSourceControlClient.h
├── MercurialSourceControlCommand.cpp
├── MercurialSourceControlCommand.h
├── MercurialSourceControlFileRevision.cpp
├── MercurialSourceControlFileRevision.h
├── MercurialSourceControlFileState.cpp
├── MercurialSourceControlFileState.h
├── MercurialSourceControlModule.cpp
├── MercurialSourceControlModule.h
├── MercurialSourceControlOperationNames.cpp
├── MercurialSourceControlOperationNames.h
├── MercurialSourceControlPrivatePCH.h
├── MercurialSourceControlProvider.cpp
├── MercurialSourceControlProvider.h
├── MercurialSourceControlProviderSettings.cpp
├── MercurialSourceControlProviderSettings.h
├── MercurialSourceControlStyle.cpp
├── MercurialSourceControlStyle.h
├── MercurialSourceControlWorkers.cpp
├── MercurialSourceControlWorkers.h
├── SLargeAssetTypeTreeWidget.cpp
├── SLargeAssetTypeTreeWidget.h
├── SMercurialSourceControlSettingsWidget.cpp
└── SMercurialSourceControlSettingsWidget.h
/.gitignore:
--------------------------------------------------------------------------------
1 | Intermediate/
2 | Binaries/
--------------------------------------------------------------------------------
/Art/AddedStatusIcon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/enlight/ue4-hg-plugin/272554b7b6a67c815274486b50a4a800bfa09abb/Art/AddedStatusIcon.png
--------------------------------------------------------------------------------
/Art/AddedStatusIcon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
135 |
--------------------------------------------------------------------------------
/Art/CleanStatusIcon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/enlight/ue4-hg-plugin/272554b7b6a67c815274486b50a4a800bfa09abb/Art/CleanStatusIcon.png
--------------------------------------------------------------------------------
/Art/CleanStatusIcon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
112 |
--------------------------------------------------------------------------------
/Art/Icon128.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
98 |
--------------------------------------------------------------------------------
/Art/MissingStatusIcon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/enlight/ue4-hg-plugin/272554b7b6a67c815274486b50a4a800bfa09abb/Art/MissingStatusIcon.png
--------------------------------------------------------------------------------
/Art/MissingStatusIcon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
137 |
--------------------------------------------------------------------------------
/Art/ModifiedStatusIcon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/enlight/ue4-hg-plugin/272554b7b6a67c815274486b50a4a800bfa09abb/Art/ModifiedStatusIcon.png
--------------------------------------------------------------------------------
/Art/ModifiedStatusIcon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
112 |
--------------------------------------------------------------------------------
/Art/NotTrackedStatusIcon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/enlight/ue4-hg-plugin/272554b7b6a67c815274486b50a4a800bfa09abb/Art/NotTrackedStatusIcon.png
--------------------------------------------------------------------------------
/Art/NotTrackedStatusIcon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
133 |
--------------------------------------------------------------------------------
/Art/RemovedStatusIcon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/enlight/ue4-hg-plugin/272554b7b6a67c815274486b50a4a800bfa09abb/Art/RemovedStatusIcon.png
--------------------------------------------------------------------------------
/Art/RemovedStatusIcon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
131 |
--------------------------------------------------------------------------------
/Content/SlateBrushes/AddedStatusIcon.uasset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/enlight/ue4-hg-plugin/272554b7b6a67c815274486b50a4a800bfa09abb/Content/SlateBrushes/AddedStatusIcon.uasset
--------------------------------------------------------------------------------
/Content/SlateBrushes/CleanStatusIcon.uasset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/enlight/ue4-hg-plugin/272554b7b6a67c815274486b50a4a800bfa09abb/Content/SlateBrushes/CleanStatusIcon.uasset
--------------------------------------------------------------------------------
/Content/SlateBrushes/MissingStatusIcon.uasset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/enlight/ue4-hg-plugin/272554b7b6a67c815274486b50a4a800bfa09abb/Content/SlateBrushes/MissingStatusIcon.uasset
--------------------------------------------------------------------------------
/Content/SlateBrushes/ModifiedStatusIcon.uasset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/enlight/ue4-hg-plugin/272554b7b6a67c815274486b50a4a800bfa09abb/Content/SlateBrushes/ModifiedStatusIcon.uasset
--------------------------------------------------------------------------------
/Content/SlateBrushes/NotTrackedStatusIcon.uasset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/enlight/ue4-hg-plugin/272554b7b6a67c815274486b50a4a800bfa09abb/Content/SlateBrushes/NotTrackedStatusIcon.uasset
--------------------------------------------------------------------------------
/Content/SlateBrushes/RemovedStatusIcon.uasset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/enlight/ue4-hg-plugin/272554b7b6a67c815274486b50a4a800bfa09abb/Content/SlateBrushes/RemovedStatusIcon.uasset
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Vadim Macagon
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/MercurialSourceControl.uplugin:
--------------------------------------------------------------------------------
1 | {
2 | "FileVersion" : 3,
3 |
4 | "Version" : 3,
5 | "VersionName" : "0.3",
6 | "FriendlyName" : "Mercurial",
7 | "CreatedBy" : "Vadim Macagon",
8 | "CreatedByURL" : "https://github.com/enlight/ue4-hg-plugin",
9 | "Description" : "Allows the Editor to interact with the Mercurial distributed version control system. Supports basic add, remove, revert, commit operations, provides access to asset history, and enables built-in visual diffing of Blueprints.",
10 | "Category" : "Editor.Source Control",
11 | "EngineVersion" : "4.21.2",
12 |
13 | "Modules" :
14 | [
15 | {
16 | "Name" : "MercurialSourceControl",
17 | "Type" : "Editor"
18 | }
19 | ],
20 |
21 | "CanContainContent" : true,
22 | "IsBetaVersion" : true
23 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ue4-hg-plugin (alpha)
2 |
3 | A basic Mercurial source control plugin for Unreal Engine 4, available under the MIT license.
4 | The master branch can be built against UE 4.7.
5 |
6 | ## Overview
7 |
8 | The Unreal Editor has built-in visual diffing for Blueprint assets, this feature relies on a **Source Control Provider** plugin to interface with the repository your assets are stored in. Currently UE4 ships with built-in source control provider plugins for SVN and Perforce. This source control provider plugin allows the Unreal Editor to interact with a Mercurial repository, thus unlocking all the built-in asset diffing goodness for those of us who prefer to use Mercurial.
9 |
10 | ## Supported Operating Systems
11 |
12 | The plugin should work on any OS the Unreal Editor can run on, however, I do all my development on Windows so if something isn't working right on another OS please let me know how to fix it :).
13 |
14 | ## Installation
15 |
16 | ### Prerequisites
17 | You need Mercurial installed on your system, preferably a standalone version that doesn't rely on Python (though that may work too, I just haven't tried). On Windows I'd recommend installing [TortoiseHg](http://tortoisehg.bitbucket.org/), the plugin will then auto-detect the location of the relevant Mercurial executable.
18 |
19 | ### Using the Binary UE4 Release
20 | When using a binary release of UE4 you can extract a binary release of the plugin (if available) to either of the following locations:
21 | >```/4.7/Engine/Plugins/Editor/MercurialSourceControl/```
22 | >
23 | >```/Plugins/Editor/MercurialSourceControl/```
24 |
25 | If you extract the plugin binaries into your project's plugins directory it will only be available for that project.
26 |
27 | Alternatively, you can either clone or extract the plugin source to your project's plugins directory, which is covered next. Note that placing the plugin source into the engine plugins directory probably won't work because I don't think the binary UE4 release is configured to build engine plugins from source (but I haven't tried yet).
28 |
29 | ### Using the GitHub UE4 Release
30 | If you'd like the plugin to be available for all your UE4 projects you need to clone or extract the plugin source to:
31 | >```/Engine/Plugins/Editor/MercurialSourceControl/```
32 |
33 | Then follow these steps on Windows (adjust as needed on other OSes):
34 |
35 | 1. Run **GenerateProjectFiles.bat** in the UE4 source directory.
36 | 2. Open the generated **UE4.sln** Visual Studio solution file and build it.
37 | 3. Launch the Unreal Editor, open any project, and follow the instructions in the next section.
38 |
39 | Alternatively, if you only want to make the plugin available for a single project clone or extract the plugin source to:
40 |
41 | >```/Plugins/Editor/MercurialSourceControl/```
42 |
43 | Then follow these steps on Windows (adjust as needed on other OSes):
44 |
45 | 1. Right-click on the **.uproject** file in Windows Explorer (e.g. **MyProject.uproject**) in your root project directory and select **Generate Visual Studio Files**.
46 | 2. Open the generated Visual Studio solution file (e.g. **MyProject.sln**) and build it.
47 | 3. Launch the Unreal Editor, open the project you've just built, and follow the instructions in the next section.
48 |
49 | Note that your existing project must have a **Source** subdirectory with a couple of **.Target.cs** files in it, if it doesn't you may need to follow the steps in the **Building from Scratch** section below and then copy the built plugin into your project(s).
50 |
51 | ### Editor Configuration
52 | Once you've got a binary version of the plugin (either by building or downloading) follow these steps:
53 |
54 | 1. Open **Window->Plugins** from the main menu of the Unreal Editor.
55 | 2. Navigate to the **Built-in/Editor/Source Control** or the **Installed/Editor/Source Control** sub-category, you should see the **Mercurial** plugin in the list.
56 | 3. Enable the plugin and restart the editor if requested to do so.
57 | 4. Click on the **circular red icon** in the top right corner of the Unreal Editor.
58 | 5. Select **Mercurial** from the drop-down.
59 | 6. If you installed [TortoiseHg](http://tortoisehg.bitbucket.org/) the **Mercurial Executable** should've been auto-detected, otherwise you need to specify the location of the Mercurial executable (hg.exe on Windows, may be just hg elsewhere).
60 | 7. Press the **Accept Settings** button to enable the Mercurial source control provider.
61 |
62 | ## Building from Scratch
63 |
64 | The following steps explain how to build the plugin as part of a new (mostly) empty project, in case you hit any issues while attempting to build it as part of an existing project, or as an engine plugin.
65 |
66 | 1. Create a new **Basic Code (C++)** project from the UE4 editor, e.g. **MyProject**, close the editor.
67 | 2. Create a new subdirectory called **Plugins** in your root project directory, e.g. **MyProject/Plugins**.
68 | 3. Clone or extract the code for this plugin into a subdirectory within the **Plugins** directory, e.g. **MyProject/Plugins/MercurialSourceControl**.
69 | 4. On Windows right-click on the **.uproject** file (e.g. **MyProject.uproject**) in your root project directory and select **Generate Visual Studio Files**.
70 | 5. On Windows open the generated Visual Studio solution file (e.g. **MyProject.sln**) and build it.
71 | 6. Launch the UE4 editor and open the project you created in step 1.
72 | 7. Open **Window->Plugins** from the main menu, navigate to the **Installed/Editor** category and you should see the **Mercurial** plugin in the list.
73 |
74 | If you'd like to build this plugin within an existing project just skip step 1, note that your existing project must have a **Source** subdirectory with a couple of **.Target.cs** files in it.
75 |
--------------------------------------------------------------------------------
/Resources/Icon128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/enlight/ue4-hg-plugin/272554b7b6a67c815274486b50a4a800bfa09abb/Resources/Icon128.png
--------------------------------------------------------------------------------
/Source/MercurialSourceControl/MercurialSourceControl.Build.cs:
--------------------------------------------------------------------------------
1 | namespace UnrealBuildTool.Rules
2 | {
3 | public class MercurialSourceControl : ModuleRules
4 | {
5 | public MercurialSourceControl(ReadOnlyTargetRules Target) : base(Target)
6 | {
7 | PrivatePCHHeaderFile = "Private/MercurialSourceControlPrivatePCH.h";
8 | PublicIncludePaths.AddRange(
9 | new string[] {
10 | // ... add public include paths required here ...
11 | }
12 | );
13 |
14 | PrivateIncludePaths.AddRange(
15 | new string[] {
16 | // ... add other private include paths required here ...
17 | }
18 | );
19 |
20 | PublicDependencyModuleNames.AddRange(
21 | new string[]
22 | {
23 | // ... add other public dependencies that you statically link with here ...
24 | }
25 | );
26 |
27 | PrivateDependencyModuleNames.AddRange(
28 | new string[]
29 | {
30 | "Core",
31 | "Slate",
32 | "SlateCore",
33 | "EditorStyle",
34 | "SourceControl",
35 | "XmlParser",
36 | "InputCore",
37 | "DesktopPlatform",
38 | "AssetTools",
39 | "CoreUObject",
40 | "AssetRegistry",
41 | "UnrealEd"
42 | // ... add private dependencies that you statically link with here ...
43 | }
44 | );
45 |
46 | DynamicallyLoadedModuleNames.AddRange(
47 | new string[]
48 | {
49 | // ... add any modules that your module loads dynamically here ...
50 | }
51 | );
52 | }
53 | }
54 | }
--------------------------------------------------------------------------------
/Source/MercurialSourceControl/Private/IMercurialSourceControlWorker.h:
--------------------------------------------------------------------------------
1 | //-------------------------------------------------------------------------------
2 | // The MIT License (MIT)
3 | //
4 | // Copyright (c) 2014 Vadim Macagon
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //-------------------------------------------------------------------------------
24 | #pragma once
25 |
26 | namespace MercurialSourceControl {
27 |
28 | /**
29 | * Interface for objects that do all the actual work of interfacing with Mercurial
30 | * to get things done.
31 | */
32 | class IWorker
33 | {
34 | public:
35 | /**
36 | * Get a distinct name for the operation performed by the worker.
37 | * @note Must be unique for each class that implements this interface.
38 | */
39 | virtual FName GetName() const = 0;
40 |
41 | /**
42 | * Perform the source control operation.
43 | * @note May be called on another thread.
44 | */
45 | virtual bool Execute(class FCommand& InCommand) = 0;
46 |
47 | /**
48 | * Update the state of any affected items after completion of the operation.
49 | * @note Always called on the main thread.
50 | */
51 | virtual bool UpdateStates() const = 0;
52 |
53 | virtual ~IWorker() = 0 {};
54 | };
55 |
56 | typedef TSharedRef FWorkerRef;
57 | typedef TSharedPtr FWorkerPtr;
58 |
59 | } // namespace MercurialSourceControl
60 |
--------------------------------------------------------------------------------
/Source/MercurialSourceControl/Private/MercurialSourceControlClient.cpp:
--------------------------------------------------------------------------------
1 | //-------------------------------------------------------------------------------
2 | // The MIT License (MIT)
3 | //
4 | // Copyright (c) 2014 Vadim Macagon
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //-------------------------------------------------------------------------------
24 |
25 | #include "MercurialSourceControlPrivatePCH.h"
26 | #include "MercurialSourceControlClient.h"
27 | #include "ISourceControlModule.h"
28 | #include "XmlParser.h"
29 | #include "PlatformFilemanager.h"
30 | #include "WindowsHWrapper.h"
31 |
32 | // WinBase.h defines GetUserName conflicting with ISourceControlRevision::GetUserName and leads to obscure errors.
33 | // The line bellow prevents this error.
34 | #undef GetUserName
35 |
36 | namespace MercurialSourceControl {
37 |
38 | #define LOCTEXT_NAMESPACE "MercurialSourceControl.Client"
39 |
40 | FClientSharedPtr FClient::Singleton;
41 |
42 | /**
43 | * Creates a temp file on disk that is bound to the lifetime of an FScopedTempFile instance.
44 | * When an FScopedTempFile instance is destroyed the temp file it created is deleted from disk.
45 | */
46 | class FScopedTempFile
47 | {
48 | public:
49 | FScopedTempFile(const FString& InExtension)
50 | : Extension(InExtension)
51 | {
52 | }
53 |
54 | ~FScopedTempFile()
55 | {
56 | if (!Filename.IsEmpty())
57 | {
58 | IFileManager::Get().Delete(*Filename);
59 | }
60 | }
61 |
62 | const FString& GetFilename()
63 | {
64 | if (Filename.IsEmpty())
65 | {
66 | FString OutputDir = FPaths::ProjectLogDir();
67 | FPaths::NormalizeDirectoryName(OutputDir);
68 | Filename = FPaths::CreateTempFilename(*OutputDir, TEXT("hg-"), *Extension);
69 | Filename = FPaths::ConvertRelativePathToFull(Filename);
70 | }
71 | return Filename;
72 | }
73 |
74 | private:
75 | FString Filename;
76 | FString Extension;
77 | };
78 |
79 | bool FClient::IsValidExecutable(const FString& InFilename)
80 | {
81 | if (FPaths::FileExists(InFilename))
82 | {
83 | int32 ReturnCode = 0;
84 | FString Output;
85 | FString Error;
86 |
87 | FPlatformProcess::ExecProcess(*InFilename, TEXT("version"), &ReturnCode, &Output, &Error);
88 | return (ReturnCode == 0) && Output.Contains(TEXT("Mercurial"));
89 | }
90 | return false;
91 | }
92 |
93 | bool FClient::FindExecutable(FString& OutFilename)
94 | {
95 | OutFilename.Empty();
96 |
97 | #if PLATFORM_WINDOWS
98 | // look for the hg.exe that's shipped with TortoiseHg
99 | const TCHAR* SubKey = TEXT("Software\\TortoiseHg");
100 | const TCHAR* ValueName = TEXT("");
101 | FString HgPath;
102 |
103 | if (FPlatformMisc::QueryRegKey(HKEY_CURRENT_USER, SubKey, ValueName, HgPath) ||
104 | FPlatformMisc::QueryRegKey(HKEY_LOCAL_MACHINE, SubKey, ValueName, HgPath))
105 | {
106 | HgPath /= TEXT("hg.exe");
107 | if (IsValidExecutable(HgPath))
108 | {
109 | OutFilename = HgPath;
110 | }
111 | }
112 | #endif // PLATFORM_WINDOWS
113 |
114 | return !OutFilename.IsEmpty();
115 | }
116 |
117 | bool FClient::Create(const FString& InMercurialPath, FText& OutError)
118 | {
119 | if (ensure(!Singleton.IsValid()))
120 | {
121 | FString ExePath = InMercurialPath;
122 |
123 | bool bExeFound = ExePath.IsEmpty() ?
124 | FClient::FindExecutable(ExePath) : FClient::IsValidExecutable(ExePath);
125 |
126 | if (bExeFound)
127 | {
128 | Singleton = MakeShareable(new FClient(ExePath));
129 | }
130 | else
131 | {
132 | OutError = LOCTEXT("ExeNotFound", "Failed to locate a valid Mercurial executable.");
133 | }
134 | }
135 | return Singleton.IsValid();
136 | }
137 |
138 | const FClientSharedPtr& FClient::Get()
139 | {
140 | return Singleton;
141 | }
142 |
143 | void FClient::Destroy()
144 | {
145 | Singleton.Reset();
146 | }
147 |
148 | bool FClient::GetRepositoryRoot(const FString& InWorkingDirectory, FString& OutRepositoryRoot) const
149 | {
150 | FString Output;
151 | TArray None;
152 | TArray Errors;
153 | if (RunCommand(TEXT("root"), None, InWorkingDirectory, None, false, Output, Errors))
154 | {
155 | Output.RemoveFromEnd(TEXT("\n"));
156 | OutRepositoryRoot = Output;
157 | FPaths::NormalizeDirectoryName(OutRepositoryRoot);
158 | OutRepositoryRoot += TEXT("/");
159 | return true;
160 | }
161 | return false;
162 | }
163 |
164 | bool FClient::GetFileStates(
165 | const FString& InWorkingDirectory, const TArray& InAbsoluteFiles,
166 | TArray& OutFileStates, TArray& OutErrors
167 | ) const
168 | {
169 | TArray RelativeFiles;
170 | // convert absolute paths to be relative to the working directory
171 | for (const auto& AbsoluteFilename : InAbsoluteFiles)
172 | {
173 | // TODO: Consider logging a warning if the relative path can't be deduced.
174 | // Unfortunately UnrealEd has a tendency to pass in paths to built-in engine content,
175 | // and if the end user creates their project on a different drive to the one the
176 | // engine is installed on those paths can't be converted to be relative to the
177 | // project's repository working directory.
178 | FString Filename = AbsoluteFilename;
179 | if (FPaths::MakePathRelativeTo(Filename, *InWorkingDirectory))
180 | {
181 | RelativeFiles.Add(Filename);
182 | }
183 | }
184 |
185 | if (RelativeFiles.Num() == 0)
186 | {
187 | return true;
188 | }
189 |
190 | TArray Options;
191 | // show all modified, added, removed, deleted, unknown, clean, and ignored files
192 | Options.Add(TEXT("-marduci"));
193 | FString Output;
194 |
195 | if (RunCommand(TEXT("status"), Options, InWorkingDirectory, RelativeFiles, false, Output, OutErrors))
196 | {
197 | TArray Lines;
198 | Output.ParseIntoArray(Lines, TEXT("\n"), true);
199 | for (const auto& Line : Lines)
200 | {
201 | // each line consists of a one character status code followed by a filename,
202 | // a single space separates the status code from the filename
203 | FString Filename = Line.RightChop(2);
204 | FPaths::NormalizeFilename(Filename);
205 | FFileState FileState(InWorkingDirectory / Filename);
206 | FileState.SetFileStatus(StatusCodeToFileStatus(Line[0]));
207 | OutFileStates.Add(FileState);
208 | }
209 | return true;
210 | }
211 | return false;
212 | }
213 |
214 | bool FClient::GetFileHistory(
215 | const FString& InWorkingDirectory, const TArray& InAbsoluteFiles,
216 | TMap >& OutFileRevisionsMap, TArray& OutErrors
217 | ) const
218 | {
219 | TArray RelativeFiles;
220 | if (!ConvertFilesToRelative(InWorkingDirectory, InAbsoluteFiles, RelativeFiles))
221 | {
222 | // FIXME: Instead of quiting as soon as we get an invalid filename keep going!
223 | return false;
224 | }
225 |
226 | bool bResult = true;
227 | TArray Options;
228 | Options.Add(TEXT("--encoding utf-8"));
229 | Options.Add(TEXT("--style xml"));
230 | // verbose: all changes and full commit messages
231 | Options.Add(TEXT("-v"));
232 | FXmlFile XmlFile;
233 |
234 | for (const auto& RelativeFile : RelativeFiles)
235 | {
236 | FString Output;
237 | if (RunCommand(TEXT("log"), Options, InWorkingDirectory, RelativeFile, Output, OutErrors))
238 | {
239 | if (XmlFile.LoadFile(Output, EConstructMethod::ConstructFromBuffer))
240 | {
241 | TArray FileRevisions;
242 | GetFileRevisionsFromXml(RelativeFile, XmlFile, FileRevisions);
243 | if (FileRevisions.Num() > 0)
244 | {
245 | FString AbsoluteFile = InWorkingDirectory / RelativeFile;
246 | for (const auto& Revision : FileRevisions)
247 | {
248 | Revision->SetFilename(AbsoluteFile);
249 | }
250 | OutFileRevisionsMap.Add(AbsoluteFile, FileRevisions);
251 | }
252 | }
253 | }
254 | else
255 | {
256 | bResult = false;
257 | }
258 | }
259 | return bResult;
260 | }
261 |
262 | bool FClient::ExtractFileFromRevision(
263 | const FString& InWorkingDirectory, int32 RevisionNumber, const FString& InFileToExtract,
264 | const FString& InDestinationFile, TArray& OutErrors
265 | ) const
266 | {
267 | FString Filename = InFileToExtract;
268 | if (!FPaths::MakePathRelativeTo(Filename, *InWorkingDirectory))
269 | {
270 | return false;
271 | }
272 |
273 | TArray Options;
274 | Options.Add(FString::Printf(TEXT("--rev %d"), RevisionNumber));
275 | Options.Add(FString(TEXT("--output ")) + QuoteFilename(InDestinationFile));
276 | FString Output;
277 |
278 | return RunCommand(
279 | TEXT("cat"), Options, InWorkingDirectory, Filename, Output, OutErrors
280 | );
281 | }
282 |
283 | bool FClient::AddFiles(
284 | const FString& InWorkingDirectory, const TArray& InAbsoluteFiles, bool bInAddAsLarge,
285 | TArray& OutErrors
286 | ) const
287 | {
288 | TArray RelativeFiles;
289 | if (!ConvertFilesToRelative(InWorkingDirectory, InAbsoluteFiles, RelativeFiles))
290 | {
291 | return false;
292 | }
293 |
294 | TArray Options;
295 | if (bInAddAsLarge)
296 | {
297 | Options.Add("--large");
298 | }
299 | FString Output;
300 |
301 | return RunCommand(TEXT("add"), Options, InWorkingDirectory, RelativeFiles, false, Output, OutErrors);
302 | }
303 |
304 | bool FClient::RevertFiles(
305 | const FString& InWorkingDirectory, const TArray& InAbsoluteFiles,
306 | TArray& OutErrors
307 | ) const
308 | {
309 | TArray RelativeFiles;
310 | if (!ConvertFilesToRelative(InWorkingDirectory, InAbsoluteFiles, RelativeFiles))
311 | {
312 | return false;
313 | }
314 |
315 | TArray Options;
316 | // TODO: It would be a good idea to allow users to toggle this option via the source control
317 | // provider settings panel/dialog.
318 | Options.Add(TEXT("--no-backup"));
319 | FString Output;
320 |
321 | return RunCommand(
322 | TEXT("revert"), Options, InWorkingDirectory, RelativeFiles, false, Output, OutErrors
323 | );
324 | }
325 |
326 | bool FClient::RemoveFiles(
327 | const FString& InWorkingDirectory, const TArray& InAbsoluteFiles,
328 | TArray& OutErrors
329 | ) const
330 | {
331 | TArray RelativeFiles;
332 | if (!ConvertFilesToRelative(InWorkingDirectory, InAbsoluteFiles, RelativeFiles))
333 | {
334 | return false;
335 | }
336 |
337 | TArray Options;
338 | FString Output;
339 |
340 | return RunCommand(
341 | TEXT("remove"), Options, InWorkingDirectory, RelativeFiles, false, Output, OutErrors
342 | );
343 | }
344 |
345 | bool FClient::RemoveAllFiles(
346 | const FString& InWorkingDirectory, const TArray& InAbsoluteFiles,
347 | TArray& OutErrors
348 | ) const
349 | {
350 | // The idea here is to emulate the functionality of "svn delete", which works slightly
351 | // differently to "hg remove". The difference being SVN will delete files with a status of
352 | // "added" from the disk, but HG will not (it expects you to use "hg forget" first and then
353 | // delete the file from disk manually).
354 |
355 | // first we need to figure out what the status of each file we need to remove is
356 | TArray FileStates;
357 | if (!GetFileStates(InWorkingDirectory, InAbsoluteFiles, FileStates, OutErrors))
358 | {
359 | return false;
360 | }
361 |
362 | // now we can split out the "added" files that need special handling from the rest
363 | TArray AddedFiles;
364 | TArray RemovableFiles;
365 | for (const auto& FileState : FileStates)
366 | {
367 | switch (FileState.GetFileStatus())
368 | {
369 | case EFileStatus::Added:
370 | AddedFiles.Add(FileState.GetFilename());
371 | break;
372 |
373 | case EFileStatus::Clean:
374 | case EFileStatus::Missing:
375 | RemovableFiles.Add(FileState.GetFilename());
376 | break;
377 | }
378 | }
379 |
380 | bool bResult = true;
381 |
382 | // forget and delete added files
383 | if (AddedFiles.Num() > 0)
384 | {
385 | TArray RelativeFiles;
386 | if (!ConvertFilesToRelative(InWorkingDirectory, AddedFiles, RelativeFiles))
387 | {
388 | return false;
389 | }
390 |
391 | TArray Options;
392 | FString Output;
393 |
394 | bResult &= RunCommand(
395 | TEXT("forget"), Options, InWorkingDirectory, RelativeFiles, false, Output, OutErrors
396 | );
397 |
398 | for (const auto& Filename : AddedFiles)
399 | {
400 | bResult &= IFileManager::Get().Delete(*Filename);
401 | }
402 | }
403 |
404 | // remove any other removable files
405 | if (RemovableFiles.Num() > 0)
406 | {
407 | bResult &= RemoveFiles(InWorkingDirectory, RemovableFiles, OutErrors);
408 | }
409 |
410 | return bResult;
411 | }
412 |
413 | bool FClient::CommitFiles(
414 | const FString& InWorkingDirectory, const TArray& InAbsoluteFiles,
415 | const FString& InCommitMessage, TArray& OutErrors
416 | ) const
417 | {
418 | TArray RelativeFiles;
419 | if (!ConvertFilesToRelative(InWorkingDirectory, InAbsoluteFiles, RelativeFiles))
420 | {
421 | return false;
422 | }
423 |
424 | auto Encoding = FCString::IsPureAnsi(*InCommitMessage) ?
425 | FFileHelper::EEncodingOptions::ForceAnsi : FFileHelper::EEncodingOptions::ForceUTF8;
426 |
427 | // write the commit message to a temp file
428 | FScopedTempFile CommitMessageFile(TEXT(".txt"));
429 | bool bResult = FFileHelper::SaveStringToFile(
430 | InCommitMessage, *CommitMessageFile.GetFilename(), Encoding
431 | );
432 |
433 | if (!bResult)
434 | {
435 | OutErrors.Add(FString::Printf(
436 | TEXT("Failed to write to temp file: %s"), *CommitMessageFile.GetFilename()
437 | ));
438 | return false;
439 | }
440 |
441 | TArray Options;
442 | if (Encoding == FFileHelper::EEncodingOptions::ForceUTF8)
443 | {
444 | Options.Add(FString(TEXT("--encoding utf-8")));
445 | }
446 | Options.Add(FString(TEXT("--logfile ")) + QuoteFilename(CommitMessageFile.GetFilename()));
447 | FString Output;
448 |
449 | return RunCommand(
450 | TEXT("commit"), Options, InWorkingDirectory, RelativeFiles, true, Output, OutErrors
451 | );
452 | }
453 |
454 | bool FClient::GetWorkingDirectoryParentRevisionID(
455 | const FString& InWorkingDirectory, FString& OutRevisionID, TArray& OutErrors
456 | ) const
457 | {
458 | TArray Options;
459 | // just grab the local revision number
460 | Options.Add(FString(TEXT("--template \"{rev}\"")));
461 | FString Command(TEXT("parents"));
462 | AppendCommandOptions(Command, Options, InWorkingDirectory);
463 | return RunCommand(Command, OutRevisionID, OutErrors);
464 | }
465 |
466 | void FClient::AppendCommandOptions(
467 | FString& InOutCommand, const TArray& InOptions, const FString& InWorkingDirectory
468 | )
469 | {
470 | for (int32 i = 0; i < InOptions.Num(); ++i)
471 | {
472 | InOutCommand += TEXT(" ");
473 | InOutCommand += InOptions[i];
474 | }
475 |
476 | // run in non-interactive mode
477 | // (not strictly necessary as hg should detect the lack of a terminal, but just in case)
478 | InOutCommand += TEXT(" -y");
479 |
480 | // set the current working directory to the current game's content root
481 | InOutCommand += TEXT(" --cwd ");
482 | InOutCommand += QuoteFilename(InWorkingDirectory);
483 | }
484 |
485 | void FClient::AppendCommandFile(FString& InOutCommand, const FString& InFilename)
486 | {
487 | InOutCommand += TEXT(" ");
488 | InOutCommand += QuoteFilename(InFilename);
489 | }
490 |
491 | void FClient::AppendCommandFiles(FString& InOutCommand, const TArray& InFiles)
492 | {
493 | for (int32 i = 0; i < InFiles.Num(); ++i)
494 | {
495 | InOutCommand += TEXT(" ");
496 | InOutCommand += QuoteFilename(InFiles[i]);
497 | }
498 | }
499 |
500 | int32 FClient::GetFullCommandLength(const FString& InCommand, const TArray& InFiles)
501 | {
502 | int32 Length = InCommand.Len();
503 | for (const auto& Filename : InFiles)
504 | {
505 | Length += Filename.Len();
506 | Length += 3; // 1 space + 2 double-quotes
507 | }
508 | return Length;
509 | }
510 |
511 | bool FClient::RunCommand(
512 | const FString& InCommand, FString& OutResults, TArray& OutErrorMessages
513 | ) const
514 | {
515 | UE_LOG(LogSourceControl, Log, TEXT("Executing hg %s"), *InCommand);
516 |
517 | int32 ReturnCode = 0;
518 | FString StdError;
519 |
520 | FPlatformProcess::ExecProcess(
521 | *MercurialExecutablePath, *InCommand, &ReturnCode, &OutResults, &StdError
522 | );
523 |
524 | TArray ErrorMessages;
525 | if (StdError.ParseIntoArray(ErrorMessages, TEXT("\n"), true) > 0)
526 | {
527 | OutErrorMessages.Append(ErrorMessages);
528 | }
529 |
530 | return ReturnCode == 0;
531 | }
532 |
533 | bool FClient::RunCommand(
534 | const FString& InCommand, const TArray& InOptions,
535 | const FString& InWorkingDirectory, const TArray& InFiles, bool bForceFileList,
536 | FString& OutResults, TArray& OutErrorMessages
537 | ) const
538 | {
539 | FString Command(InCommand);
540 | AppendCommandOptions(Command, InOptions, InWorkingDirectory);
541 |
542 | // on Windows 7+ this number is actually around 32,000, but we'll pick something lower in case
543 | // other platforms are less generous
544 | const int32 MaxCommandLineLength = 16000;
545 |
546 | if (bForceFileList
547 | || ((InFiles.Num() > 0) && (GetFullCommandLength(Command, InFiles) > MaxCommandLineLength)))
548 | {
549 | // Write all the filenames to be committed to a temp file that will be passed in to hg,
550 | // this gets around command-line argument length limitations.
551 | FString FileList;
552 | for (const auto& RelativeFilename : InFiles)
553 | {
554 | FileList += TEXT("path:");
555 | FileList += RelativeFilename + TEXT("\n");
556 | }
557 |
558 | // The file list must be saved using the system's default encoding, because that's the
559 | // encoding hg will always use when reading in the file list. For future reference:
560 | // http://mercurial.selenic.com/wiki/EncodingStrategy
561 | // http://en.it-usenet.org/thread/16853/40385/
562 | FScopedTempFile ListFile(TEXT(".lst"));
563 | bool bResult = FFileHelper::SaveStringToFile(
564 | FileList, *ListFile.GetFilename(), FFileHelper::EEncodingOptions::ForceAnsi
565 | );
566 |
567 | if (!bResult)
568 | {
569 | OutErrorMessages.Add(
570 | FString::Printf(TEXT("Failed to write to temp file: '%s'"), *ListFile.GetFilename())
571 | );
572 | return false;
573 | }
574 |
575 | AppendCommandFile(Command, FString::Printf(TEXT("listfile:%s"), *ListFile.GetFilename()));
576 | // ListFile must be in-scope when this call is made
577 | return RunCommand(Command, OutResults, OutErrorMessages);
578 | }
579 | else
580 | {
581 | AppendCommandFiles(Command, InFiles);
582 | return RunCommand(Command, OutResults, OutErrorMessages);
583 | }
584 | }
585 |
586 | bool FClient::RunCommand(
587 | const FString& InCommand, const TArray& InOptions,
588 | const FString& InWorkingDirectory, const FString& InFilename,
589 | FString& OutResults, TArray& OutErrorMessages
590 | ) const
591 | {
592 | FString Command(InCommand);
593 | AppendCommandOptions(Command, InOptions, InWorkingDirectory);
594 | AppendCommandFile(Command, InFilename);
595 | return RunCommand(Command, OutResults, OutErrorMessages);
596 | }
597 |
598 | FString FClient::QuoteFilename(const FString& InFilename)
599 | {
600 | const FString Quote(TEXT("\""));
601 | return Quote + InFilename + Quote;
602 | }
603 |
604 | EFileStatus FClient::StatusCodeToFileStatus(TCHAR StatusCode)
605 | {
606 | switch (StatusCode)
607 | {
608 | case TEXT('M'):
609 | return EFileStatus::Modified;
610 |
611 | case TEXT('A'):
612 | return EFileStatus::Added;
613 |
614 | case TEXT('R'):
615 | return EFileStatus::Removed;
616 |
617 | case TEXT('C'):
618 | return EFileStatus::Clean;
619 |
620 | case TEXT('!'):
621 | return EFileStatus::Missing;
622 |
623 | case TEXT('?'):
624 | return EFileStatus::NotTracked;
625 |
626 | case TEXT('I'):
627 | return EFileStatus::Ignored;
628 |
629 | default:
630 | return EFileStatus::Unknown;
631 | }
632 | }
633 |
634 | FString FClient::ActionCodeToString(TCHAR ActionCode)
635 | {
636 | switch (ActionCode)
637 | {
638 | case TEXT('M'):
639 | return TEXT("edit");
640 |
641 | case TEXT('A'):
642 | return TEXT("add");
643 |
644 | case TEXT('R'):
645 | return TEXT("remove");
646 |
647 | default:
648 | return TEXT("unknown");
649 | }
650 | }
651 |
652 | FDateTime FClient::Rfc3339DateToDateTime(const FString& InDateString)
653 | {
654 | // There are some slight variations but the variant Mercurial seems to use by default is:
655 | // YYYY-MM-DDTHH:MM:SS[+,-]HH:MM
656 | const TCHAR* Space = TEXT(" ");
657 | FString Buffer = InDateString.Replace(TEXT("T"), Space);
658 | Buffer.ReplaceInline(TEXT("Z"), Space);
659 | Buffer.ReplaceInline(TEXT("-"), Space);
660 | Buffer.ReplaceInline(TEXT(":"), Space);
661 |
662 | TArray Segments;
663 | Buffer.ParseIntoArray(Segments, Space, true);
664 |
665 | int32 Year = FMath::Clamp((Segments.Num() > 0) ? FCString::Atoi(*Segments[0]) : 0, 0, 9999);
666 | int32 Month = FMath::Clamp((Segments.Num() > 1) ? FCString::Atoi(*Segments[1]) : 0, 1, 12);
667 | int32 Day = FMath::Clamp(
668 | (Segments.Num() > 2) ? FCString::Atoi(*Segments[2]) : 0,
669 | 1, FDateTime::DaysInMonth(Year, Month)
670 | );
671 | int32 Hour = FMath::Clamp((Segments.Num() > 3) ? FCString::Atoi(*Segments[3]) : 0, 0, 23);
672 | int32 Minute = FMath::Clamp((Segments.Num() > 4) ? FCString::Atoi(*Segments[4]) : 0, 0, 59);
673 | int32 Second = FMath::Clamp((Segments.Num() > 5) ? FCString::Atoi(*Segments[5]) : 0, 0, 59);
674 |
675 | return FDateTime(Year, Month, Day, Hour, Minute, Second);
676 | }
677 |
678 | void FClient::GetFileRevisionsFromXml(
679 | const FString& InFilename, const FXmlFile& InXmlFile, TArray& OutFileRevisions
680 | )
681 | {
682 | static const FString LogTag(TEXT("log"));
683 | static const FString LogEntryTag(TEXT("logentry"));
684 | static const FString RevisionTag(TEXT("revision"));
685 | static const FString CommitIdTag(TEXT("node"));
686 | static const FString AuthorTag(TEXT("author"));
687 | static const FString DateTag(TEXT("date"));
688 | static const FString MsgTag(TEXT("msg"));
689 | static const FString PathsTag(TEXT("paths"));
690 | static const FString PathTag(TEXT("path"));
691 | static const FString ActionTag(TEXT("action"));
692 |
693 | const FXmlNode* LogNode = InXmlFile.GetRootNode();
694 | check(LogNode && (LogNode->GetTag() == LogTag));
695 | if (!LogNode || (LogNode->GetTag() != LogTag))
696 | return;
697 |
698 | const TArray LogEntries = LogNode->GetChildrenNodes();
699 | for (auto LogEntryIt(LogEntries.CreateConstIterator()); LogEntryIt; LogEntryIt++)
700 | {
701 | const FXmlNode* LogEntryNode = *LogEntryIt;
702 | check(LogEntryNode && (LogEntryNode->GetTag() == LogEntryTag));
703 | if (!LogEntryNode || (LogEntryNode->GetTag() != LogEntryTag))
704 | {
705 | continue;
706 | }
707 |
708 | // note: we don't set the filename for the created revision, this is because the filename
709 | // must be absolute and we only have the relative filename at this point
710 | FFileRevisionRef FileRevision = MakeShareable(new FFileRevision());
711 | FileRevision->SetRevisionNumber(FCString::Atoi(*LogEntryNode->GetAttribute(RevisionTag)));
712 | FileRevision->SetCommitId(*LogEntryNode->GetAttribute(CommitIdTag));
713 |
714 | const FXmlNode* AuthorNode = LogEntryNode->FindChildNode(AuthorTag);
715 | if (AuthorNode)
716 | {
717 | FileRevision->SetUserName(AuthorNode->GetContent());
718 | }
719 |
720 | const FXmlNode* DateNode = LogEntryNode->FindChildNode(DateTag);
721 | if (DateNode)
722 | {
723 | FileRevision->SetDate(Rfc3339DateToDateTime(DateNode->GetContent()));
724 | }
725 |
726 | const FXmlNode* MsgNode = LogEntryNode->FindChildNode(MsgTag);
727 | if (MsgNode)
728 | {
729 | FileRevision->SetDescription(UnescapeXMLEntities(MsgNode->GetContent()));
730 | }
731 |
732 | // the paths node contains path nodes indicating the operations that were performed,
733 | // e.g.
734 | //
735 | // foo/bar/Test.txt
736 | // foo/Test.txt
737 | //
738 | // In the example above Test.txt was moved from directory foo to foo/bar.
739 | const FXmlNode* PathsNode = LogEntryNode->FindChildNode(PathsTag);
740 | if (PathsNode)
741 | {
742 | const TArray Paths = PathsNode->GetChildrenNodes();
743 | for (auto PathIt(Paths.CreateConstIterator()); PathIt; PathIt++)
744 | {
745 | const FXmlNode* PathNode = *PathIt;
746 | if (PathNode && (PathNode->GetTag() == PathTag))
747 | {
748 | if (PathNode->GetContent() == InFilename)
749 | {
750 | FString ActionCode = PathNode->GetAttribute(ActionTag);
751 | if (ActionCode.Len() > 0)
752 | {
753 | FileRevision->SetAction(ActionCodeToString(ActionCode[0]));
754 | }
755 | else
756 | {
757 | FileRevision->SetAction(TEXT("unknown"));
758 | }
759 | }
760 | }
761 | }
762 | }
763 |
764 | OutFileRevisions.Add(FileRevision);
765 | }
766 | }
767 |
768 | FString FClient::UnescapeXMLEntities(const FString& InEscapedText)
769 | {
770 | FString Text(InEscapedText);
771 |
772 | Text.ReplaceInline(TEXT("<"), TEXT("<"));
773 | Text.ReplaceInline(TEXT("<"), TEXT("<"));
774 |
775 | Text.ReplaceInline(TEXT(">"), TEXT(">"));
776 | Text.ReplaceInline(TEXT(">"), TEXT(">"));
777 |
778 | Text.ReplaceInline(TEXT("""), TEXT("\""));
779 | Text.ReplaceInline(TEXT("""), TEXT("\""));
780 |
781 | Text.ReplaceInline(TEXT("'"), TEXT("'"));
782 | Text.ReplaceInline(TEXT("'"), TEXT("'"));
783 |
784 | Text.ReplaceInline(TEXT("&"), TEXT("&"));
785 | Text.ReplaceInline(TEXT("&"), TEXT("&"));
786 |
787 | return Text;
788 | }
789 |
790 | bool FClient::ConvertFilesToRelative(
791 | const FString& InRelativeTo, const TArray& InFiles, TArray& OutFiles
792 | )
793 | {
794 | for (const auto& AbsoluteFilename : InFiles)
795 | {
796 | FString Filename = AbsoluteFilename;
797 | if (FPaths::MakePathRelativeTo(Filename, *InRelativeTo))
798 | {
799 | OutFiles.Add(Filename);
800 | }
801 | else
802 | {
803 | return false;
804 | }
805 | }
806 | return true;
807 | }
808 |
809 | #undef LOCTEXT_NAMESPACE
810 |
811 | } // namespace MercurialSourceControl
812 |
--------------------------------------------------------------------------------
/Source/MercurialSourceControl/Private/MercurialSourceControlClient.h:
--------------------------------------------------------------------------------
1 | //-------------------------------------------------------------------------------
2 | // The MIT License (MIT)
3 | //
4 | // Copyright (c) 2014 Vadim Macagon
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //-------------------------------------------------------------------------------
24 | #pragma once
25 |
26 | #include "MercurialSourceControlFileState.h"
27 | #include "MercurialSourceControlFileRevision.h"
28 |
29 | class FXmlFile;
30 |
31 | namespace MercurialSourceControl {
32 |
33 | typedef TSharedPtr FClientSharedPtr;
34 |
35 | class FFileState;
36 |
37 | /** Executes source control commands in a Mercurial repository by invoking hg.exe. */
38 | class FClient : public TSharedFromThis
39 | {
40 | public:
41 | /**
42 | * Check if the given filename corresponds to a valid Mercurial executable file.
43 | * @note It's safe to call this method at any time, even before Initialize().
44 | */
45 | static bool IsValidExecutable(const FString& InFilename);
46 |
47 | static bool FindExecutable(FString& OutFilename);
48 |
49 | /**
50 | * Create and initialize the FClient singleton instance.
51 | * @param InMercurialPath Absolute path to the Mercurial executable that should be invoked to
52 | * manipulate a Mercurial repository.
53 | * @param OutError Will contain an error message if this method returns false.
54 | * @return true if the singleton instance was created and initialized successfully,
55 | * false otherwise.
56 | */
57 | static bool Create(const FString& InMercurialPath, FText& OutError);
58 | static const FClientSharedPtr& Get();
59 | static void Destroy();
60 |
61 | public:
62 | /** Get the root directory of the repository in which the given working directory resides. */
63 | bool GetRepositoryRoot(const FString& InWorkingDirectory, FString& OutRepositoryRoot) const;
64 |
65 | bool GetFileStates(
66 | const FString& InWorkingDirectory, const TArray& InAbsoluteFiles,
67 | TArray& OutFileStates, TArray& OutErrors
68 | ) const;
69 |
70 | bool GetFileHistory(
71 | const FString& InWorkingDirectory, const TArray& InAbsoluteFiles,
72 | TMap >& OutFileRevisionsMap, TArray& OutErrors
73 | ) const;
74 |
75 | /**
76 | * Recreate a file as it was at the given revision.
77 | * @param InWorkingDirectory The working directory to set for hg.exe.
78 | * @param RevisionNumber The local revision to recreate the file from.
79 | * @param InFileToExtract The original absolute filename of the file to be recreated.
80 | * @param InDestinationFile The absolute path at which the file should be recreated.
81 | * @param OutErrors Output from stderr of hg.exe.
82 | * @return true if the operation was successful, false otherwise.
83 | */
84 | bool ExtractFileFromRevision(
85 | const FString& InWorkingDirectory, int32 RevisionNumber, const FString& InFileToExtract,
86 | const FString& InDestinationFile, TArray& OutErrors
87 | ) const;
88 |
89 | /**
90 | * Add files to the repository.
91 | * @param InWorkingDirectory The working directory to set for hg.exe.
92 | * @param InAbsoluteFiles Full filenames of files to add to the repository.
93 | * @param bInAddAsLarge If true the files will be flagged as large when they're added to the
94 | * repository, otherwise they'll be added with no special flags.
95 | * @param OutErrors Output from stderr of hg.exe.
96 | * @return true if the operation was successful, false otherwise.
97 | */
98 | bool AddFiles(
99 | const FString& InWorkingDirectory, const TArray& InAbsoluteFiles,
100 | bool bInAddAsLarge, TArray& OutErrors
101 | ) const;
102 |
103 | /**
104 | * Revert the given files to the contents they had in the parent of the working directory.
105 | * The files will be restored to an unmodified state and any pending adds, removes, copies,
106 | * and renames will be undone.
107 | * @param InWorkingDirectory The working directory to set for hg.exe.
108 | * @param InAbsoluteFiles The absolute filenames of the files to revert.
109 | * @param OutErrors Output from stderr of hg.exe.
110 | * @return true if the operation was successful, false otherwise.
111 | */
112 | bool RevertFiles(
113 | const FString& InWorkingDirectory, const TArray& InAbsoluteFiles,
114 | TArray& OutErrors
115 | ) const;
116 |
117 | /** Remove clean and missing files from the repository. */
118 | bool RemoveFiles(
119 | const FString& InWorkingDirectory, const TArray& InAbsoluteFiles,
120 | TArray& OutErrors
121 | ) const;
122 |
123 | /** Remove added, clean, and missing files from the repository. */
124 | bool RemoveAllFiles(
125 | const FString& InWorkingDirectory, const TArray& InAbsoluteFiles,
126 | TArray& OutErrors
127 | ) const;
128 |
129 | bool CommitFiles(
130 | const FString& InWorkingDirectory, const TArray& InAbsoluteFiles,
131 | const FString& InCommitMessage, TArray& OutErrors
132 | ) const;
133 |
134 | /** Get the local ID of the working directory's parent revision. */
135 | bool GetWorkingDirectoryParentRevisionID(
136 | const FString& InWorkingDirectory, FString& OutRevisionID, TArray& OutErrors
137 | ) const;
138 |
139 | private:
140 | static void AppendCommandOptions(
141 | FString& InOutCommand, const TArray& InOptions,
142 | const FString& InWorkingDirectory
143 | );
144 | static void AppendCommandFile(FString& InOutCommand, const FString& InFilename);
145 | static void AppendCommandFiles(FString& InOutCommand, const TArray& InFiles);
146 | static int32 GetFullCommandLength(const FString& InCommand, const TArray& InFiles);
147 |
148 | /** Enclose the given filename in double-quotes. */
149 | static FString QuoteFilename(const FString& InFilename);
150 |
151 | /** Convert a standard Mercurial status code character to the corresponding EFileStatus. */
152 | static EFileStatus StatusCodeToFileStatus(TCHAR StatusCode);
153 |
154 | static FString ActionCodeToString(TCHAR ActionCode);
155 | static FDateTime Rfc3339DateToDateTime(const FString& InDateString);
156 |
157 | /**
158 | * Extract file revisions from an XML log.
159 | * @param InFilename The filename for which revisions should be extracted.
160 | * @note The extracted revisions don't have a filename set!
161 | */
162 | static void GetFileRevisionsFromXml(
163 | const FString& InFilename, const FXmlFile& InXmlFile,
164 | TArray& OutFileRevisions
165 | );
166 |
167 | static FString UnescapeXMLEntities(const FString& InEscapedText);
168 |
169 | /** Convert all the given filenames to be relative to the specified path. */
170 | static bool ConvertFilesToRelative(
171 | const FString& InRelativeTo, const TArray& InFiles, TArray& OutFiles
172 | );
173 |
174 | private:
175 | /**
176 | * Constructor.
177 | * @param InMercurialPath Absolute valid path to hg.exe.
178 | */
179 | FClient(const FString& InMercurialPath) : MercurialExecutablePath(InMercurialPath) {}
180 |
181 | /**
182 | * Invoke hg.exe with the given command and return the output.
183 | * @param InCommand A fully formed hg command, e.g. status --verbose Content/SomeFile.txt
184 | * @param OutResults Output from stdout of hg.exe.
185 | * @param OutErrorMessages Output from stderr of hg.exe.
186 | * @return true if hg indicated the command was successful, false otherwise.
187 | */
188 | bool RunCommand(
189 | const FString& InCommand, FString& OutResults, TArray& OutErrorMessages
190 | ) const;
191 |
192 | /**
193 | * Invoke hg.exe with the given arguments and return the output.
194 | * @param InCommand An hg command, e.g. add
195 | * @param InOptions Zero or more options for the hg command.
196 | * @param InWorkingDirectory The working directory to set for hg.exe.
197 | * @param InFiles Zero or more filenames the hg command should operate on, all filenames should
198 | * be relative to InWorkingDirectory.
199 | * @param bForceFileList If true force all filenames in InFiles to be written to a temporary
200 | * file which is then passed in as a command argument instead of the
201 | * individual filenames in InFiles.
202 | * If false a temporary file will only be used when command line length
203 | * limits are exceeded.
204 | * @param OutResults Output from stdout of hg.exe.
205 | * @param OutErrorMessages Output from stderr of hg.exe.
206 | * @return true if hg indicated the command was successful, false otherwise.
207 | */
208 | bool RunCommand(
209 | const FString& InCommand, const TArray& InOptions,
210 | const FString& InWorkingDirectory, const TArray& InFiles, bool bForceFileList,
211 | FString& OutResults, TArray& OutErrorMessages
212 | ) const;
213 |
214 | /**
215 | * Invoke hg.exe with the given arguments and return the output.
216 | * @param InCommand An hg command, e.g. add
217 | * @param InOptions Zero or more options for the hg command.
218 | * @param InWorkingDirectory The working directory to set for hg.exe.
219 | * @param InFilename The filename the hg command should operate on, the filename should
220 | * be relative to InWorkingDirectory.
221 | * @param OutResults Output from stdout of hg.exe.
222 | * @param OutErrorMessages Output from stderr of hg.exe.
223 | * @return true if hg indicated the command was successful, false otherwise.
224 | */
225 | bool RunCommand(
226 | const FString& InCommand, const TArray& InOptions,
227 | const FString& InWorkingDirectory, const FString& InFilename,
228 | FString& OutResults, TArray& OutErrorMessages
229 | ) const;
230 |
231 | private:
232 | FString MercurialExecutablePath;
233 |
234 | private:
235 | static FClientSharedPtr Singleton;
236 | };
237 |
238 | } // namespace MercurialSourceControl
239 |
--------------------------------------------------------------------------------
/Source/MercurialSourceControl/Private/MercurialSourceControlCommand.cpp:
--------------------------------------------------------------------------------
1 | //-------------------------------------------------------------------------------
2 | // The MIT License (MIT)
3 | //
4 | // Copyright (c) 2014 Vadim Macagon
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //-------------------------------------------------------------------------------
24 |
25 | #include "MercurialSourceControlPrivatePCH.h"
26 | #include "MercurialSourceControlCommand.h"
27 |
28 | namespace MercurialSourceControl {
29 |
30 | FCommand::FCommand(
31 | const FString& InWorkingDirectory,
32 | const FString& InContentDirectory,
33 | const FSourceControlOperationRef& InOperation,
34 | const FWorkerRef& InWorker,
35 | const FSourceControlOperationComplete& InCompleteDelegate
36 | ) : Operation(InOperation)
37 | , Worker(InWorker)
38 | , WorkingDirectory(InWorkingDirectory)
39 | , ContentDirectory(InContentDirectory)
40 | , OperationCompleteDelegate(InCompleteDelegate)
41 | , bExecuteProcessed(0)
42 | , bCommandSuccessful(false)
43 | , Concurrency(EConcurrency::Synchronous)
44 | {
45 | check(IsInGameThread());
46 | }
47 |
48 | bool FCommand::DoWork()
49 | {
50 | bCommandSuccessful = Worker->Execute(*this);
51 | FPlatformAtomics::InterlockedExchange(&bExecuteProcessed, 1);
52 | return bCommandSuccessful;
53 | }
54 |
55 | void FCommand::DoThreadedWork()
56 | {
57 | Concurrency = EConcurrency::Asynchronous;
58 | DoWork();
59 | }
60 |
61 | void FCommand::Abandon()
62 | {
63 | FPlatformAtomics::InterlockedExchange(&bExecuteProcessed, 1);
64 | }
65 |
66 | } // namespace MercurialSourceControl
67 |
--------------------------------------------------------------------------------
/Source/MercurialSourceControl/Private/MercurialSourceControlCommand.h:
--------------------------------------------------------------------------------
1 | //-------------------------------------------------------------------------------
2 | // The MIT License (MIT)
3 | //
4 | // Copyright (c) 2014 Vadim Macagon
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //-------------------------------------------------------------------------------
24 | #pragma once
25 |
26 | #include "IMercurialSourceControlWorker.h"
27 | #include "ISourceControlProvider.h"
28 |
29 | namespace MercurialSourceControl {
30 |
31 | typedef TSharedRef FSourceControlOperationRef;
32 |
33 | /**
34 | * Executes a Mercurial command, the execution may be done on a worker thread.
35 | * The hard work is delegated to an IMercurialSourceControlWorker object.
36 | */
37 | class FCommand : public IQueuedWork
38 | {
39 | public:
40 | FCommand(
41 | const FString& InWorkingDirectory,
42 | const FString& InContentDirectory,
43 | const FSourceControlOperationRef& InOperation,
44 | const FWorkerRef& InWorker,
45 | const FSourceControlOperationComplete& InCompleteDelegate = FSourceControlOperationComplete()
46 | );
47 |
48 | /** Execute the command. */
49 | bool DoWork();
50 |
51 | /** Return true iff the command has finished executing. */
52 | bool HasExecuted() const
53 | {
54 | return bExecuteProcessed != 0;
55 | }
56 |
57 | /** Update the state of any affected items after the command has executed. */
58 | bool UpdateStates()
59 | {
60 | check(bExecuteProcessed);
61 |
62 | return Worker->UpdateStates();
63 | }
64 |
65 | /** Get the result (succeeded/failed) of the command execution. */
66 | ECommandResult::Type GetResult() const
67 | {
68 | check(bExecuteProcessed);
69 |
70 | return bCommandSuccessful ? ECommandResult::Succeeded : ECommandResult::Failed;
71 | }
72 |
73 | /** Notify that the command has finished executing. */
74 | void NotifyOperationComplete()
75 | {
76 | OperationCompleteDelegate.ExecuteIfBound(Operation, GetResult());
77 | }
78 |
79 | /** Get the absolute path to the working directory of the command. */
80 | const FString& GetWorkingDirectory() const
81 | {
82 | return WorkingDirectory;
83 | }
84 |
85 | /** Get the absolute path to the current content directory. */
86 | const FString& GetContentDirectory() const
87 | {
88 | return ContentDirectory;
89 | }
90 |
91 | FSourceControlOperationRef GetOperation() const
92 | {
93 | return Operation;
94 | }
95 |
96 | void SetAbsoluteFiles(const TArray& InAbsoluteFiles)
97 | {
98 | Files = InAbsoluteFiles;
99 | }
100 |
101 | /** Get the absolute paths to the files the source control operation should be performed on. */
102 | const TArray& GetAbsoluteFiles() const
103 | {
104 | return Files;
105 | }
106 |
107 | void SetAbsoluteLargeFiles(const TArray& InAbsoluteLargeFiles)
108 | {
109 | LargeFiles = InAbsoluteLargeFiles;
110 | }
111 |
112 | const TArray& GetAbsoluteLargeFiles() const
113 | {
114 | return LargeFiles;
115 | }
116 |
117 | public:
118 | // FQueuedWork methods
119 | virtual void DoThreadedWork() override;
120 | virtual void Abandon() override;
121 |
122 | public:
123 | /** Descriptions of errors (if any) encountered while executing the command. */
124 | TArray ErrorMessages;
125 |
126 | private:
127 | /** The source control operation to perform when the command is executed. */
128 | FSourceControlOperationRef Operation;
129 |
130 | /** The absolute paths to the files (if any) to perform the operation on. */
131 | TArray Files;
132 |
133 | /** The absolute paths to the large files (if any) to perform an 'add' operation on. */
134 | TArray LargeFiles;
135 |
136 | /** The worker that will actually perform the operation. */
137 | FWorkerRef Worker;
138 |
139 | /** Absolute path to the working directory for the command. */
140 | FString WorkingDirectory;
141 |
142 | /** Absolute path to the current content directory. */
143 | FString ContentDirectory;
144 |
145 | /** Will be set to true if the operation is performed successfully. */
146 | bool bCommandSuccessful;
147 |
148 | /** Executed after the operation completes. */
149 | FSourceControlOperationComplete OperationCompleteDelegate;
150 |
151 | /** Has the operation been completed? */
152 | volatile int32 bExecuteProcessed;
153 |
154 | /** Is this operation being performed synchronously or asynchronously? */
155 | EConcurrency::Type Concurrency;
156 | };
157 |
158 | } // namespace MercurialSourceControl
159 |
--------------------------------------------------------------------------------
/Source/MercurialSourceControl/Private/MercurialSourceControlFileRevision.cpp:
--------------------------------------------------------------------------------
1 | //-------------------------------------------------------------------------------
2 | // The MIT License (MIT)
3 | //
4 | // Copyright (c) 2014 Vadim Macagon
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //-------------------------------------------------------------------------------
24 |
25 | #include "MercurialSourceControlPrivatePCH.h"
26 | #include "MercurialSourceControlModule.h"
27 | #include "MercurialSourceControlFileRevision.h"
28 | #include "MercurialSourceControlProvider.h"
29 | #include "MercurialSourceControlClient.h"
30 |
31 | namespace MercurialSourceControl {
32 |
33 | bool FFileRevision::Get(FString& InOutFilename) const
34 | {
35 | const FClientSharedPtr Client = FClient::Get();
36 | if (!Client.IsValid())
37 | {
38 | return false;
39 | }
40 |
41 | // if a filename for the temp file wasn't supplied generate a unique-ish one
42 | if (InOutFilename.Len() == 0)
43 | {
44 | InOutFilename = FString::Printf(
45 | TEXT("Temp-Rev-%d-%d-%s"),
46 | RevisionNumber,
47 | FDateTime::UtcNow().ToUnixTimestamp(),
48 | *FPaths::GetCleanFilename(AbsoluteFilename)
49 | );
50 | // the extracted file should go into the designated diffing directory
51 | IFileManager::Get().MakeDirectory(*FPaths::DiffDir(), true);
52 | InOutFilename = FPaths::ConvertRelativePathToFull(FPaths::DiffDir() / InOutFilename);
53 | }
54 |
55 | FProvider& Provider = FModule::GetProvider();
56 | TArray Errors;
57 | bool bSucceeded = Client->ExtractFileFromRevision(
58 | Provider.GetWorkingDirectory(), RevisionNumber, AbsoluteFilename, InOutFilename, Errors
59 | );
60 | Provider.LogErrors(Errors);
61 | return bSucceeded;
62 | }
63 |
64 | bool FFileRevision::GetAnnotated(TArray& OutLines) const
65 | {
66 | // TODO
67 | return false;
68 | }
69 |
70 | bool FFileRevision::GetAnnotated(FString& InOutFilename) const
71 | {
72 | // TODO
73 | return false;
74 | }
75 |
76 | const FString& FFileRevision::GetFilename() const
77 | {
78 | return AbsoluteFilename;
79 | }
80 |
81 | int32 FFileRevision::GetRevisionNumber() const
82 | {
83 | return RevisionNumber;
84 | }
85 |
86 | const FString& FFileRevision::GetRevision() const
87 | {
88 | return CommitId;
89 | }
90 |
91 | const FString& FFileRevision::GetDescription() const
92 | {
93 | return Description;
94 | }
95 |
96 | const FString& FFileRevision::GetUserName() const
97 | {
98 | return UserName;
99 | }
100 |
101 | const FString& FFileRevision::GetClientSpec() const
102 | {
103 | static const FString EmptyString(TEXT(""));
104 | return EmptyString;
105 | }
106 |
107 | const FString& FFileRevision::GetAction() const
108 | {
109 | return Action;
110 | }
111 |
112 | FSourceControlRevisionPtr FFileRevision::GetBranchSource() const
113 | {
114 | // TODO: if this revision was copied from some other revision, then that source revision should
115 | // be returned here (this should be determined when history is being fetched)
116 | return nullptr;
117 | }
118 |
119 | const FDateTime& FFileRevision::GetDate() const
120 | {
121 | return Date;
122 | }
123 |
124 | int32 FFileRevision::GetCheckInIdentifier() const
125 | {
126 | return RevisionNumber;
127 | }
128 |
129 | int32 FFileRevision::GetFileSize() const
130 | {
131 | // Mercurial doesn't appear to provide easy access to file sizes.
132 | return 0;
133 | }
134 |
135 | } // namespace MercurialSourceControl
136 |
--------------------------------------------------------------------------------
/Source/MercurialSourceControl/Private/MercurialSourceControlFileRevision.h:
--------------------------------------------------------------------------------
1 | //-------------------------------------------------------------------------------
2 | // The MIT License (MIT)
3 | //
4 | // Copyright (c) 2014 Vadim Macagon
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //-------------------------------------------------------------------------------
24 | #pragma once
25 |
26 | #include "ISourceControlRevision.h"
27 |
28 | namespace MercurialSourceControl {
29 |
30 | typedef TSharedPtr FSourceControlRevisionPtr;
31 |
32 | /**
33 | * Provides information relating to a revision of a file in a Mercurial repository.
34 | */
35 | class FFileRevision
36 | : public ISourceControlRevision
37 | , public TSharedFromThis
38 | {
39 | public:
40 | FFileRevision() : RevisionNumber(0) {}
41 |
42 | void SetFilename(const FString& InFilename)
43 | {
44 | AbsoluteFilename = InFilename;
45 | }
46 |
47 | void SetRevisionNumber(int32 InRevisionNumber)
48 | {
49 | RevisionNumber = InRevisionNumber;
50 | }
51 |
52 | void SetCommitId(const FString &commitId)
53 | {
54 | CommitId = commitId;
55 | }
56 |
57 | void SetUserName(const FString& InUserName)
58 | {
59 | UserName = InUserName;
60 | }
61 |
62 | void SetDate(const FDateTime& InDate)
63 | {
64 | Date = InDate;
65 | }
66 |
67 | void SetDescription(const FString& InDescription)
68 | {
69 | Description = InDescription;
70 | }
71 |
72 | void SetAction(const FString& InAction)
73 | {
74 | Action = InAction;
75 | }
76 |
77 | public:
78 | // ISourceControlRevision methods
79 |
80 | /**
81 | * Copy this file revision into a temporary file.
82 | * @param InOutFilename The filename that this revision should be written to. If this is empty
83 | * a temporary filename will be generated and returned in this string.
84 | * @return true on success, false otherwise.
85 | */
86 | virtual bool Get(FString& InOutFilename) const override;
87 | virtual bool GetAnnotated(TArray& OutLines) const override;
88 | virtual bool GetAnnotated(FString& InOutFilename) const override;
89 | virtual const FString& GetFilename() const override;
90 | virtual int32 GetRevisionNumber() const override;
91 | virtual const FString& GetRevision() const override;
92 | virtual const FString& GetDescription() const override;
93 | virtual const FString& GetUserName() const override;
94 | virtual const FString& GetClientSpec() const override;
95 | virtual const FString& GetAction() const override;
96 | virtual FSourceControlRevisionPtr GetBranchSource() const override;
97 | virtual const FDateTime& GetDate() const override;
98 | virtual int32 GetCheckInIdentifier() const override;
99 | virtual int32 GetFileSize() const override;
100 |
101 | private:
102 | FString AbsoluteFilename;
103 | int32 RevisionNumber;
104 | FString CommitId;
105 | FString Description;
106 | FString UserName;
107 | FString Action;
108 | FDateTime Date;
109 | };
110 |
111 | typedef TSharedRef FFileRevisionRef;
112 |
113 | } // namespace MercurialSourceControl
114 |
--------------------------------------------------------------------------------
/Source/MercurialSourceControl/Private/MercurialSourceControlFileState.cpp:
--------------------------------------------------------------------------------
1 | //-------------------------------------------------------------------------------
2 | // The MIT License (MIT)
3 | //
4 | // Copyright (c) 2014 Vadim Macagon
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //-------------------------------------------------------------------------------
24 |
25 | #include "MercurialSourceControlPrivatePCH.h"
26 | #include "MercurialSourceControlFileState.h"
27 | #include "MercurialSourceControlStyle.h"
28 |
29 | namespace MercurialSourceControl {
30 |
31 | #define LOCTEXT_NAMESPACE "MercurialSourceControl.State"
32 |
33 | int32 FFileState::GetHistorySize() const
34 | {
35 | return History.Num();
36 | }
37 |
38 | FSourceControlRevisionPtr FFileState::GetHistoryItem(int32 HistoryIndex) const
39 | {
40 | check(History.IsValidIndex(HistoryIndex));
41 | return History[HistoryIndex];
42 | }
43 |
44 | FSourceControlRevisionPtr FFileState::FindHistoryRevision(int32 RevisionNumber) const
45 | {
46 | for (int32 i = 0; i < History.Num(); ++i)
47 | {
48 | if (History[i]->GetRevisionNumber() == RevisionNumber)
49 | {
50 | return History[i];
51 | }
52 | }
53 | return NULL;
54 | }
55 |
56 | FSourceControlRevisionPtr FFileState::FindHistoryRevision(const FString& InRevision) const
57 | {
58 | for (int32 i = 0; i < History.Num(); ++i)
59 | {
60 | if (History[i]->GetRevision() == InRevision)
61 | {
62 | return History[i];
63 | }
64 | }
65 | return NULL;
66 | }
67 |
68 | FSourceControlRevisionPtr FFileState::GetBaseRevForMerge() const
69 | {
70 | // TODO: return the revision of the common ancestor when there is a conflict
71 | return nullptr;
72 | }
73 |
74 | FName FFileState::GetIconName() const
75 | {
76 | if (!IsCurrent())
77 | {
78 | return FName("Subversion.NotAtHeadRevision");
79 | }
80 |
81 | // TODO: Moar icons?!
82 | switch (FileStatus)
83 | {
84 | case EFileStatus::Clean:
85 | return FMercurialStyle::CleanStatusIcon32;
86 | case EFileStatus::Added:
87 | return FMercurialStyle::AddedStatusIcon32;
88 | case EFileStatus::NotTracked:
89 | return FMercurialStyle::NotTrackedStatusIcon32;
90 | case EFileStatus::Modified:
91 | return FMercurialStyle::ModifiedStatusIcon32;
92 | case EFileStatus::Removed:
93 | return FMercurialStyle::RemovedStatusIcon32;
94 | case EFileStatus::Missing:
95 | return FMercurialStyle::MissingStatusIcon32;
96 | default:
97 | return NAME_None;
98 | }
99 | }
100 |
101 | FName FFileState::GetSmallIconName() const
102 | {
103 | if (!IsCurrent())
104 | {
105 | return FName("Subversion.NotAtHeadRevision_Small");
106 | }
107 |
108 | // TODO: Moar icons?!
109 | switch (FileStatus)
110 | {
111 | case EFileStatus::Clean:
112 | return FMercurialStyle::CleanStatusIcon16;
113 | case EFileStatus::Added:
114 | return FMercurialStyle::AddedStatusIcon16;
115 | case EFileStatus::NotTracked:
116 | return FMercurialStyle::NotTrackedStatusIcon16;
117 | case EFileStatus::Modified:
118 | return FMercurialStyle::ModifiedStatusIcon16;
119 | case EFileStatus::Removed:
120 | return FMercurialStyle::RemovedStatusIcon16;
121 | case EFileStatus::Missing:
122 | return FMercurialStyle::MissingStatusIcon16;
123 | default:
124 | return NAME_None;
125 | }
126 | }
127 |
128 | FText FFileState::GetDisplayName() const
129 | {
130 | switch (FileStatus)
131 | {
132 | case EFileStatus::Unknown:
133 | return LOCTEXT("Unknown", "Uknown");
134 |
135 | case EFileStatus::Clean:
136 | return LOCTEXT("Clean", "Clean");
137 |
138 | case EFileStatus::Added:
139 | return LOCTEXT("Added", "Added");
140 |
141 | case EFileStatus::Removed:
142 | return LOCTEXT("Removed", "Removed");
143 |
144 | case EFileStatus::Modified:
145 | return LOCTEXT("Modified", "Modified");
146 |
147 | case EFileStatus::NotTracked:
148 | return LOCTEXT("NotTracked", "Not Tracked");
149 |
150 | case EFileStatus::Ignored:
151 | return LOCTEXT("Ignored", "Ignored");
152 |
153 | case EFileStatus::Missing:
154 | return LOCTEXT("Missing", "Missing");
155 | }
156 | return FText();
157 | }
158 |
159 | FText FFileState::GetDisplayTooltip() const
160 | {
161 | switch (FileStatus)
162 | {
163 | case EFileStatus::Unknown:
164 | return LOCTEXT("Unknown_Tooltip", "Item status is unknown, or maybe hell froze over.");
165 |
166 | case EFileStatus::Clean:
167 | return LOCTEXT("Clean_Tooltip", "Item hasn't been modified.");
168 |
169 | case EFileStatus::Added:
170 | return LOCTEXT("Added_Tooltip", "Item has been added.");
171 |
172 | case EFileStatus::Removed:
173 | return LOCTEXT("Removed_Tooltip", "Item has been removed.");
174 |
175 | case EFileStatus::Modified:
176 | return LOCTEXT("Modified_Tooltip", "Item has been modified.");
177 |
178 | case EFileStatus::NotTracked:
179 | return LOCTEXT("NotTracked_Tooltip", "Item is not under source control.");
180 |
181 | case EFileStatus::Ignored:
182 | return LOCTEXT("Ignored_Tooltip", "Item is being ignored.");
183 |
184 | case EFileStatus::Missing:
185 | return LOCTEXT(
186 | "Missing_Tooltip",
187 | "Mercurial is unable to locate the item on disk, this may occur when an item is deleted or moved by a non-Mercurial command."
188 | );
189 | }
190 | return FText();
191 | }
192 |
193 | const FString& FFileState::GetFilename() const
194 | {
195 | return AbsoluteFilename;
196 | }
197 |
198 | const FDateTime& FFileState::GetTimeStamp() const
199 | {
200 | return TimeStamp;
201 | }
202 |
203 | bool FFileState::CanCheckIn() const
204 | {
205 | return !IsConflicted()
206 | && ((FileStatus == EFileStatus::Added)
207 | || (FileStatus == EFileStatus::Modified)
208 | || (FileStatus == EFileStatus::Removed));
209 | }
210 |
211 | bool FFileState::CanCheckout() const
212 | {
213 | // the check-out operation is not supported by the Mercurial provider
214 | return false;
215 | }
216 |
217 | bool FFileState::IsCheckedOut() const
218 | {
219 | // since Mercurial has no concept of exclusive checkouts (unlike Perforce & SVN)
220 | // any file being tracked by Mercurial is always considered checked out so that the end user
221 | // doesn't have to perform a pointless check-out operation before they can edit a file
222 | return IsSourceControlled();
223 | }
224 |
225 | bool FFileState::IsCheckedOutOther(FString* Who) const
226 | {
227 | // Mercurial doesn't keep track of who checked what out
228 | return false;
229 | }
230 |
231 | bool FFileState::IsCurrent() const
232 | {
233 | // TODO
234 | return true;
235 | }
236 |
237 | bool FFileState::IsSourceControlled() const
238 | {
239 | return (FileStatus != EFileStatus::NotTracked) && (FileStatus != EFileStatus::Unknown);
240 | }
241 |
242 | bool FFileState::IsAdded() const
243 | {
244 | return FileStatus == EFileStatus::Added;
245 | }
246 |
247 | bool FFileState::IsDeleted() const
248 | {
249 | return FileStatus == EFileStatus::Removed;
250 | }
251 |
252 | bool FFileState::IsIgnored() const
253 | {
254 | return FileStatus == EFileStatus::Ignored;
255 | }
256 |
257 | bool FFileState::CanEdit() const
258 | {
259 | return true;
260 | }
261 |
262 | bool FFileState::IsUnknown() const
263 | {
264 | return FileStatus == EFileStatus::Unknown;
265 | }
266 |
267 | bool FFileState::IsModified() const
268 | {
269 | // In case you're wondering why we check for EFileStatus::Added in here, it's because
270 | // UnrealEd makes certain assumptions about source control providers, and those assumptions are
271 | // based on Perfoce. In this particular case we're working around the assumption that it's
272 | // a good idea to revert unchanged files before a commit (see
273 | // FSourceControlWindows::PromptForCheckin() for details), with that in mind...
274 | //
275 | // What is an unchanged file? Anything that IsCheckedOut() && !IsModified(). In the Mercurial
276 | // provider all the files are checked out all the time, so it all comes down to !IsModified().
277 | // Here's what would happen if we didn't account for EFileStatus::Added in IsModified():
278 | // 1. User creates a new file and marks it for add, its status is now EFileStatus::Added.
279 | // 2. User tries to commit the added file (Check In in UnrealEd).
280 | // 3. UnrealEd reverts the file because IsModified() == false, so its status is now
281 | // EFileStatus::NotTracked.
282 | // 4. UnrealEd tries to commit the file it just reverted, but that fails since Mercurial is
283 | // no longer tracking it.
284 |
285 | return (FileStatus == EFileStatus::Modified) || (FileStatus == EFileStatus::Added);
286 | }
287 |
288 | bool FFileState::CanAdd() const
289 | {
290 | return FileStatus == EFileStatus::NotTracked;
291 | }
292 |
293 | bool FFileState::IsConflicted() const
294 | {
295 | // TODO: Figure out if the file is actually in conflict or not when retrieving the file status
296 | return false;
297 | }
298 |
299 | bool FFileState::CanDelete() const
300 | {
301 | // TODO: Stub for 4.14 build
302 | return (FileStatus == EFileStatus::Clean) || (FileStatus == EFileStatus::Added) || (FileStatus == EFileStatus::Removed);
303 | }
304 |
305 | bool FFileState::CanRevert() const
306 | {
307 | // TODO: This should work fine, but needs to be checked. Fix for UE 4.19
308 | return (FileStatus == EFileStatus::Modified) || (FileStatus == EFileStatus::Missing);
309 | }
310 |
311 | bool FFileState::IsCheckedOutInOtherBranch(const FString& CurrentBranch) const
312 | {
313 | // TODO: Stub for 4.20 build
314 | return false;
315 | }
316 |
317 | bool FFileState::IsModifiedInOtherBranch(const FString& CurrentBranch) const
318 | {
319 | // TODO: Stub for 4.20 build
320 | return false;
321 | }
322 |
323 | bool FFileState::IsCheckedOutOrModifiedInOtherBranch(const FString& CurrentBranch) const
324 | {
325 | // TODO: Stub for 4.20 build
326 | return false;
327 | }
328 |
329 | TArray FFileState::GetCheckedOutBranches() const
330 | {
331 | // TODO: Stub for 4.20 build
332 | return TArray();
333 | }
334 |
335 | FString FFileState::GetOtherUserBranchCheckedOuts() const
336 | {
337 | // TODO: Stub for 4.20 build
338 | return FString();
339 | }
340 |
341 | bool FFileState::GetOtherBranchHeadModification(FString& HeadBranchOut, FString& ActionOut, int32& HeadChangeListOut) const
342 | {
343 | // TODO: Stub for 4.20 build
344 | return false;
345 | }
346 |
347 | #undef LOCTEXT_NAMESPACE
348 |
349 | } // namespace MercurialSourceControl
350 |
--------------------------------------------------------------------------------
/Source/MercurialSourceControl/Private/MercurialSourceControlFileState.h:
--------------------------------------------------------------------------------
1 | //-------------------------------------------------------------------------------
2 | // The MIT License (MIT)
3 | //
4 | // Copyright (c) 2014 Vadim Macagon
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //-------------------------------------------------------------------------------
24 | #pragma once
25 |
26 | #include "ISourceControlState.h"
27 | #include "MercurialSourceControlFileRevision.h"
28 |
29 | namespace MercurialSourceControl {
30 |
31 | enum class EFileStatus
32 | {
33 | Unknown,
34 | Clean,
35 | Added,
36 | Removed,
37 | Modified,
38 | NotTracked,
39 | Ignored,
40 | Missing,
41 | };
42 |
43 | /**
44 | * Provides information relating to the current status of a file in a Mercurial repository,
45 | * and the revision history of that file.
46 | */
47 | class FFileState
48 | : public ISourceControlState
49 | , public TSharedFromThis
50 | {
51 | public:
52 | FFileState(const FString& InFilename)
53 | : AbsoluteFilename(InFilename)
54 | , FileStatus(EFileStatus::Unknown)
55 | , TimeStamp(0)
56 | {
57 | }
58 |
59 | void SetFileStatus(EFileStatus InFileStatus)
60 | {
61 | FileStatus = InFileStatus;
62 | }
63 |
64 | EFileStatus GetFileStatus() const
65 | {
66 | return FileStatus;
67 | }
68 |
69 | void SetTimeStamp(const FDateTime& InTimeStamp)
70 | {
71 | TimeStamp = InTimeStamp;
72 | }
73 |
74 | void SetHistory(const TArray& InFileRevisions)
75 | {
76 | History = InFileRevisions;
77 | }
78 |
79 | public:
80 | // ISourceControlState methods
81 |
82 | virtual int32 GetHistorySize() const;
83 | virtual FSourceControlRevisionPtr GetHistoryItem(int32 HistoryIndex) const override;
84 | virtual FSourceControlRevisionPtr FindHistoryRevision(int32 RevisionNumber) const override;
85 | virtual FSourceControlRevisionPtr FindHistoryRevision(const FString& InRevision) const override;
86 | virtual FSourceControlRevisionPtr GetBaseRevForMerge() const override;
87 | virtual FName GetIconName() const override;
88 | virtual FName GetSmallIconName() const override;
89 | virtual FText GetDisplayName() const override;
90 | virtual FText GetDisplayTooltip() const override;
91 | virtual const FString& GetFilename() const override;
92 | virtual const FDateTime& GetTimeStamp() const override;
93 | virtual bool CanCheckIn() const override;
94 | virtual bool CanCheckout() const override;
95 | virtual bool IsCheckedOut() const override;
96 | virtual bool IsCheckedOutOther(FString* Who = nullptr) const override;
97 | virtual bool IsCurrent() const override;
98 | virtual bool IsSourceControlled() const override;
99 | virtual bool IsAdded() const override;
100 | virtual bool IsDeleted() const override;
101 | virtual bool IsIgnored() const override;
102 | virtual bool CanEdit() const override;
103 | virtual bool IsUnknown() const override;
104 | virtual bool IsModified() const override;
105 | virtual bool CanAdd() const override;
106 | virtual bool IsConflicted() const override;
107 | virtual bool CanDelete() const override;
108 | virtual bool CanRevert() const override;
109 | virtual bool IsCheckedOutInOtherBranch(const FString& CurrentBranch = FString()) const override;
110 | virtual bool IsModifiedInOtherBranch(const FString& CurrentBranch = FString()) const override;
111 | virtual bool IsCheckedOutOrModifiedInOtherBranch(const FString& CurrentBranch = FString()) const override;
112 | virtual TArray GetCheckedOutBranches() const override;
113 | virtual FString GetOtherUserBranchCheckedOuts() const override;
114 | virtual bool GetOtherBranchHeadModification(FString& HeadBranchOut, FString& ActionOut, int32& HeadChangeListOut) const override;
115 |
116 | private:
117 | /** All the revisions of the file */
118 | TArray History;
119 |
120 | FString AbsoluteFilename;
121 | EFileStatus FileStatus;
122 |
123 | /**
124 | * Last time the state was updated.
125 | * @note This is not the last modified time of the file, just the last time
126 | * the FileStatus etc. member fields were updated.
127 | */
128 | FDateTime TimeStamp;
129 | };
130 |
131 | typedef TSharedRef FFileStateRef;
132 |
133 | } // MercurialSourceControl
134 |
--------------------------------------------------------------------------------
/Source/MercurialSourceControl/Private/MercurialSourceControlModule.cpp:
--------------------------------------------------------------------------------
1 | //-------------------------------------------------------------------------------
2 | // The MIT License (MIT)
3 | //
4 | // Copyright (c) 2014 Vadim Macagon
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //-------------------------------------------------------------------------------
24 |
25 | #include "MercurialSourceControlPrivatePCH.h"
26 | #include "MercurialSourceControlModule.h"
27 | #include "Features/IModularFeatures.h"
28 | #include "MercurialSourceControlOperationNames.h"
29 | #include "MercurialSourceControlWorkers.h"
30 | #include "MercurialSourceControlStyle.h"
31 |
32 | namespace MercurialSourceControl {
33 |
34 | namespace
35 | {
36 | template
37 | FWorkerRef CreateWorker()
38 | {
39 | return MakeShareable(new T());
40 | }
41 | } // unnamed namespace
42 |
43 | void FModule::StartupModule()
44 | {
45 | FMercurialStyle::Initialize();
46 |
47 | Provider.RegisterWorkerCreator(
48 | OperationNames::Connect,
49 | FCreateWorkerDelegate::CreateStatic(&CreateWorker)
50 | );
51 | Provider.RegisterWorkerCreator(
52 | OperationNames::UpdateStatus,
53 | FCreateWorkerDelegate::CreateStatic(&CreateWorker)
54 | );
55 | Provider.RegisterWorkerCreator(
56 | OperationNames::Revert,
57 | FCreateWorkerDelegate::CreateStatic(&CreateWorker)
58 | );
59 | Provider.RegisterWorkerCreator(
60 | OperationNames::Delete,
61 | FCreateWorkerDelegate::CreateStatic(&CreateWorker)
62 | );
63 | Provider.RegisterWorkerCreator(
64 | OperationNames::MarkForAdd,
65 | FCreateWorkerDelegate::CreateStatic(&CreateWorker)
66 | );
67 | Provider.RegisterWorkerCreator(
68 | OperationNames::CheckIn,
69 | FCreateWorkerDelegate::CreateStatic(&CreateWorker)
70 | );
71 |
72 | IModularFeatures::Get().RegisterModularFeature(FeatureName, &Provider);
73 | }
74 |
75 | void FModule::ShutdownModule()
76 | {
77 | Provider.Close();
78 | IModularFeatures::Get().UnregisterModularFeature(FeatureName, &Provider);
79 | FMercurialStyle::Shutdown();
80 | }
81 |
82 | bool FModule::IsGameModule() const
83 | {
84 | // no gameplay code in this module
85 | return false;
86 | }
87 |
88 | FProvider& FModule::GetProvider()
89 | {
90 | FModule& MercurialModule = FModuleManager::LoadModuleChecked("MercurialSourceControl");
91 | return MercurialModule.Provider;
92 | }
93 |
94 | } // namespace MercurialSourceControl
95 |
96 | IMPLEMENT_MODULE(MercurialSourceControl::FModule, MercurialSourceControl);
--------------------------------------------------------------------------------
/Source/MercurialSourceControl/Private/MercurialSourceControlModule.h:
--------------------------------------------------------------------------------
1 | //-------------------------------------------------------------------------------
2 | // The MIT License (MIT)
3 | //
4 | // Copyright (c) 2014 Vadim Macagon
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //-------------------------------------------------------------------------------
24 | #pragma once
25 |
26 | #include "MercurialSourceControlProvider.h"
27 |
28 | namespace MercurialSourceControl {
29 |
30 | class FModule : public IModuleInterface
31 | {
32 | public:
33 | // IModuleInterface methods
34 | virtual void StartupModule() override;
35 | virtual void ShutdownModule() override;
36 | virtual bool IsGameModule() const;
37 |
38 | public:
39 | FModule() : FeatureName("SourceControl") {}
40 |
41 | static FProvider& GetProvider();
42 |
43 | private:
44 | FProvider Provider;
45 | FName FeatureName;
46 | };
47 |
48 | } // namespace MercurialSourceControl
49 |
--------------------------------------------------------------------------------
/Source/MercurialSourceControl/Private/MercurialSourceControlOperationNames.cpp:
--------------------------------------------------------------------------------
1 | //-------------------------------------------------------------------------------
2 | // The MIT License (MIT)
3 | //
4 | // Copyright (c) 2014 Vadim Macagon
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //-------------------------------------------------------------------------------
24 |
25 | #include "MercurialSourceControlPrivatePCH.h"
26 | #include "MercurialSourceControlOperationNames.h"
27 |
28 | namespace MercurialSourceControl {
29 |
30 | const FName OperationNames::Connect("Connect");
31 | const FName OperationNames::UpdateStatus("UpdateStatus");
32 | const FName OperationNames::Revert("Revert");
33 | const FName OperationNames::Delete("Delete");
34 | const FName OperationNames::MarkForAdd("MarkForAdd");
35 | const FName OperationNames::CheckIn("CheckIn");
36 |
37 | } // namespace MercurialSourceControl
--------------------------------------------------------------------------------
/Source/MercurialSourceControl/Private/MercurialSourceControlOperationNames.h:
--------------------------------------------------------------------------------
1 | //-------------------------------------------------------------------------------
2 | // The MIT License (MIT)
3 | //
4 | // Copyright (c) 2014 Vadim Macagon
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //-------------------------------------------------------------------------------
24 | #pragma once
25 |
26 | namespace MercurialSourceControl {
27 |
28 | /** Names of all the operations supported by the Mercurial source control provider. */
29 | struct OperationNames
30 | {
31 | static const FName Connect;
32 | static const FName UpdateStatus;
33 | static const FName Revert;
34 | static const FName Delete;
35 | static const FName MarkForAdd;
36 | static const FName CheckIn;
37 | };
38 |
39 | } // namespace MercurialSourceControl
--------------------------------------------------------------------------------
/Source/MercurialSourceControl/Private/MercurialSourceControlPrivatePCH.h:
--------------------------------------------------------------------------------
1 | //-------------------------------------------------------------------------------
2 | // The MIT License (MIT)
3 | //
4 | // Copyright (c) 2014 Vadim Macagon
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //-------------------------------------------------------------------------------
24 | #pragma once
25 |
26 | #include "Core.h"
27 | #include "SlateBasics.h"
28 | #include "EditorStyle.h"
29 | #include "AssetToolsModule.h"
30 | #include "AssetRegistryModule.h"
31 |
--------------------------------------------------------------------------------
/Source/MercurialSourceControl/Private/MercurialSourceControlProvider.cpp:
--------------------------------------------------------------------------------
1 | //-------------------------------------------------------------------------------
2 | // The MIT License (MIT)
3 | //
4 | // Copyright (c) 2014 Vadim Macagon
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //-------------------------------------------------------------------------------
24 |
25 | #include "MercurialSourceControlPrivatePCH.h"
26 | #include "MercurialSourceControlProvider.h"
27 | #include "SMercurialSourceControlSettingsWidget.h"
28 | #include "MercurialSourceControlCommand.h"
29 | #include "MercurialSourceControlFileState.h"
30 | #include "MercurialSourceControlClient.h"
31 | #include "MessageLog.h"
32 | #include "ScopedSourceControlProgress.h"
33 | #include "MercurialSourceControlOperationNames.h"
34 | #include "ISourceControlModule.h"
35 | #include "ARFilter.h"
36 | #include "SourceControlOperations.h"
37 |
38 | namespace MercurialSourceControl {
39 |
40 | FName FProvider::SourceControlLogName("SourceControl");
41 |
42 | // for LOCTEXT()
43 | #define LOCTEXT_NAMESPACE "MercurialSourceControl"
44 |
45 | void FProvider::Init(bool bForceConnection)
46 | {
47 | Settings.Load();
48 | AbsoluteContentDirectory = FPaths::ConvertRelativePathToFull(FPaths::ProjectContentDir());
49 | }
50 |
51 | void FProvider::Close()
52 | {
53 | // clear out the file state cache
54 | FileStateMap.Empty();
55 | // destroy the FClient singleton
56 | FClient::Destroy();
57 | }
58 |
59 | const FName& FProvider::GetName() const
60 | {
61 | return ProviderName;
62 | }
63 |
64 | FText FProvider::GetStatusText() const
65 | {
66 | FFormatNamedArguments Args;
67 | Args.Add(TEXT("ProviderName"), LOCTEXT("Mercurial", "Mercurial"));
68 | Args.Add(TEXT("YesOrNo"), IsEnabled() ? LOCTEXT("Yes", "Yes") : LOCTEXT("No", "No"));
69 | Args.Add(TEXT("LocalPath"), FText::FromString(GetWorkingDirectory()));
70 | return FText::Format(
71 | LOCTEXT("Status", "Provider: {ProviderName}\nEnabled: {YesOrNo}\nRepository: {LocalPath}"),
72 | Args
73 | );
74 | }
75 |
76 | bool FProvider::IsEnabled() const
77 | {
78 | return FClient::Get().IsValid();
79 | }
80 |
81 | bool FProvider::IsAvailable() const
82 | {
83 | return IsEnabled();
84 | }
85 |
86 | ECommandResult::Type FProvider::Login(
87 | const FString& InPassword, EConcurrency::Type InConcurrency,
88 | const FSourceControlOperationComplete& InOperationCompleteDelegate
89 | )
90 | {
91 | // UnrealEd occasionally likes to "login" even though the "connection" has already been
92 | // established, just to be sure. There isn't much point in doing so with Mercurial so ignore
93 | // any pointless login requests.
94 | if (IsAvailable())
95 | {
96 | TSharedRef ConnectOperation =
97 | ISourceControlOperation::Create();
98 | ConnectOperation->SetPassword(InPassword);
99 | InOperationCompleteDelegate.ExecuteIfBound(ConnectOperation, ECommandResult::Succeeded);
100 | }
101 | else
102 | {
103 | // call the base class method
104 | return ISourceControlProvider::Login(InPassword, InConcurrency, InOperationCompleteDelegate);
105 | }
106 | return ECommandResult::Succeeded;
107 | }
108 |
109 | ECommandResult::Type FProvider::GetState(
110 | const TArray& InFiles,
111 | TArray< TSharedRef >& OutState,
112 | EStateCacheUsage::Type InStateCacheUsage
113 | )
114 | {
115 | if (!IsEnabled())
116 | {
117 | return ECommandResult::Failed;
118 | }
119 |
120 | TArray AbsoluteFiles;
121 | for (const auto& Filename : InFiles)
122 | {
123 | AbsoluteFiles.Add(FPaths::ConvertRelativePathToFull(Filename));
124 | }
125 |
126 | // update the cache if requested to do so
127 | if (InStateCacheUsage == EStateCacheUsage::ForceUpdate)
128 | {
129 | // TODO: Should really check the return value, but the SVN/Perforce providers don't
130 | // this call will block until the operation is complete
131 | Execute(ISourceControlOperation::Create(), AbsoluteFiles);
132 | }
133 |
134 | // retrieve the states for the given files from the cache
135 | for (const auto& Filename : AbsoluteFiles)
136 | {
137 | OutState.Add(GetFileStateFromCache(Filename));
138 | }
139 |
140 | return ECommandResult::Succeeded;
141 | }
142 |
143 | FDelegateHandle FProvider::RegisterSourceControlStateChanged_Handle(
144 | const FSourceControlStateChanged::FDelegate& SourceControlStateChanged
145 | )
146 | {
147 | return OnSourceControlStateChanged.Add(SourceControlStateChanged);
148 | }
149 |
150 | void FProvider::UnregisterSourceControlStateChanged_Handle(
151 | FDelegateHandle Handle
152 | )
153 | {
154 | OnSourceControlStateChanged.Remove(Handle);
155 | }
156 |
157 | ECommandResult::Type FProvider::Execute(
158 | const TSharedRef& InOperation,
159 | const TArray& InFiles,
160 | EConcurrency::Type InConcurrency,
161 | const FSourceControlOperationComplete& InOperationCompleteDelegate
162 | )
163 | {
164 | // the "Connect" operation is the only operation that can be performed while the
165 | // provider is disabled, if the operation is successful the provider will be enabled
166 | if (!IsEnabled() && (InOperation->GetName() != OperationNames::Connect))
167 | {
168 | return ECommandResult::Failed;
169 | }
170 |
171 | // attempt to create a worker to perform the requested operation
172 | FWorkerPtr WorkerPtr = CreateWorker(InOperation->GetName());
173 | if (!WorkerPtr.IsValid())
174 | {
175 | // apparently we don't support this particular operation
176 | FFormatNamedArguments Arguments;
177 | Arguments.Add(TEXT("OperationName"), FText::FromName(InOperation->GetName()));
178 | Arguments.Add(TEXT("ProviderName"), FText::FromName(GetName()));
179 | LogError(
180 | FText::Format(
181 | LOCTEXT(
182 | "UnsupportedOperation",
183 | "Operation '{OperationName}' not supported by source control provider '{ProviderName}'"
184 | ),
185 | Arguments
186 | )
187 | );
188 | return ECommandResult::Failed;
189 | }
190 |
191 | auto* Command = new FCommand(
192 | GetWorkingDirectory(), AbsoluteContentDirectory, InOperation,
193 | WorkerPtr.ToSharedRef(), InOperationCompleteDelegate
194 | );
195 |
196 | TArray AbsoluteFiles;
197 | if (InOperation->GetName() == OperationNames::Connect)
198 | {
199 | AbsoluteFiles.Add(Settings.GetMercurialPath());
200 | }
201 | else if (InOperation->GetName() == OperationNames::MarkForAdd)
202 | {
203 | TArray AbsoluteLargeFiles;
204 | PrepareFilenamesForAddCommand(InFiles, AbsoluteFiles, AbsoluteLargeFiles);
205 |
206 | if (AbsoluteLargeFiles.Num() > 0)
207 | {
208 | Command->SetAbsoluteLargeFiles(AbsoluteLargeFiles);
209 | }
210 | }
211 | else
212 | {
213 | for (const auto& Filename : InFiles)
214 | {
215 | AbsoluteFiles.Add(FPaths::ConvertRelativePathToFull(Filename));
216 | }
217 | }
218 |
219 | if (AbsoluteFiles.Num() > 0)
220 | {
221 | Command->SetAbsoluteFiles(AbsoluteFiles);
222 | }
223 |
224 | if (InConcurrency == EConcurrency::Synchronous)
225 | {
226 | auto Result = ExecuteSynchronousCommand(Command, InOperation->GetInProgressString());
227 | delete Command;
228 | return Result;
229 | }
230 | else
231 | {
232 | return ExecuteCommand(Command, true);
233 | }
234 | }
235 |
236 | bool FProvider::CanCancelOperation(
237 | const TSharedRef& InOperation
238 | ) const
239 | {
240 | // don't support cancellation
241 | return false;
242 | }
243 |
244 | void FProvider::CancelOperation(
245 | const TSharedRef& InOperation
246 | )
247 | {
248 | // nothing to do here
249 | }
250 |
251 | TArray< TSharedRef > FProvider::GetLabels(
252 | const FString& InMatchingSpec
253 | ) const
254 | {
255 | TArray< TSharedRef > Labels;
256 | // TODO: figure out if this needs to be implemented
257 | return Labels;
258 | }
259 |
260 | bool FProvider::UsesLocalReadOnlyState() const
261 | {
262 | return false;
263 | }
264 |
265 | bool FProvider::UsesChangelists() const
266 | {
267 | return false;
268 | }
269 |
270 | void FProvider::Tick()
271 | {
272 | bool bNotifyStateChanged = false;
273 |
274 | // remove commands that have finished executing from the queue
275 | for (int32 i = 0; i < CommandQueue.Num(); ++i)
276 | {
277 | FCommandQueueEntry CommandQueueEntry = CommandQueue[i];
278 | auto* Command = CommandQueueEntry.Command;
279 | if (Command->HasExecuted())
280 | {
281 | CommandQueue.RemoveAt(i);
282 | bNotifyStateChanged = Command->UpdateStates();
283 | LogErrors(Command->ErrorMessages);
284 | Command->NotifyOperationComplete();
285 |
286 | if (CommandQueueEntry.bAutoDelete)
287 | {
288 | delete Command;
289 | }
290 |
291 | // NotifyOperationComplete() may indirectly alter the command queue
292 | // so the loop must stop here, the queue cleanup will resume on the
293 | // next tick.
294 | break;
295 | }
296 | }
297 |
298 | if (bNotifyStateChanged)
299 | {
300 | OnSourceControlStateChanged.Broadcast();
301 | }
302 | }
303 |
304 | bool FProvider::UsesCheckout() const
305 | {
306 | // TODO
307 | return false;
308 | }
309 |
310 | bool FProvider::QueryStateBranchConfig(const FString& ConfigSrc, const FString& ConfigDest)
311 | {
312 | // TODO: Stub for 4.20 build
313 | return false;
314 | }
315 |
316 | void FProvider::RegisterStateBranches(const TArray& BranchNames, const FString& ContentRoot)
317 | {
318 | // TODO: Stub for 4.20 build
319 | }
320 |
321 | int32 FProvider::GetStateBranchIndex(const FString& BranchName) const
322 | {
323 | // TODO: Stub for 4.20 build
324 | return 0;
325 | }
326 |
327 | #if SOURCE_CONTROL_WITH_SLATE
328 | TSharedRef FProvider::MakeSettingsWidget() const
329 | {
330 | return SNew(SProviderSettingsWidget);
331 | }
332 | #endif // SOURCE_CONTROL_WITH_SLATE
333 |
334 | void FProvider::RegisterWorkerCreator(
335 | const FName& InOperationName, const FCreateWorkerDelegate& InDelegate
336 | )
337 | {
338 | WorkerCreatorsMap.Add(InOperationName, InDelegate);
339 | }
340 |
341 | bool FProvider::UpdateFileStateCache(const TArray& InStates)
342 | {
343 | for (auto StateIt(InStates.CreateConstIterator()); StateIt; StateIt++)
344 | {
345 | FFileStateRef FileState = GetFileStateFromCache(StateIt->GetFilename());
346 | FileState->SetFileStatus(StateIt->GetFileStatus());
347 | FileState->SetTimeStamp(StateIt->GetTimeStamp());
348 | }
349 | return InStates.Num() > 0;
350 | }
351 |
352 | bool FProvider::UpdateFileStateCache(
353 | const TMap >& InFileRevisionsMap
354 | )
355 | {
356 | for (auto It(InFileRevisionsMap.CreateConstIterator()); It; ++It)
357 | {
358 | FFileStateRef FileState = GetFileStateFromCache(It.Key());
359 | FileState->SetHistory(It.Value());
360 | FileState->SetTimeStamp(FDateTime::Now());
361 | }
362 | return InFileRevisionsMap.Num() > 0;
363 | }
364 |
365 | void FProvider::LogError(const FText& InErrorMessage)
366 | {
367 | FMessageLog(SourceControlLogName).Error(InErrorMessage);
368 | }
369 |
370 | void FProvider::LogErrors(const TArray& ErrorMessages)
371 | {
372 | FMessageLog SourceControlLog(SourceControlLogName);
373 | for (auto It(ErrorMessages.CreateConstIterator()); It; ++It)
374 | {
375 | SourceControlLog.Error(FText::FromString(*It));
376 | }
377 | }
378 |
379 | FFileStateRef FProvider::GetFileStateFromCache(const FString& Filename)
380 | {
381 | FFileStateRef* StatePtr = FileStateMap.Find(Filename);
382 | if (StatePtr)
383 | {
384 | return *StatePtr;
385 | }
386 | else
387 | {
388 | FFileStateRef DefaultFileState = MakeShareable(new FFileState(Filename));
389 | FileStateMap.Add(Filename, DefaultFileState);
390 | return DefaultFileState;
391 | }
392 | }
393 |
394 | ECommandResult::Type FProvider::ExecuteSynchronousCommand(
395 | FCommand* Command, const FText& ProgressText
396 | )
397 | {
398 | // display a progress dialog if progress text was provided
399 | FScopedSourceControlProgress Progress(ProgressText);
400 |
401 | // attempt to execute the command asynchronously
402 | ExecuteCommand(Command, false);
403 |
404 | // wait for the command to finish executing
405 | while (!Command->HasExecuted())
406 | {
407 | Tick();
408 | Progress.Tick();
409 | FPlatformProcess::Sleep(0.01f);
410 | }
411 |
412 | // make sure the command queue is cleaned up
413 | Tick();
414 |
415 | return Command->GetResult();
416 | }
417 |
418 | ECommandResult::Type FProvider::ExecuteCommand(FCommand* Command, bool bAutoDelete)
419 | {
420 | if (GThreadPool)
421 | {
422 | GThreadPool->AddQueuedWork(Command);
423 | CommandQueue.Add({Command, bAutoDelete});
424 | return ECommandResult::Succeeded;
425 | }
426 | else // fall back to synchronous execution
427 | {
428 | Command->DoWork();
429 | Command->UpdateStates();
430 | LogErrors(Command->ErrorMessages);
431 | Command->NotifyOperationComplete();
432 |
433 | auto Result = Command->GetResult();
434 | if (bAutoDelete)
435 | {
436 | delete Command;
437 | }
438 | return Result;
439 | }
440 | }
441 |
442 | FWorkerPtr FProvider::CreateWorker(const FName& InOperationName) const
443 | {
444 | const auto* CreateWorkerPtr = WorkerCreatorsMap.Find(InOperationName);
445 | if (CreateWorkerPtr)
446 | {
447 | return CreateWorkerPtr->Execute();
448 | }
449 | return nullptr;
450 | }
451 |
452 | void FProvider::PrepareFilenamesForAddCommand(
453 | const TArray& InFiles,
454 | TArray& OutAbsoluteFiles, TArray& OutAbsoluteLargeFiles
455 | )
456 | {
457 | if (Settings.IsLargefilesIntegrationEnabled())
458 | {
459 | FARFilter LargeAssetFilter;
460 | LargeAssetFilter.bRecursiveClasses = true;
461 |
462 | // convert filenames to long package names that can be used in the asset filter
463 | for (const auto& Filename : InFiles)
464 | {
465 | // currently only .uasset files can be auto-flagged as large
466 | if (!Filename.EndsWith(FPackageName::GetAssetPackageExtension()))
467 | {
468 | continue;
469 | }
470 |
471 | FString PackageName;
472 | if (!FPackageName::TryConvertFilenameToLongPackageName(Filename, PackageName))
473 | {
474 | UE_LOG(
475 | LogSourceControl, Error,
476 | TEXT("Failed to convert filename '%s' to package name"), *Filename
477 | );
478 | continue;
479 | }
480 |
481 | LargeAssetFilter.PackageNames.Add(*PackageName);
482 | }
483 |
484 | // add the asset types that the user has designated as "large" to the asset filter
485 | TArray LargeAssetTypes;
486 | Settings.GetLargeAssetTypes(LargeAssetTypes);
487 | for (const auto& ClassName : LargeAssetTypes)
488 | {
489 | LargeAssetFilter.ClassNames.Add(*ClassName);
490 | }
491 |
492 | FAssetRegistryModule& AssetRegistryModule =
493 | FModuleManager::LoadModuleChecked(TEXT("AssetRegistry"));
494 |
495 | // find all the assets matching the asset filter
496 | TArray LargeAssets;
497 | AssetRegistryModule.Get().GetAssets(LargeAssetFilter, LargeAssets);
498 |
499 | // convert the long package names of all matching assets back to filenames
500 | for (const auto& Asset : LargeAssets)
501 | {
502 | FString RelativePath = FPackageName::LongPackageNameToFilename(
503 | Asset.PackageName.ToString(), FPackageName::GetAssetPackageExtension()
504 | );
505 | OutAbsoluteLargeFiles.Add(
506 | FPaths::ConvertRelativePathToFull(RelativePath)
507 | );
508 | }
509 |
510 | // any input file that didn't match the asset filter will be added with no special flags
511 | for (const auto& Filename : InFiles)
512 | {
513 | FString FullPath(FPaths::ConvertRelativePathToFull(Filename));
514 | if (!OutAbsoluteLargeFiles.Contains(FullPath))
515 | {
516 | OutAbsoluteFiles.Add(FullPath);
517 | }
518 | }
519 | }
520 | else
521 | {
522 | for (const auto& Filename : InFiles)
523 | {
524 | OutAbsoluteFiles.Add(FPaths::ConvertRelativePathToFull(Filename));
525 | }
526 | }
527 | }
528 |
529 | TArray FProvider::GetCachedStateByPredicate(
530 | TFunctionRef Predicate
531 | ) const
532 | {
533 | TArray MatchingFileStates;
534 | for (const auto& FileStateMapEntry : FileStateMap)
535 | {
536 | auto FileState = FileStateMapEntry.Value;
537 | if (Predicate(FileState))
538 | {
539 | MatchingFileStates.Add(FileState);
540 | }
541 | }
542 | return MatchingFileStates;
543 | }
544 |
545 | #undef LOCTEXT_NAMESPACE
546 |
547 | } // namespace namespace MercurialSourceControl
548 |
--------------------------------------------------------------------------------
/Source/MercurialSourceControl/Private/MercurialSourceControlProvider.h:
--------------------------------------------------------------------------------
1 | //-------------------------------------------------------------------------------
2 | // The MIT License (MIT)
3 | //
4 | // Copyright (c) 2014 Vadim Macagon
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //-------------------------------------------------------------------------------
24 | #pragma once
25 |
26 | #include "ISourceControlProvider.h"
27 | #include "IMercurialSourceControlWorker.h"
28 | #include "MercurialSourceControlFileState.h"
29 | #include "MercurialSourceControlProviderSettings.h"
30 |
31 | namespace MercurialSourceControl {
32 |
33 | DECLARE_DELEGATE_RetVal(FWorkerRef, FCreateWorkerDelegate)
34 |
35 | class FCommand;
36 |
37 | /**
38 | * Provides access to the file revision history stored in a Mercurial repository.
39 | *
40 | * This source control provider works with files that are stored in the project Content directory.
41 | * The project Content directory must be located within a Mercurial repository,
42 | * or be the root of the repository itself.
43 | */
44 | class FProvider : public ISourceControlProvider
45 | {
46 | public:
47 | // ISourceControlProvider methods
48 |
49 | virtual void Init(bool bForceConnection = true) override;
50 | virtual void Close() override;
51 | virtual const FName& GetName() const override;
52 | virtual FText GetStatusText() const override;
53 | virtual bool IsEnabled() const override;
54 | virtual bool IsAvailable() const override;
55 |
56 | virtual ECommandResult::Type Login(
57 | const FString& InPassword, EConcurrency::Type InConcurrency,
58 | const FSourceControlOperationComplete& InOperationCompleteDelegate
59 | ) override;
60 |
61 | virtual ECommandResult::Type GetState(
62 | const TArray& InFiles,
63 | TArray< TSharedRef >& OutState,
64 | EStateCacheUsage::Type InStateCacheUsage
65 | ) override;
66 |
67 | virtual TArray GetCachedStateByPredicate(
68 | TFunctionRef Predicate
69 | ) const override;
70 |
71 |
72 | virtual FDelegateHandle RegisterSourceControlStateChanged_Handle(
73 | const FSourceControlStateChanged::FDelegate& SourceControlStateChanged
74 | ) override;
75 |
76 | virtual void UnregisterSourceControlStateChanged_Handle(
77 | FDelegateHandle Handle
78 | ) override;
79 |
80 | virtual ECommandResult::Type Execute(
81 | const TSharedRef& InOperation,
82 | const TArray& InFiles,
83 | EConcurrency::Type InConcurrency = EConcurrency::Synchronous,
84 | const FSourceControlOperationComplete& InOperationCompleteDelegate = FSourceControlOperationComplete()
85 | ) override;
86 |
87 | virtual bool CanCancelOperation(
88 | const TSharedRef& InOperation
89 | ) const override;
90 |
91 | virtual void CancelOperation(
92 | const TSharedRef& InOperation
93 | ) override;
94 |
95 | virtual TArray< TSharedRef > GetLabels(
96 | const FString& InMatchingSpec
97 | ) const override;
98 |
99 | virtual bool UsesLocalReadOnlyState() const override;
100 | virtual bool UsesChangelists() const override;
101 | virtual void Tick() override;
102 | virtual bool UsesCheckout() const override;
103 | virtual bool QueryStateBranchConfig(const FString& ConfigSrc, const FString& ConfigDest) override;
104 | virtual void RegisterStateBranches(const TArray& BranchNames, const FString& ContentRoot) override;
105 | virtual int32 GetStateBranchIndex(const FString& BranchName) const override;
106 |
107 | #if SOURCE_CONTROL_WITH_SLATE
108 | virtual TSharedRef MakeSettingsWidget() const override;
109 | #endif // SOURCE_CONTROL_WITH_SLATE
110 |
111 | public:
112 | FProvider() : ProviderName("Mercurial") {}
113 |
114 | /**
115 | * Register a delegate that creates a worker.
116 | * Each worker performs a specific source control operation.
117 | * @param InOperationName The name of the operation the worker will perform.
118 | * @param InDelegate The delegate that will be called to create a worker.
119 | */
120 | void RegisterWorkerCreator(const FName& InOperationName, const FCreateWorkerDelegate& InDelegate);
121 |
122 | /** Update the file status cache with the content of the given file states. */
123 | bool UpdateFileStateCache(const TArray& InStates);
124 |
125 | /** Update the file status cache with the content of the given file revisions. */
126 | bool UpdateFileStateCache(const TMap >& InFileRevisionsMap);
127 |
128 | static void LogError(const FText& InErrorMessage);
129 | static void LogErrors(const TArray& ErrorMessages);
130 |
131 | /**
132 | * Set the absolute path to the repository root.
133 | * @note The path must end in a '/'.
134 | */
135 | void SetRepositoryRoot(const FString& InRepositoryRoot)
136 | {
137 | check(!FPaths::IsRelative(InRepositoryRoot));
138 |
139 | RepositoryRoot = InRepositoryRoot;
140 | }
141 |
142 | /** Get the working directory that will be used when hg.exe is invoked. */
143 | const FString& GetWorkingDirectory() const
144 | {
145 | // repository root will only be set after a successful "Connect" command
146 | return (RepositoryRoot.Len() > 0) ? RepositoryRoot : AbsoluteContentDirectory;
147 | }
148 |
149 | FProviderSettings& GetSettings()
150 | {
151 | return Settings;
152 | }
153 |
154 | private:
155 | /**
156 | * Attempt to retrieve the state of the given file from the cache, if that fails create a
157 | * default state for the file.
158 | */
159 | FFileStateRef GetFileStateFromCache(const FString& Filename);
160 |
161 | /**
162 | * Execute a command synchronously.
163 | * @param ProgressText Text to be displayed on the progress dialog while the command is
164 | * executing.
165 | */
166 | ECommandResult::Type ExecuteSynchronousCommand(FCommand* Command, const FText& ProgressText);
167 |
168 | /**
169 | * Execute a command asynchronously if possible,
170 | * fall back to synchronous execution if necessary.
171 | * @param bAutoDelete If true the command will be deleted after it finishes executing,
172 | * assuming it's executed asynchronously this will happen in Tick().
173 | */
174 | ECommandResult::Type ExecuteCommand(FCommand* Command, bool bAutoDelete);
175 |
176 | /**
177 | * Attempt to create a worker to perform the named operation,
178 | * if that fails return an invalid pointer.
179 | */
180 | FWorkerPtr CreateWorker(const FName& InOperationName) const;
181 |
182 | /**
183 | * Split out the given files into two sets, regular, and large.
184 | * @param InFiles Either relative or absolute filenames that should be tracked by Mercurial.
185 | * @param OutAbsoluteFiles Will be filled in with the absolute filenames of any files in
186 | * InFiles that are not in OutAbsoluteFiles.
187 | * @param OutAbsoluteLargeFiles Will be filled in with the absolute filenames of any files in
188 | * InFiles that match one of the asset types specified by the user.
189 | */
190 | void PrepareFilenamesForAddCommand(
191 | const TArray& InFiles,
192 | TArray& OutAbsoluteFiles, TArray& OutAbsoluteLargeFiles
193 | );
194 |
195 | private:
196 | /** All the registered worker creation delegates. */
197 | TMap WorkerCreatorsMap;
198 |
199 | struct FCommandQueueEntry
200 | {
201 | FCommand* Command;
202 | bool bAutoDelete;
203 | };
204 |
205 | /** Queue of commands given by the main thread. */
206 | TArray CommandQueue;
207 |
208 | /** Cache of file states. */
209 | TMap FileStateMap;
210 |
211 | /** Used to notify when the state of an item (or group of items) has changed. */
212 | FSourceControlStateChanged OnSourceControlStateChanged;
213 |
214 | /** Absolute path to the current project's content directory. */
215 | FString AbsoluteContentDirectory;
216 |
217 | /** Absolute path to the repository root directory. */
218 | FString RepositoryRoot;
219 |
220 | /** User accessible settings. */
221 | FProviderSettings Settings;
222 |
223 | FName ProviderName;
224 |
225 | private:
226 | static FName SourceControlLogName;
227 | };
228 |
229 | } // namespace MercurialSourceControl
230 |
--------------------------------------------------------------------------------
/Source/MercurialSourceControl/Private/MercurialSourceControlProviderSettings.cpp:
--------------------------------------------------------------------------------
1 | //-------------------------------------------------------------------------------
2 | // The MIT License (MIT)
3 | //
4 | // Copyright (c) 2014 Vadim Macagon
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //-------------------------------------------------------------------------------
24 |
25 | #include "MercurialSourceControlPrivatePCH.h"
26 | #include "MercurialSourceControlProviderSettings.h"
27 | #include "SourceControlHelpers.h"
28 |
29 | namespace MercurialSourceControl {
30 |
31 | namespace Settings {
32 | const TCHAR* Section = TEXT("MercurialSourceControl.ProviderSettings");
33 | const TCHAR* MercurialPath = TEXT("MercurialPath");
34 | const TCHAR* LargefilesIntegration = TEXT("LargefilesIntegration");
35 | const TCHAR* LargeAssetTypes = TEXT("LargeAssetTypes");
36 | } // namespace Settings
37 |
38 |
39 | const FString& FProviderSettings::GetMercurialPath() const
40 | {
41 | FScopeLock ScopeLock(&CriticalSection);
42 | return MercurialPath;
43 | }
44 |
45 | void FProviderSettings::SetMercurialPath(const FString& InMercurialPath)
46 | {
47 | FScopeLock ScopeLock(&CriticalSection);
48 | MercurialPath = InMercurialPath;
49 | }
50 |
51 | bool FProviderSettings::IsLargefilesIntegrationEnabled() const
52 | {
53 | FScopeLock ScopeLock(&CriticalSection);
54 | return bEnableLargefilesIntegration;
55 | }
56 |
57 | void FProviderSettings::EnableLargefilesIntegration(bool bEnable)
58 | {
59 | FScopeLock ScopeLock(&CriticalSection);
60 | bEnableLargefilesIntegration = bEnable;
61 | }
62 |
63 | void FProviderSettings::GetLargeAssetTypes(TArray& OutLargeAssetTypes) const
64 | {
65 | FScopeLock ScopeLock(&CriticalSection);
66 | OutLargeAssetTypes = LargeAssetTypes;
67 | }
68 |
69 | void FProviderSettings::SetLargeAssetTypes(const TArray& InLargeAssetTypes)
70 | {
71 | FScopeLock ScopeLock(&CriticalSection);
72 | LargeAssetTypes = InLargeAssetTypes;
73 | }
74 |
75 | void FProviderSettings::Save()
76 | {
77 | FScopeLock ScopeLock(&CriticalSection);
78 | const FString& SettingsFile = SourceControlHelpers::GetSettingsIni();
79 | if (GConfig)
80 | {
81 | GConfig->SetString(Settings::Section, Settings::MercurialPath, *MercurialPath, SettingsFile);
82 | GConfig->SetBool(Settings::Section, Settings::LargefilesIntegration, bEnableLargefilesIntegration, SettingsFile);
83 | GConfig->SetArray(Settings::Section, Settings::LargeAssetTypes, LargeAssetTypes, SettingsFile);
84 | }
85 | }
86 |
87 | void FProviderSettings::Load()
88 | {
89 | FScopeLock ScopeLock(&CriticalSection);
90 | const FString& SettingsFile = SourceControlHelpers::GetSettingsIni();
91 | if (GConfig)
92 | {
93 | GConfig->GetString(Settings::Section, Settings::MercurialPath, MercurialPath, SettingsFile);
94 | GConfig->GetBool(Settings::Section, Settings::LargefilesIntegration, bEnableLargefilesIntegration, SettingsFile);
95 | GConfig->GetArray(Settings::Section, Settings::LargeAssetTypes, LargeAssetTypes, SettingsFile);
96 | }
97 | }
98 |
99 | } // namespace MercurialSourceControl
100 |
--------------------------------------------------------------------------------
/Source/MercurialSourceControl/Private/MercurialSourceControlProviderSettings.h:
--------------------------------------------------------------------------------
1 | //-------------------------------------------------------------------------------
2 | // The MIT License (MIT)
3 | //
4 | // Copyright (c) 2014 Vadim Macagon
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //-------------------------------------------------------------------------------
24 |
25 | #pragma once
26 |
27 | namespace MercurialSourceControl {
28 |
29 | /** Provides access to settings stored in SourceControlSettings.ini. */
30 | class FProviderSettings
31 | {
32 | public:
33 | const FString& GetMercurialPath() const;
34 | void SetMercurialPath(const FString& InMercurialPath);
35 | bool IsLargefilesIntegrationEnabled() const;
36 | void EnableLargefilesIntegration(bool bEnable);
37 | void GetLargeAssetTypes(TArray& OutLargeAssetTypes) const;
38 | void SetLargeAssetTypes(const TArray& InLargeAssetTypes);
39 |
40 | void Save();
41 | void Load();
42 |
43 | private:
44 | mutable FCriticalSection CriticalSection;
45 |
46 | /** Full path to Mercurial executable. */
47 | FString MercurialPath;
48 |
49 | bool bEnableLargefilesIntegration;
50 |
51 | /**
52 | Class names of all the asset types that should be flagged as "large" by the Largefiles
53 | extension.
54 | */
55 | TArray LargeAssetTypes;
56 | };
57 |
58 | } // namespace MercurialSourceControl
59 |
--------------------------------------------------------------------------------
/Source/MercurialSourceControl/Private/MercurialSourceControlStyle.cpp:
--------------------------------------------------------------------------------
1 | //-------------------------------------------------------------------------------
2 | // The MIT License (MIT)
3 | //
4 | // Copyright (c) 2014 Vadim Macagon
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //-------------------------------------------------------------------------------
24 |
25 | #include "MercurialSourceControlPrivatePCH.h"
26 | #include "MercurialSourceControlStyle.h"
27 | #include "SlateStyle.h"
28 | #include "EditorStyle.h"
29 |
30 | namespace MercurialSourceControl {
31 |
32 | const FName FMercurialStyle::CleanStatusIcon32("Mercurial.CleanStatusIcon");
33 | const FName FMercurialStyle::CleanStatusIcon16("Mercurial.CleanStatusIcon.Small");
34 | const FName FMercurialStyle::AddedStatusIcon32("Mercurial.AddedStatusIcon");
35 | const FName FMercurialStyle::AddedStatusIcon16("Mercurial.AddedStatusIcon.Small");
36 | const FName FMercurialStyle::ModifiedStatusIcon32("Mercurial.ModifiedStatusIcon");
37 | const FName FMercurialStyle::ModifiedStatusIcon16("Mercurial.ModifiedStatusIcon.Small");
38 | const FName FMercurialStyle::RemovedStatusIcon32("Mercurial.RemovedStatusIcon");
39 | const FName FMercurialStyle::RemovedStatusIcon16("Mercurial.RemovedStatusIcon.Small");
40 | const FName FMercurialStyle::NotTrackedStatusIcon32("Mercurial.NotTrackedStatusIcon");
41 | const FName FMercurialStyle::NotTrackedStatusIcon16("Mercurial.NotTrackedStatusIcon.Small");
42 | const FName FMercurialStyle::MissingStatusIcon32("Mercurial.MissingStatusIcon");
43 | const FName FMercurialStyle::MissingStatusIcon16("Mercurial.MissingStatusIcon.Small");
44 |
45 | TSharedPtr MercurialSourceControl::FMercurialStyle::Instance = nullptr;
46 |
47 | void FMercurialStyle::Initialize()
48 | {
49 | if (!Instance.IsValid())
50 | {
51 | Instance = Create();
52 | }
53 | SetStyle(Instance.ToSharedRef());
54 | }
55 |
56 | void FMercurialStyle::Shutdown()
57 | {
58 | ResetToDefault(); // switch back to the previous style
59 | ensure(Instance.IsUnique());
60 | Instance.Reset();
61 | }
62 |
63 | TSharedRef FMercurialStyle::Create()
64 | {
65 | auto& EditorStyleModule = FModuleManager::LoadModuleChecked(TEXT("EditorStyle"));
66 | TSharedRef EditorStyleSetRef = EditorStyleModule.CreateEditorStyleInstance();
67 | FSlateStyleSet& EditorStyle = EditorStyleSetRef.Get();
68 |
69 | const FVector2D Icon16x16(16.0f, 16.0f);
70 | const FVector2D Icon32x32(32.0f, 32.0f);
71 |
72 | const FString SlateBrushesPath =
73 | FSlateBrush::UTextureIdentifier()
74 | // content root (i.e. parent directory of this plugin's Content folder),
75 | // will be resolved to the location of the MercurialSourceControl.uplugin file,
76 | // but this only works if "CanContainContent" is set to true in the .uplugin file
77 | + "/MercurialSourceControl"
78 | // asset path that's relative to /Content
79 | + "/SlateBrushes";
80 |
81 | EditorStyle.Set(
82 | CleanStatusIcon32,
83 | new FSlateImageBrush(
84 | SlateBrushesPath / TEXT("CleanStatusIcon.CleanStatusIcon"), Icon32x32
85 | )
86 | );
87 | EditorStyle.Set(
88 | CleanStatusIcon16,
89 | new FSlateImageBrush(
90 | SlateBrushesPath / TEXT("CleanStatusIcon.CleanStatusIcon"), Icon16x16
91 | )
92 | );
93 | EditorStyle.Set(
94 | AddedStatusIcon32,
95 | new FSlateImageBrush(
96 | SlateBrushesPath / TEXT("AddedStatusIcon.AddedStatusIcon"), Icon32x32
97 | )
98 | );
99 | EditorStyle.Set(
100 | AddedStatusIcon16,
101 | new FSlateImageBrush(
102 | SlateBrushesPath / TEXT("AddedStatusIcon.AddedStatusIcon"), Icon16x16
103 | )
104 | );
105 | EditorStyle.Set(
106 | ModifiedStatusIcon32,
107 | new FSlateImageBrush(
108 | SlateBrushesPath / TEXT("ModifiedStatusIcon.ModifiedStatusIcon"), Icon32x32
109 | )
110 | );
111 | EditorStyle.Set(
112 | ModifiedStatusIcon16,
113 | new FSlateImageBrush(
114 | SlateBrushesPath / TEXT("ModifiedStatusIcon.ModifiedStatusIcon"), Icon16x16
115 | )
116 | );
117 | EditorStyle.Set(
118 | RemovedStatusIcon32,
119 | new FSlateImageBrush(
120 | SlateBrushesPath / TEXT("RemovedStatusIcon.RemovedStatusIcon"), Icon32x32
121 | )
122 | );
123 | EditorStyle.Set(
124 | RemovedStatusIcon16,
125 | new FSlateImageBrush(
126 | SlateBrushesPath / TEXT("RemovedStatusIcon.RemovedStatusIcon"), Icon16x16
127 | )
128 | );
129 | EditorStyle.Set(
130 | NotTrackedStatusIcon32,
131 | new FSlateImageBrush(
132 | SlateBrushesPath / TEXT("NotTrackedStatusIcon.NotTrackedStatusIcon"), Icon32x32
133 | )
134 | );
135 | EditorStyle.Set(
136 | NotTrackedStatusIcon16,
137 | new FSlateImageBrush(
138 | SlateBrushesPath / TEXT("NotTrackedStatusIcon.NotTrackedStatusIcon"), Icon16x16
139 | )
140 | );
141 | EditorStyle.Set(
142 | MissingStatusIcon32,
143 | new FSlateImageBrush(
144 | SlateBrushesPath / TEXT("MissingStatusIcon.MissingStatusIcon"), Icon32x32
145 | )
146 | );
147 | EditorStyle.Set(
148 | MissingStatusIcon16,
149 | new FSlateImageBrush(
150 | SlateBrushesPath / TEXT("MissingStatusIcon.MissingStatusIcon"), Icon16x16
151 | )
152 | );
153 |
154 | return EditorStyleSetRef;
155 | }
156 |
157 | } // namespace MercurialSourceControl
158 |
--------------------------------------------------------------------------------
/Source/MercurialSourceControl/Private/MercurialSourceControlStyle.h:
--------------------------------------------------------------------------------
1 | //-------------------------------------------------------------------------------
2 | // The MIT License (MIT)
3 | //
4 | // Copyright (c) 2014 Vadim Macagon
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //-------------------------------------------------------------------------------
24 | #pragma once
25 |
26 | class FSlateStyleSet;
27 |
28 | /**
29 | * Extends EditorStyle with Mercurial source control icon overlays.
30 | */
31 | namespace MercurialSourceControl {
32 |
33 | class FMercurialStyle : public FEditorStyle
34 | {
35 | public:
36 | /** Replace the current EditorStyle with this one. */
37 | static void Initialize();
38 | /** Restore the previous EditorStyle. */
39 | static void Shutdown();
40 |
41 | public:
42 | static const FName CleanStatusIcon32;
43 | static const FName CleanStatusIcon16;
44 | static const FName AddedStatusIcon32;
45 | static const FName AddedStatusIcon16;
46 | static const FName ModifiedStatusIcon32;
47 | static const FName ModifiedStatusIcon16;
48 | static const FName RemovedStatusIcon32;
49 | static const FName RemovedStatusIcon16;
50 | static const FName NotTrackedStatusIcon32;
51 | static const FName NotTrackedStatusIcon16;
52 | static const FName MissingStatusIcon32;
53 | static const FName MissingStatusIcon16;
54 |
55 | private:
56 | FMercurialStyle() {}
57 |
58 | static TSharedRef Create();
59 |
60 | private:
61 | static TSharedPtr Instance;
62 | };
63 |
64 | } // namespace MercurialSourceControl
65 |
--------------------------------------------------------------------------------
/Source/MercurialSourceControl/Private/MercurialSourceControlWorkers.cpp:
--------------------------------------------------------------------------------
1 | //-------------------------------------------------------------------------------
2 | // The MIT License (MIT)
3 | //
4 | // Copyright (c) 2014 Vadim Macagon
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //-------------------------------------------------------------------------------
24 |
25 | #include "MercurialSourceControlPrivatePCH.h"
26 | #include "MercurialSourceControlWorkers.h"
27 | #include "MercurialSourceControlOperationNames.h"
28 | #include "MercurialSourceControlClient.h"
29 | #include "MercurialSourceControlModule.h"
30 | #include "MercurialSourceControlCommand.h"
31 |
32 | namespace MercurialSourceControl {
33 |
34 | #define LOCTEXT_NAMESPACE "MercurialSourceControl.Workers"
35 |
36 | FName FConnectWorker::GetName() const
37 | {
38 | return OperationNames::Connect;
39 | }
40 |
41 | bool FConnectWorker::Execute(FCommand& InCommand)
42 | {
43 | check(InCommand.GetOperation()->GetName() == OperationNames::Connect);
44 | check(InCommand.GetAbsoluteFiles().Num() == 1);
45 |
46 | FText ErrorMessage;
47 | TSharedRef Operation =
48 | StaticCastSharedRef(InCommand.GetOperation());
49 |
50 | if (!FClient::Create(InCommand.GetAbsoluteFiles()[0], ErrorMessage))
51 | {
52 | Operation->SetErrorText(ErrorMessage);
53 | return false;
54 | }
55 |
56 | if (!FClient::Get()->GetRepositoryRoot(InCommand.GetWorkingDirectory(), RepositoryRoot))
57 | {
58 | Operation->SetErrorText(
59 | FText::Format(
60 | LOCTEXT("DirNotInRepo", "Directory '{0}' is not in a Mercurial repository."),
61 | FText::FromString(InCommand.GetWorkingDirectory())
62 | )
63 | );
64 | return false;
65 | }
66 |
67 | return true;
68 | }
69 |
70 | bool FConnectWorker::UpdateStates() const
71 | {
72 | FProvider& Provider = FModule::GetProvider();
73 | if (!RepositoryRoot.IsEmpty())
74 | {
75 | Provider.SetRepositoryRoot(RepositoryRoot);
76 | }
77 | return false;
78 | }
79 |
80 | FName FUpdateStatusWorker::GetName() const
81 | {
82 | return OperationNames::UpdateStatus;
83 | }
84 |
85 | bool FUpdateStatusWorker::Execute(FCommand& InCommand)
86 | {
87 | check(InCommand.GetOperation()->GetName() == OperationNames::UpdateStatus);
88 |
89 | const FClientSharedPtr Client = FClient::Get();
90 | if (!Client.IsValid())
91 | {
92 | return false;
93 | }
94 |
95 | TSharedRef Operation =
96 | StaticCastSharedRef(InCommand.GetOperation());
97 |
98 | bool bResult = false;
99 |
100 | if (Operation->ShouldGetOpenedOnly())
101 | {
102 | // What Perforce calls "opened" files roughly corresponds to files with an
103 | // added/modified/removed status in Mercurial. To keep things simple we'll just update
104 | // the status of all the files in the current content directory.
105 | TArray Files;
106 | if (InCommand.GetWorkingDirectory() != InCommand.GetContentDirectory())
107 | {
108 | FString Directory = InCommand.GetContentDirectory();
109 | if (FPaths::MakePathRelativeTo(Directory, *InCommand.GetWorkingDirectory()))
110 | {
111 | Files.Add(Directory);
112 | }
113 | else
114 | {
115 | // In this particular case the working directory should be the repository root,
116 | // if the content directory can't be made relative to the repository root then
117 | // it's not in the repository!
118 | // TODO: localize the error message
119 | InCommand.ErrorMessages.Add(TEXT("Content directory is not in a repository."));
120 | return false;
121 | }
122 | }
123 | bResult = Client->GetFileStates(
124 | InCommand.GetWorkingDirectory(), Files, FileStates, InCommand.ErrorMessages
125 | );
126 | }
127 | else if (InCommand.GetAbsoluteFiles().Num() > 0)
128 | {
129 | bResult = Client->GetFileStates(
130 | InCommand.GetWorkingDirectory(), InCommand.GetAbsoluteFiles(), FileStates,
131 | InCommand.ErrorMessages
132 | );
133 | }
134 | else // no filenames were provided, so there's nothing to do
135 | {
136 | return true;
137 | }
138 |
139 | if (Operation->ShouldUpdateHistory() && (InCommand.GetAbsoluteFiles().Num() > 0))
140 | {
141 | bResult = Client->GetFileHistory(
142 | InCommand.GetWorkingDirectory(), InCommand.GetAbsoluteFiles(), FileRevisionsMap,
143 | InCommand.ErrorMessages
144 | );
145 | }
146 |
147 | return bResult;
148 | }
149 |
150 | bool FUpdateStatusWorker::UpdateStates() const
151 | {
152 | FProvider& Provider = FModule::GetProvider();
153 | bool bStatesUpdated = false;
154 | if (FileStates.Num() > 0)
155 | {
156 | bStatesUpdated |= Provider.UpdateFileStateCache(FileStates);
157 | }
158 | if (FileRevisionsMap.Num() > 0)
159 | {
160 | bStatesUpdated |= Provider.UpdateFileStateCache(FileRevisionsMap);
161 | }
162 | return bStatesUpdated;
163 | }
164 |
165 | FName FRevertWorker::GetName() const
166 | {
167 | return OperationNames::Revert;
168 | }
169 |
170 | bool FRevertWorker::Execute(FCommand& InCommand)
171 | {
172 | check(InCommand.GetOperation()->GetName() == OperationNames::Revert);
173 |
174 | const FClientSharedPtr Client = FClient::Get();
175 | if (!Client.IsValid())
176 | {
177 | return false;
178 | }
179 |
180 | bool bResult = Client->RevertFiles(
181 | InCommand.GetWorkingDirectory(), InCommand.GetAbsoluteFiles(), InCommand.ErrorMessages
182 | );
183 |
184 | bResult &= Client->GetFileStates(
185 | InCommand.GetWorkingDirectory(), InCommand.GetAbsoluteFiles(), FileStates,
186 | InCommand.ErrorMessages
187 | );
188 |
189 | return bResult;
190 | }
191 |
192 | bool FRevertWorker::UpdateStates() const
193 | {
194 | return FModule::GetProvider().UpdateFileStateCache(FileStates);
195 | }
196 |
197 | FName FDeleteWorker::GetName() const
198 | {
199 | return OperationNames::Delete;
200 | }
201 |
202 | bool FDeleteWorker::Execute(FCommand& InCommand)
203 | {
204 | check(InCommand.GetOperation()->GetName() == OperationNames::Delete);
205 |
206 | const FClientSharedPtr Client = FClient::Get();
207 | if (!Client.IsValid())
208 | {
209 | return false;
210 | }
211 |
212 | // NOTE: This will not remove files with an "added" status, but the Editor seems to revert
213 | // files before deleting them, so we shouldn't need to handle "added" files here.
214 | bool bResult = Client->RemoveFiles(
215 | InCommand.GetWorkingDirectory(), InCommand.GetAbsoluteFiles(), InCommand.ErrorMessages
216 | );
217 |
218 | bResult &= Client->GetFileStates(
219 | InCommand.GetWorkingDirectory(), InCommand.GetAbsoluteFiles(), FileStates,
220 | InCommand.ErrorMessages
221 | );
222 |
223 | return bResult;
224 | }
225 |
226 | bool FDeleteWorker::UpdateStates() const
227 | {
228 | return FModule::GetProvider().UpdateFileStateCache(FileStates);
229 | }
230 |
231 | FName FMarkForAddWorker::GetName() const
232 | {
233 | return OperationNames::MarkForAdd;
234 | }
235 |
236 | bool FMarkForAddWorker::Execute(FCommand& InCommand)
237 | {
238 | check(InCommand.GetOperation()->GetName() == OperationNames::MarkForAdd);
239 | check((InCommand.GetAbsoluteFiles().Num() > 0) || (InCommand.GetAbsoluteLargeFiles().Num() > 0));
240 |
241 | const FClientSharedPtr Client = FClient::Get();
242 | if (!Client.IsValid())
243 | {
244 | return false;
245 | }
246 |
247 | bool bResult = true;
248 |
249 | if (InCommand.GetAbsoluteFiles().Num() > 0)
250 | {
251 | bResult &= Client->AddFiles(
252 | InCommand.GetWorkingDirectory(), InCommand.GetAbsoluteFiles(), false,
253 | InCommand.ErrorMessages
254 | );
255 | }
256 |
257 | if (InCommand.GetAbsoluteLargeFiles().Num() > 0)
258 | {
259 | bResult &= Client->AddFiles(
260 | InCommand.GetWorkingDirectory(), InCommand.GetAbsoluteLargeFiles(), true,
261 | InCommand.ErrorMessages
262 | );
263 | }
264 |
265 | bResult &= Client->GetFileStates(
266 | InCommand.GetWorkingDirectory(), InCommand.GetAbsoluteFiles(), FileStates,
267 | InCommand.ErrorMessages
268 | );
269 |
270 | return bResult;
271 | }
272 |
273 | bool FMarkForAddWorker::UpdateStates() const
274 | {
275 | return FModule::GetProvider().UpdateFileStateCache(FileStates);
276 | }
277 |
278 | FName FCheckInWorker::GetName() const
279 | {
280 | return OperationNames::CheckIn;
281 | }
282 |
283 | bool FCheckInWorker::Execute(FCommand& InCommand)
284 | {
285 | check(InCommand.GetOperation()->GetName() == OperationNames::CheckIn);
286 |
287 | const FClientSharedPtr Client = FClient::Get();
288 | if (!Client.IsValid())
289 | {
290 | return false;
291 | }
292 |
293 | TSharedRef Operation =
294 | StaticCastSharedRef(InCommand.GetOperation());
295 |
296 | bool bResult = Client->CommitFiles(
297 | InCommand.GetWorkingDirectory(), InCommand.GetAbsoluteFiles(),
298 | Operation->GetDescription().ToString(), InCommand.ErrorMessages
299 | );
300 |
301 | if (bResult)
302 | {
303 | FString RevisionID;
304 | bool bRetrievedID = Client->GetWorkingDirectoryParentRevisionID(
305 | InCommand.GetWorkingDirectory(), RevisionID, InCommand.ErrorMessages
306 | );
307 |
308 | if (!bRetrievedID)
309 | {
310 | RevisionID = "???";
311 | }
312 |
313 | Operation->SetSuccessMessage(
314 | FText::Format(
315 | LOCTEXT("CommitSuccessful", "Committed revision {0}."),
316 | FText::FromString(RevisionID)
317 | )
318 | );
319 | }
320 |
321 | bResult &= Client->GetFileStates(
322 | InCommand.GetWorkingDirectory(), InCommand.GetAbsoluteFiles(), FileStates,
323 | InCommand.ErrorMessages
324 | );
325 |
326 | return bResult;
327 | }
328 |
329 | bool FCheckInWorker::UpdateStates() const
330 | {
331 | return FModule::GetProvider().UpdateFileStateCache(FileStates);
332 | }
333 |
334 | #undef LOCTEXT_NAMESPACE
335 |
336 | } // namespace MercurialSourceControl
337 |
--------------------------------------------------------------------------------
/Source/MercurialSourceControl/Private/MercurialSourceControlWorkers.h:
--------------------------------------------------------------------------------
1 | //-------------------------------------------------------------------------------
2 | // The MIT License (MIT)
3 | //
4 | // Copyright (c) 2014 Vadim Macagon
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //-------------------------------------------------------------------------------
24 | #pragma once
25 |
26 | #include "IMercurialSourceControlWorker.h"
27 | #include "MercurialSourceControlFileRevision.h"
28 |
29 | namespace MercurialSourceControl {
30 |
31 | class FCommand;
32 | class FFileState;
33 |
34 | /**
35 | * Determines the location of the Mercurial repository root.
36 | * If the repository root is not found the Mercurial source control provider will not be enabled.
37 | */
38 | class FConnectWorker : public IWorker
39 | {
40 | public:
41 | virtual FName GetName() const override;
42 | virtual bool Execute(FCommand& InCommand) override;
43 | virtual bool UpdateStates() const override;
44 |
45 | private:
46 | FString RepositoryRoot;
47 | };
48 |
49 | /**
50 | * Updates the file status and file revision history caches in the Mercurial
51 | * source control provider.
52 | */
53 | class FUpdateStatusWorker : public IWorker
54 | {
55 | public:
56 | virtual FName GetName() const override;
57 | virtual bool Execute(FCommand& InCommand) override;
58 | virtual bool UpdateStates() const override;
59 |
60 | private:
61 | TArray FileStates;
62 | TMap > FileRevisionsMap;
63 | };
64 |
65 | /** Reverts files back to the most recent revision in the repository. */
66 | class FRevertWorker : public IWorker
67 | {
68 | public:
69 | virtual FName GetName() const;
70 | virtual bool Execute(FCommand& InCommand);
71 | virtual bool UpdateStates() const;
72 |
73 | private:
74 | TArray FileStates;
75 | };
76 |
77 | /** Removes files from the repository. */
78 | class FDeleteWorker : public IWorker
79 | {
80 | public:
81 | virtual FName GetName() const;
82 | virtual bool Execute(FCommand& InCommand);
83 | virtual bool UpdateStates() const;
84 |
85 | private:
86 | TArray FileStates;
87 | };
88 |
89 | /** Marks files to be added to the repository. */
90 | class FMarkForAddWorker : public IWorker
91 | {
92 | public:
93 | virtual FName GetName() const;
94 | virtual bool Execute(FCommand& InCommand);
95 | virtual bool UpdateStates() const;
96 |
97 | private:
98 | TArray FileStates;
99 | };
100 |
101 | /** Commits files to the repository. */
102 | class FCheckInWorker : public IWorker
103 | {
104 | public:
105 | virtual FName GetName() const;
106 | virtual bool Execute(FCommand& InCommand);
107 | virtual bool UpdateStates() const;
108 |
109 | private:
110 | TArray FileStates;
111 | };
112 |
113 | } // namespace MercurialSourceControl
114 |
--------------------------------------------------------------------------------
/Source/MercurialSourceControl/Private/SLargeAssetTypeTreeWidget.cpp:
--------------------------------------------------------------------------------
1 | //-------------------------------------------------------------------------------
2 | // The MIT License (MIT)
3 | //
4 | // Copyright (c) 2014 Vadim Macagon
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //-------------------------------------------------------------------------------
24 |
25 | #include "MercurialSourceControlPrivatePCH.h"
26 | #include "SLargeAssetTypeTreeWidget.h"
27 |
28 | #include "IAssetTypeActions.h"
29 | #include "IAssetTools.h"
30 |
31 | namespace MercurialSourceControl {
32 |
33 | typedef IAssetTypeActions IAssetType; // sorry, but the original name was horrid
34 | typedef TWeakPtr FAssetTypeWeakPtr;
35 |
36 | /**
37 | * A tree item in an SLargeAssetTypeTreeWidget.
38 | *
39 | * Tree items of this type can represent either an asset category or an asset type.
40 | * Asset categories are the top level tree items and can contain asset type items.
41 | */
42 | class FLargeAssetTypeTreeItem : public TSharedFromThis
43 | {
44 | public:
45 | // text that will be displayed for this item in the tree view
46 | FText Title;
47 | // parents store shared pointers to children,
48 | // so children must only store weak pointers to their parents to ensure proper cleanup
49 | FLargeAssetTypeTreeItemWeakPtr Parent;
50 | // only asset category items will actually have any children
51 | TArray Children;
52 | // the class name that corresponds to this asset type
53 | FString AssetTypeClassName;
54 |
55 | public:
56 | FLargeAssetTypeTreeItem(const FText& InTitle)
57 | : Title(InTitle)
58 | , bIsSelected(false)
59 | {
60 | }
61 |
62 | FLargeAssetTypeTreeItem(
63 | const IAssetType& InAssetType, const TWeakPtr& InParent
64 | )
65 | : Parent(InParent)
66 | , bIsSelected(false)
67 | {
68 | Title = InAssetType.GetName();
69 | AssetTypeClassName = InAssetType.GetSupportedClass()->GetName();
70 | }
71 |
72 | bool IsSelected() const
73 | {
74 | if (SharedState.IsValid())
75 | {
76 | return SharedState->bIsSelected;
77 | }
78 | return bIsSelected;
79 | }
80 |
81 | void SetIsSelected(bool bInIsSelected)
82 | {
83 | if (SharedState.IsValid())
84 | {
85 | SharedState->bIsSelected = bInIsSelected;
86 | }
87 | else
88 | {
89 | bIsSelected = bInIsSelected;
90 | }
91 | }
92 |
93 | static void CreateSharedState(
94 | const TArray& InAssetTypeItems, bool bIsSelected
95 | )
96 | {
97 | // An asset type may belong to multiple asset categories, however,
98 | // all tree items are distinct, so multiple tree items may correspond to the same
99 | // asset type. The checked state is shared between such duplicate items,
100 | // so that checking/unchecking an asset type in one category results in that
101 | // asset type being checked/unchecked in any other categories it may appear in.
102 | TSharedPtr SharedState = MakeShareable(new FSharedState(bIsSelected));
103 |
104 | for (auto AssetTypeItem : InAssetTypeItems)
105 | {
106 | AssetTypeItem.Pin()->SharedState = SharedState;
107 | }
108 | }
109 |
110 | private:
111 | // state shared by items that represent asset types belonging to multiple categories
112 | struct FSharedState : public TSharedFromThis
113 | {
114 | bool bIsSelected;
115 |
116 | FSharedState(bool bInIsSelected) : bIsSelected(bInIsSelected) {}
117 | };
118 |
119 | private:
120 | // only relevant for asset type items, and furthermore only when SharedState.IsValid() == false
121 | bool bIsSelected;
122 | // will only be valid for asset type items that correspond to asset types that belong to
123 | // multiple categories
124 | TSharedPtr SharedState;
125 | };
126 |
127 | #define LOCTEXT_NAMESPACE "MercurialSourceControl.SLargeAssetTypeTreeWidget"
128 |
129 | void SLargeAssetTypeTreeWidget::Construct(const FArguments& InArgs)
130 | {
131 | OnItemCheckStateChanged = InArgs._OnItemCheckStateChanged;
132 |
133 | ChildSlot
134 | [
135 | SAssignNew(TreeView, SLargeAssetTypeTreeView)
136 | .SelectionMode(ESelectionMode::None)
137 | .TreeItemsSource(&AssetCategories)
138 | // get child items for any given parent item
139 | .OnGetChildren(this, &SLargeAssetTypeTreeWidget::TreeView_OnGetChildren)
140 | // generate a widget for each item
141 | .OnGenerateRow(this, &SLargeAssetTypeTreeWidget::TreeView_OnGenerateRow)
142 | ];
143 |
144 | Populate(InArgs._SelectedAssetTypeNames);
145 | }
146 |
147 | void SLargeAssetTypeTreeWidget::AddCategoriesToCategoryMap(
148 | TMap& OutCategoryMap
149 | )
150 | {
151 | // reuse localized strings from the content browser
152 | #define NS_CONTENT_BROWSER "ContentBrowser"
153 |
154 | OutCategoryMap.Add(
155 | EAssetTypeCategories::Basic,
156 | MakeShareable(
157 | new FLargeAssetTypeTreeItem(
158 | NSLOCTEXT(NS_CONTENT_BROWSER, "BasicFilter", "Basic")
159 | )
160 | )
161 | );
162 | OutCategoryMap.Add(
163 | EAssetTypeCategories::Animation,
164 | MakeShareable(
165 | new FLargeAssetTypeTreeItem(
166 | NSLOCTEXT(NS_CONTENT_BROWSER, "AnimationFilter", "Animation")
167 | )
168 | )
169 | );
170 | OutCategoryMap.Add(
171 | EAssetTypeCategories::MaterialsAndTextures,
172 | MakeShareable(
173 | new FLargeAssetTypeTreeItem(
174 | NSLOCTEXT(NS_CONTENT_BROWSER, "MaterialFilter", "Materials & Textures")
175 | )
176 | )
177 | );
178 | OutCategoryMap.Add(
179 | EAssetTypeCategories::Sounds,
180 | MakeShareable(
181 | new FLargeAssetTypeTreeItem(
182 | NSLOCTEXT(NS_CONTENT_BROWSER, "SoundFilter", "Sounds")
183 | )
184 | )
185 | );
186 | OutCategoryMap.Add(
187 | EAssetTypeCategories::Physics,
188 | MakeShareable(
189 | new FLargeAssetTypeTreeItem(
190 | NSLOCTEXT(NS_CONTENT_BROWSER, "PhysicsFilter", "Physics")
191 | )
192 | )
193 | );
194 | OutCategoryMap.Add(
195 | EAssetTypeCategories::Misc,
196 | MakeShareable(
197 | new FLargeAssetTypeTreeItem(
198 | NSLOCTEXT(NS_CONTENT_BROWSER, "MiscFilter", "Miscellaneous")
199 | )
200 | )
201 | );
202 |
203 | #undef NS_CONTENT_BROWSER
204 | }
205 |
206 | void SLargeAssetTypeTreeWidget::Populate(const TArray& InSelectedAssetTypeClassNames)
207 | {
208 | AssetCategories.Empty();
209 |
210 | TMap CategoryMap;
211 | AddCategoriesToCategoryMap(CategoryMap);
212 |
213 | FAssetToolsModule& AssetToolsModule =
214 | FModuleManager::LoadModuleChecked(TEXT("AssetTools"));
215 | TArray AssetTypes;
216 | AssetToolsModule.Get().GetAssetTypeActionsList(AssetTypes);
217 |
218 | struct FOrderAssetTypesByNameAsc
219 | {
220 | // return true if A should appear before B, false otherwise
221 | bool operator()(const FAssetTypeWeakPtr& A, const FAssetTypeWeakPtr& B) const
222 | {
223 | return A.Pin()->GetName().CompareTo(B.Pin()->GetName()) < 0;
224 | }
225 | };
226 |
227 | AssetTypes.Sort(FOrderAssetTypesByNameAsc());
228 |
229 | // assign all the asset types to the corresponding category tree items
230 | for (const auto AssetTypeWeakPtr : AssetTypes)
231 | {
232 | TSharedPtr AssetType = AssetTypeWeakPtr.Pin();
233 | // for consistency ignore asset types that can't be filtered by in the content browser,
234 | // usually this is because the asset type is not fully supported
235 | if (AssetType.IsValid() && AssetType->CanFilter())
236 | {
237 | TArray AssetTypeItems;
238 | for (auto CategoryMapEntry : CategoryMap)
239 | {
240 | if (AssetType->GetCategories() & CategoryMapEntry.Key)
241 | {
242 | FLargeAssetTypeTreeItemPtr AssetTypeItem =
243 | MakeShareable(
244 | new FLargeAssetTypeTreeItem(*AssetType.Get(), CategoryMapEntry.Value)
245 | );
246 |
247 | AssetTypeItems.Add(AssetTypeItem);
248 | CategoryMapEntry.Value->Children.Add(AssetTypeItem);
249 | }
250 | }
251 |
252 | if (AssetTypeItems.Num() == 1)
253 | {
254 | auto AssetTypeItem = AssetTypeItems[0].Pin();
255 | AssetTypeItem->SetIsSelected(
256 | InSelectedAssetTypeClassNames.Contains(AssetTypeItem->AssetTypeClassName)
257 | );
258 | }
259 | else if (AssetTypeItems.Num() > 1)
260 | {
261 | auto AssetTypeItem = AssetTypeItems[0].Pin();
262 | FLargeAssetTypeTreeItem::CreateSharedState(
263 | AssetTypeItems,
264 | InSelectedAssetTypeClassNames.Contains(AssetTypeItem->AssetTypeClassName)
265 | );
266 | }
267 | }
268 | }
269 |
270 | for (const auto CategoryMapEntry : CategoryMap)
271 | {
272 | AssetCategories.Add(CategoryMapEntry.Value);
273 | }
274 |
275 | TreeView->RequestTreeRefresh();
276 | }
277 |
278 | void SLargeAssetTypeTreeWidget::SelectAssetTypesByClassName(
279 | const TArray& InAssetTypeClassNames
280 | )
281 | {
282 | for (const auto AssetCategory : AssetCategories)
283 | {
284 | for (const auto AssetType : AssetCategory->Children)
285 | {
286 | AssetType->SetIsSelected(InAssetTypeClassNames.Contains(AssetType->AssetTypeClassName));
287 | }
288 | }
289 | }
290 |
291 | void SLargeAssetTypeTreeWidget::GetSelectedAssetTypeClassNames(
292 | TArray& OutAssetTypeClassNames
293 | ) const
294 | {
295 | for (const auto AssetCategory : AssetCategories)
296 | {
297 | for (const auto AssetType : AssetCategory->Children)
298 | {
299 | if (AssetType->IsSelected())
300 | {
301 | OutAssetTypeClassNames.AddUnique(AssetType->AssetTypeClassName);
302 | }
303 | }
304 | }
305 | }
306 |
307 | TSharedRef SLargeAssetTypeTreeWidget::TreeView_OnGenerateRow(
308 | FLargeAssetTypeTreeItemPtr Item, const TSharedRef& OwnerTable
309 | )
310 | {
311 | return
312 | SNew(STableRow, OwnerTable)
313 | [
314 | SNew(SHorizontalBox)
315 | + SHorizontalBox::Slot()
316 | .AutoWidth()
317 | [
318 | SNew(SCheckBox)
319 | .IsChecked(
320 | this, &SLargeAssetTypeTreeWidget::TreeView_IsChecked,
321 | FLargeAssetTypeTreeItemWeakPtr(Item)
322 | )
323 | .OnCheckStateChanged(
324 | this, &SLargeAssetTypeTreeWidget::TreeView_OnCheckStateChanged,
325 | FLargeAssetTypeTreeItemWeakPtr(Item)
326 | )
327 | ]
328 | + SHorizontalBox::Slot()
329 | [
330 | SNew(STextBlock)
331 | .Text(Item->Title)
332 | .ToolTipText(
333 | !Item->AssetTypeClassName.IsEmpty() ?
334 | FText::FromString(FString(TEXT("Class: ")) + Item->AssetTypeClassName) : FText()
335 | )
336 | ]
337 | ];
338 | }
339 |
340 | void SLargeAssetTypeTreeWidget::TreeView_OnGetChildren(
341 | FLargeAssetTypeTreeItemPtr Parent, TArray& OutChildren
342 | )
343 | {
344 | if (Parent.IsValid())
345 | {
346 | OutChildren = Parent->Children;
347 | }
348 | }
349 |
350 | ECheckBoxState SLargeAssetTypeTreeWidget::TreeView_IsChecked(
351 | FLargeAssetTypeTreeItemWeakPtr ItemWeakPtr
352 | ) const
353 | {
354 | FLargeAssetTypeTreeItemPtr Item = ItemWeakPtr.Pin();
355 | if (Item.IsValid())
356 | {
357 | if (Item->Children.Num() > 0) // asset category item
358 | {
359 | // the checked state of an asset category item is determined by the checked states of
360 | // the contained asset type items
361 | int32 NumberOfSelectedChildren = 0;
362 | for (const auto ChildItem : Item->Children)
363 | {
364 | if (ChildItem->IsSelected())
365 | {
366 | ++NumberOfSelectedChildren;
367 | }
368 | }
369 |
370 | if (NumberOfSelectedChildren == Item->Children.Num())
371 | {
372 | return ECheckBoxState::Checked;
373 | }
374 | else if (NumberOfSelectedChildren == 0)
375 | {
376 | return ECheckBoxState::Unchecked;
377 | }
378 | else // some but not all asset type items in this asset category are checked
379 | {
380 | return ECheckBoxState::Undetermined;
381 | }
382 | }
383 | else // asset type item
384 | {
385 | return Item->IsSelected() ?
386 | ECheckBoxState::Checked : ECheckBoxState::Unchecked;
387 | }
388 | }
389 | return ECheckBoxState::Undetermined;
390 | }
391 |
392 | void SLargeAssetTypeTreeWidget::TreeView_OnCheckStateChanged(
393 | ECheckBoxState NewState, FLargeAssetTypeTreeItemWeakPtr ItemWeakPtr)
394 | {
395 | FLargeAssetTypeTreeItemPtr Item = ItemWeakPtr.Pin();
396 | if (Item.IsValid())
397 | {
398 | bool bIsItemChecked = (NewState == ECheckBoxState::Checked);
399 | // propagate the checked state of the asset category to the asset types within it
400 | for (const auto ItemChild : Item->Children)
401 | {
402 | ItemChild->SetIsSelected(bIsItemChecked);
403 | }
404 | Item->SetIsSelected(bIsItemChecked);
405 | // the delegate will only be executed once, even if the user checks/unchecks an asset
406 | // category and the checked state of multiple asset type items in that category changes,
407 | // this is by design
408 | OnItemCheckStateChanged.ExecuteIfBound();
409 | }
410 | }
411 |
412 | #undef LOCTEXT_NAMESPACE
413 |
414 | } // namespace MercurialSourceControl
415 |
--------------------------------------------------------------------------------
/Source/MercurialSourceControl/Private/SLargeAssetTypeTreeWidget.h:
--------------------------------------------------------------------------------
1 | //-------------------------------------------------------------------------------
2 | // The MIT License (MIT)
3 | //
4 | // Copyright (c) 2014 Vadim Macagon
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //-------------------------------------------------------------------------------
24 | #pragma once
25 |
26 | #include "AssetTypeCategories.h"
27 |
28 | namespace MercurialSourceControl {
29 |
30 | typedef TSharedPtr FLargeAssetTypeTreeItemPtr;
31 | typedef TWeakPtr FLargeAssetTypeTreeItemWeakPtr;
32 |
33 | /** A tree view that displays all available asset types grouped by asset categories. */
34 | class SLargeAssetTypeTreeWidget : public SCompoundWidget
35 | {
36 | public:
37 | /** Invoked when the user checks/unchecks an item in the tree. */
38 | DECLARE_DELEGATE(FOnItemCheckStateChanged);
39 |
40 | SLATE_BEGIN_ARGS(SLargeAssetTypeTreeWidget) {}
41 |
42 | SLATE_ARGUMENT(TArray, SelectedAssetTypeNames)
43 | SLATE_EVENT(FOnItemCheckStateChanged, OnItemCheckStateChanged)
44 |
45 | SLATE_END_ARGS()
46 |
47 | public:
48 | void Construct(const FArguments& InArgs);
49 |
50 | void GetSelectedAssetTypeClassNames(TArray& OutAssetTypeClassNames) const;
51 |
52 | private:
53 | static void AddCategoriesToCategoryMap(
54 | TMap& OutCategoryMap
55 | );
56 |
57 | private:
58 | /** Load all the asset categories and get TreeView to redraw itself. */
59 | void Populate(const TArray& InSelectedAssetTypeClassNames);
60 |
61 | /** Check the items in TreeView that match the given asset type class names. */
62 | void SelectAssetTypesByClassName(const TArray& InAssetTypeClassNames);
63 |
64 | /** Called by TreeView to generate a table row for the given item. */
65 | TSharedRef TreeView_OnGenerateRow(
66 | FLargeAssetTypeTreeItemPtr Item, const TSharedRef& OwnerTable
67 | );
68 |
69 | /** Called by TreeView to obtain child items for the given parent item. */
70 | void TreeView_OnGetChildren(
71 | FLargeAssetTypeTreeItemPtr Parent, TArray& OutChildren
72 | );
73 |
74 | /** Called by TreeView to obtain the checked state of the given item. */
75 | ECheckBoxState TreeView_IsChecked(FLargeAssetTypeTreeItemWeakPtr ItemWeakPtr) const;
76 |
77 | /** Called by TreeView when an item is checked or unchecked. */
78 | void TreeView_OnCheckStateChanged(
79 | ECheckBoxState NewState, FLargeAssetTypeTreeItemWeakPtr ItemWeakPtr
80 | );
81 |
82 | private:
83 | typedef STreeView SLargeAssetTypeTreeView;
84 | TSharedPtr TreeView;
85 |
86 | /** Asset categories are the top-level items in the tree. */
87 | TArray AssetCategories;
88 |
89 | /** Delegate to execute when the user checks/unchecks an item in the tree. */
90 | FOnItemCheckStateChanged OnItemCheckStateChanged;
91 | };
92 |
93 | } // namespace MercurialSourceControl
94 |
--------------------------------------------------------------------------------
/Source/MercurialSourceControl/Private/SMercurialSourceControlSettingsWidget.cpp:
--------------------------------------------------------------------------------
1 | //-------------------------------------------------------------------------------
2 | // The MIT License (MIT)
3 | //
4 | // Copyright (c) 2014 Vadim Macagon
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //-------------------------------------------------------------------------------
24 |
25 | #include "MercurialSourceControlPrivatePCH.h"
26 | #include "SMercurialSourceControlSettingsWidget.h"
27 | #include "DesktopPlatformModule.h"
28 | #include "MercurialSourceControlModule.h"
29 | #include "MercurialSourceControlClient.h"
30 | #include "SLargeAssetTypeTreeWidget.h"
31 |
32 | namespace MercurialSourceControl {
33 |
34 | #define LOCTEXT_NAMESPACE "MercurialSourceControl.SSettingsWidget"
35 |
36 | void SProviderSettingsWidget::Construct(const FArguments& InArgs)
37 | {
38 | const FProviderSettings& ProviderSettings = FModule::GetProvider().GetSettings();
39 | FString MercurialPath = ProviderSettings.GetMercurialPath();
40 | if (MercurialPath.IsEmpty())
41 | {
42 | FClient::FindExecutable(MercurialPath);
43 | }
44 | MercurialPathText = FText::FromString(MercurialPath);
45 | bEnableLargefilesIntegration = ProviderSettings.IsLargefilesIntegrationEnabled();
46 | TArray LargeAssetTypes;
47 | ProviderSettings.GetLargeAssetTypes(LargeAssetTypes);
48 |
49 | FSlateFontInfo TextFont = FEditorStyle::GetFontStyle(TEXT("SourceControl.LoginWindow.Font"));
50 |
51 | ChildSlot
52 | [
53 | SNew(SVerticalBox)
54 | // Mercurial Executable
55 | +SVerticalBox::Slot()
56 | .AutoHeight()
57 | [
58 | SNew(SHorizontalBox)
59 | +SHorizontalBox::Slot()
60 | .FillWidth(1.0f)
61 | [
62 | SNew(SVerticalBox)
63 | +SVerticalBox::Slot()
64 | .FillHeight(1.0f)
65 | .Padding(2.0f)
66 | .VAlign(VAlign_Center)
67 | [
68 | SNew(STextBlock)
69 | .Text(LOCTEXT("MercurialExecutableLabel", "Mercurial Executable"))
70 | .ToolTipText(
71 | LOCTEXT(
72 | "MercurialExecutableLabel_ToolTip", "Path to Mercurial executable (hg.exe)"
73 | )
74 | )
75 | .Font(TextFont)
76 | ]
77 | ]
78 | +SHorizontalBox::Slot()
79 | .FillWidth(2.0f)
80 | [
81 | SNew(SVerticalBox)
82 | +SVerticalBox::Slot()
83 | .FillHeight(1.0f)
84 | .Padding(2.0f)
85 | [
86 | SNew(SHorizontalBox)
87 | +SHorizontalBox::Slot()
88 | .FillWidth(3.0f)
89 | [
90 | SNew(SEditableTextBox)
91 | .Text(this, &SProviderSettingsWidget::GetMercurialPathText)
92 | .OnTextCommitted(this, &SProviderSettingsWidget::MercurialPath_OnTextCommitted)
93 | .OnTextChanged(
94 | this, &SProviderSettingsWidget::MercurialPath_OnTextCommitted,
95 | ETextCommit::Default
96 | )
97 | .Font(TextFont)
98 | ]
99 | +SHorizontalBox::Slot()
100 | .FillWidth(1.0f)
101 | .Padding(5.0f, 0.0f, 0.0f, 0.0f)
102 | [
103 | SNew(SButton)
104 | .HAlign(HAlign_Center)
105 | .VAlign(VAlign_Center)
106 | .Text(LOCTEXT("MercurialExecutableBrowseButtonLabel", "Browse"))
107 | .OnClicked(this, &SProviderSettingsWidget::MercurialPathBrowse_OnClicked)
108 | ]
109 | ]
110 | ]
111 | ]
112 | // Enable Largefiles Integration Checkbox
113 | +SVerticalBox::Slot()
114 | .AutoHeight()
115 | [
116 | SNew(SCheckBox)
117 | .IsChecked(EnableLargefilesIntegration_IsChecked())
118 | .OnCheckStateChanged(this, &SProviderSettingsWidget::EnableLargefilesIntegration_OnCheckStateChanged)
119 | .ToolTipText(
120 | LOCTEXT(
121 | "EnableLargefiles_Tooltip",
122 | "When enabled the editor will always flag certain asset types as \"large\" when adding them to a repository."
123 | )
124 | )
125 | [
126 | SNew(STextBlock)
127 | .Text(LOCTEXT("EnableLargefilesLabel", "Enable Largefiles Integration"))
128 | .Font(TextFont)
129 | ]
130 | ]
131 | // Largefiles Integration Settings
132 | +SVerticalBox::Slot()
133 | .AutoHeight()
134 | [
135 | // wrap the asset type tree in a fixed height box to prevent excessive
136 | // flickering when toggling the visibility of this section
137 | SAssignNew(LargefilesSettingsBox, SBox)
138 | .Padding(FMargin(0.0f, 10.0f))
139 | .HeightOverride(400.0f)
140 | .Visibility(GetLargeAssetTypeTreeVisibility())
141 | [
142 | SNew(SHorizontalBox)
143 | + SHorizontalBox::Slot()
144 | .FillWidth(1.0f)
145 | [
146 | SNew(STextBlock)
147 | .Text(
148 | LOCTEXT(
149 | "LargeAssetTypeTreeLabel",
150 | "Select the asset types the editor should always mark as \"large\" when adding them to a repository."
151 | )
152 | )
153 | .Font(TextFont)
154 | .AutoWrapText(true)
155 | ]
156 | + SHorizontalBox::Slot()
157 | .FillWidth(2.0f)
158 | [
159 | SNew(SBorder)
160 | .BorderImage(FEditorStyle::GetBrush("DetailsView.CategoryMiddle"))
161 | .Padding(FMargin(5.0f))
162 | [
163 | SAssignNew(LargeAssetTypeTreeWidget, SLargeAssetTypeTreeWidget)
164 | .SelectedAssetTypeNames(LargeAssetTypes)
165 | .OnItemCheckStateChanged(
166 | this,
167 | &SProviderSettingsWidget::LargeAssetTypeTree_OnItemCheckStateChanged
168 | )
169 | ]
170 | ]
171 | ]
172 | ]
173 | ];
174 | }
175 |
176 | FText SProviderSettingsWidget::GetMercurialPathText() const
177 | {
178 | return MercurialPathText;
179 | }
180 |
181 | void SProviderSettingsWidget::MercurialPath_OnTextCommitted(
182 | const FText& InText, ETextCommit::Type InCommitType
183 | )
184 | {
185 | FProviderSettings& Settings = FModule::GetProvider().GetSettings();
186 | Settings.SetMercurialPath(InText.ToString());
187 | Settings.Save();
188 | // update the backing field for the SEditableTextBox
189 | MercurialPathText = InText;
190 | }
191 |
192 | FReply SProviderSettingsWidget::MercurialPathBrowse_OnClicked()
193 | {
194 | IDesktopPlatform* DesktopPlatform = FDesktopPlatformModule::Get();
195 | if (DesktopPlatform)
196 | {
197 | FString MercurialPath = MercurialPathText.ToString();
198 |
199 | #ifdef PLATFORM_WINDOWS
200 | const FString Filter(TEXT("Executable files (*.exe;*.bat;*.cmd)|*.exe;*.bat;*.cmd"));
201 | #else
202 | const FString Filter(TEXT("All files (*.*)|*.*"));
203 | #endif
204 |
205 | TArray SelectedFiles;
206 |
207 | bool bFileSelected = DesktopPlatform->OpenFileDialog(
208 | nullptr,
209 | LOCTEXT("ChooseExecutableDialogTitle", "Choose a Mercurial executable").ToString(),
210 | *FPaths::GetPath(MercurialPath), TEXT(""), Filter, EFileDialogFlags::None, SelectedFiles
211 | );
212 |
213 | if (bFileSelected)
214 | {
215 | check(SelectedFiles.Num() == 1);
216 |
217 | MercurialPath = FPaths::ConvertRelativePathToFull(SelectedFiles[0]);
218 | if (FClient::IsValidExecutable(MercurialPath))
219 | {
220 | FProviderSettings& Settings = FModule::GetProvider().GetSettings();
221 | Settings.SetMercurialPath(MercurialPath);
222 | Settings.Save();
223 | // update the backing field for the SEditableTextBox
224 | MercurialPathText = FText::FromString(MercurialPath);
225 | }
226 | else
227 | {
228 | FMessageDialog::Open(
229 | EAppMsgType::Ok,
230 | LOCTEXT(
231 | "WrongExecutablePath",
232 | "The file you selected is not a Mercurial executable."
233 | )
234 | );
235 | }
236 | }
237 | }
238 | return FReply::Handled();
239 | }
240 |
241 | ECheckBoxState SProviderSettingsWidget::EnableLargefilesIntegration_IsChecked() const
242 | {
243 | return bEnableLargefilesIntegration ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
244 | }
245 |
246 | EVisibility SProviderSettingsWidget::GetLargeAssetTypeTreeVisibility() const
247 | {
248 | return bEnableLargefilesIntegration ? EVisibility::Visible : EVisibility::Collapsed;
249 | }
250 |
251 | void SProviderSettingsWidget::EnableLargefilesIntegration_OnCheckStateChanged(
252 | ECheckBoxState NewState
253 | )
254 | {
255 | bEnableLargefilesIntegration = (NewState == ECheckBoxState::Checked);
256 |
257 | if (LargefilesSettingsBox.IsValid())
258 | {
259 | LargefilesSettingsBox->SetVisibility(GetLargeAssetTypeTreeVisibility());
260 | }
261 |
262 | FProviderSettings& Settings = FModule::GetProvider().GetSettings();
263 | Settings.EnableLargefilesIntegration(bEnableLargefilesIntegration);
264 | Settings.Save();
265 | }
266 |
267 | void SProviderSettingsWidget::LargeAssetTypeTree_OnItemCheckStateChanged()
268 | {
269 | TArray LargeAssetTypes;
270 | LargeAssetTypeTreeWidget->GetSelectedAssetTypeClassNames(LargeAssetTypes);
271 |
272 | FProviderSettings& Settings = FModule::GetProvider().GetSettings();
273 | Settings.SetLargeAssetTypes(LargeAssetTypes);
274 | Settings.Save();
275 | }
276 |
277 | #undef LOCTEXT_NAMESPACE
278 |
279 | } // namespace MercurialSourceControl
280 |
--------------------------------------------------------------------------------
/Source/MercurialSourceControl/Private/SMercurialSourceControlSettingsWidget.h:
--------------------------------------------------------------------------------
1 | //-------------------------------------------------------------------------------
2 | // The MIT License (MIT)
3 | //
4 | // Copyright (c) 2014 Vadim Macagon
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //-------------------------------------------------------------------------------
24 | #pragma once
25 |
26 | namespace MercurialSourceControl {
27 |
28 | /** Slate widget that displays settings for the Mercurial source control provider. */
29 | class SProviderSettingsWidget : public SCompoundWidget
30 | {
31 | public:
32 | SLATE_BEGIN_ARGS(SProviderSettingsWidget) {}
33 |
34 | SLATE_END_ARGS()
35 |
36 | public:
37 | void Construct(const FArguments& InArgs);
38 |
39 | private:
40 | FText GetMercurialPathText() const;
41 | void MercurialPath_OnTextCommitted(const FText& InText, ETextCommit::Type InCommitType);
42 | FReply MercurialPathBrowse_OnClicked();
43 | EVisibility GetLargeAssetTypeTreeVisibility() const;
44 | ECheckBoxState EnableLargefilesIntegration_IsChecked() const;
45 | void EnableLargefilesIntegration_OnCheckStateChanged(ECheckBoxState NewState);
46 | void LargeAssetTypeTree_OnItemCheckStateChanged();
47 |
48 | private:
49 | FText MercurialPathText;
50 | bool bEnableLargefilesIntegration;
51 | TSharedPtr LargefilesSettingsBox;
52 | TSharedPtr LargeAssetTypeTreeWidget;
53 | };
54 |
55 | } // namespace MercurialSourceControl
56 |
--------------------------------------------------------------------------------