A game about forced loneliness, made by TACStudios
1using System.Collections.Generic;
2using System.IO;
3
4using UnityEditor;
5using UnityEngine;
6
7using Codice.Client.Common;
8using Codice.Client.Common.EventTracking;
9using Codice.CM.Common;
10using GluonGui;
11using PlasticGui;
12using PlasticGui.Gluon;
13using Unity.PlasticSCM.Editor.AssetsOverlays;
14using Unity.PlasticSCM.Editor.AssetsOverlays.Cache;
15using Unity.PlasticSCM.Editor.AssetUtils;
16using Unity.PlasticSCM.Editor.AssetUtils.Processor;
17using Unity.PlasticSCM.Editor.UI;
18using Unity.PlasticSCM.Editor.UI.Progress;
19using Unity.PlasticSCM.Editor.UI.Tree;
20
21namespace Unity.PlasticSCM.Editor.AssetMenu.Dialogs
22{
23 internal class CheckinDialog : PlasticDialog
24 {
25 protected override Rect DefaultRect
26 {
27 get
28 {
29 var baseRect = base.DefaultRect;
30 return new Rect(baseRect.x, baseRect.y, 700, 450);
31 }
32 }
33
34 protected override string GetTitle()
35 {
36 return PlasticLocalization.GetString(
37 PlasticLocalization.Name.CheckinChanges);
38 }
39
40 internal static bool CheckinPaths(
41 WorkspaceInfo wkInfo,
42 List<string> paths,
43 IAssetStatusCache assetStatusCache,
44 bool isGluonMode,
45 IWorkspaceWindow workspaceWindow,
46 ViewHost viewHost,
47 WorkspaceOperationsMonitor workspaceOperationsMonitor,
48 ISaveAssets saveAssets,
49 GuiMessage.IGuiMessage guiMessage,
50 IMergeViewLauncher mergeViewLauncher,
51 IGluonViewSwitcher gluonViewSwitcher)
52 {
53 MetaCache metaCache = new MetaCache();
54 metaCache.Build(paths);
55
56 CheckinDialog dialog = Create(
57 wkInfo,
58 paths,
59 assetStatusCache,
60 metaCache,
61 isGluonMode,
62 new ProgressControlsForDialogs(),
63 workspaceWindow,
64 viewHost,
65 workspaceOperationsMonitor,
66 saveAssets,
67 guiMessage,
68 mergeViewLauncher,
69 gluonViewSwitcher);
70
71 return dialog.RunModal(focusedWindow) == ResponseType.Ok;
72 }
73
74 protected override void OnModalGUI()
75 {
76 Title(PlasticLocalization.GetString(PlasticLocalization.Name.CheckinOnlyComment));
77
78 GUI.SetNextControlName(CHECKIN_TEXTAREA_NAME);
79
80 mComment = GUILayout.TextArea(
81 mComment,
82 EditorStyles.textArea,
83 GUILayout.MinHeight(120));
84
85 if (!mTextAreaFocused)
86 {
87 EditorGUI.FocusTextInControl(CHECKIN_TEXTAREA_NAME);
88 mTextAreaFocused = true;
89 }
90
91 Title(PlasticLocalization.GetString(PlasticLocalization.Name.Files));
92
93 DoFileList(
94 mWkInfo,
95 mPaths,
96 mAssetStatusCache,
97 mMetaCache);
98
99 DrawProgressForDialogs.For(
100 mProgressControls.ProgressData);
101
102 DoButtonsArea();
103
104 mProgressControls.ForcedUpdateProgress(this);
105 }
106
107 void DoFileList(
108 WorkspaceInfo wkInfo,
109 List<string> paths,
110 IAssetStatusCache assetStatusCache,
111 MetaCache metaCache)
112 {
113 mFileListScrollPosition = GUILayout.BeginScrollView(
114 mFileListScrollPosition,
115 EditorStyles.helpBox,
116 GUILayout.ExpandHeight(true));
117
118 foreach (string path in paths)
119 {
120 if (MetaPath.IsMetaPath(path))
121 continue;
122
123 Texture fileIcon = Directory.Exists(path) ?
124 Images.GetDirectoryIcon() :
125 Images.GetFileIcon(path);
126
127 string label = WorkspacePath.GetWorkspaceRelativePath(
128 wkInfo.ClientPath, path);
129
130 if (metaCache.HasMeta(path))
131 label = string.Concat(label, UnityConstants.TREEVIEW_META_LABEL);
132
133 AssetsOverlays.AssetStatus assetStatus =
134 assetStatusCache.GetStatus(path);
135
136 Rect selectionRect = EditorGUILayout.GetControlRect();
137
138 DoListViewItem(selectionRect, fileIcon, label, assetStatus);
139 }
140
141 GUILayout.EndScrollView();
142 }
143
144 void DoListViewItem(
145 Rect itemRect,
146 Texture fileIcon,
147 string label,
148 AssetsOverlays.AssetStatus statusToDraw)
149 {
150 Texture overlayIcon = DrawAssetOverlay.DrawOverlayIcon.
151 GetOverlayIcon(statusToDraw);
152
153 itemRect = DrawTreeViewItem.DrawIconLeft(
154 itemRect,
155 UnityConstants.TREEVIEW_ROW_HEIGHT,
156 fileIcon,
157 overlayIcon);
158
159 GUI.Label(itemRect, label);
160 }
161
162 void DoButtonsArea()
163 {
164 using (new EditorGUILayout.HorizontalScope())
165 {
166 GUILayout.FlexibleSpace();
167
168 if (Application.platform == RuntimePlatform.WindowsEditor)
169 {
170 DoCheckinButton();
171 DoCancelButton();
172 return;
173 }
174
175 DoCancelButton();
176 DoCheckinButton();
177 }
178 }
179
180 void DoCheckinButton()
181 {
182 GUI.enabled = IsCheckinButtonEnabled();
183
184 try
185 {
186 if (!AcceptButton(PlasticLocalization.GetString(
187 PlasticLocalization.Name.CheckinButton)))
188 return;
189 }
190 finally
191 {
192 if (!mSentCheckinTrackEvent)
193 {
194 TrackFeatureUseEvent.For(
195 PlasticGui.Plastic.API.GetRepositorySpec(mWkInfo),
196 TrackFeatureUseEvent.Features.ContextMenuCheckinDialogCheckin);
197
198 mSentCheckinTrackEvent = true;
199 }
200
201 GUI.enabled = true;
202 }
203
204 OkButtonWithCheckinAction();
205 }
206
207 void DoCancelButton()
208 {
209 if (!NormalButton(PlasticLocalization.GetString(
210 PlasticLocalization.Name.CancelButton)))
211 return;
212
213 if (!mSentCancelTrackEvent)
214 {
215 TrackFeatureUseEvent.For(
216 PlasticGui.Plastic.API.GetRepositorySpec(mWkInfo),
217 TrackFeatureUseEvent.Features.ContextMenuCheckinDialogCancel);
218
219 mSentCancelTrackEvent = true;
220 }
221
222 CancelButtonAction();
223 }
224
225 void OkButtonWithCheckinAction()
226 {
227 if (!IsCheckinButtonEnabled())
228 return;
229
230 bool isCancelled;
231 mSaveAssets.ForPathsWithConfirmation(
232 mWkInfo.ClientPath, mPaths, mWorkspaceOperationsMonitor,
233 out isCancelled);
234
235 if (isCancelled)
236 return;
237
238 mIsRunningCheckin = true;
239
240 mPaths.AddRange(mMetaCache.GetExistingMeta(mPaths));
241
242 if (mIsGluonMode)
243 {
244 CheckinDialogOperations.CheckinPathsPartial(
245 mWkInfo,
246 mPaths,
247 mComment,
248 mViewHost,
249 this,
250 mGuiMessage,
251 mProgressControls,
252 mGluonViewSwitcher);
253 return;
254 }
255
256 CheckinDialogOperations.CheckinPaths(
257 mWkInfo,
258 mPaths,
259 mComment,
260 mWorkspaceWindow,
261 this,
262 mGuiMessage,
263 mProgressControls,
264 mMergeViewLauncher);
265 }
266
267 bool IsCheckinButtonEnabled()
268 {
269 return !string.IsNullOrEmpty(mComment) && !mIsRunningCheckin;
270 }
271
272 static CheckinDialog Create(
273 WorkspaceInfo wkInfo,
274 List<string> paths,
275 IAssetStatusCache assetStatusCache,
276 MetaCache metaCache,
277 bool isGluonMode,
278 ProgressControlsForDialogs progressControls,
279 IWorkspaceWindow workspaceWindow,
280 ViewHost viewHost,
281 WorkspaceOperationsMonitor workspaceOperationsMonitor,
282 ISaveAssets saveAssets,
283 GuiMessage.IGuiMessage guiMessage,
284 IMergeViewLauncher mergeViewLauncher,
285 IGluonViewSwitcher gluonViewSwitcher)
286 {
287 var instance = CreateInstance<CheckinDialog>();
288 instance.IsResizable = true;
289 instance.minSize = new Vector2(520, 370);
290 instance.mWkInfo = wkInfo;
291 instance.mPaths = paths;
292 instance.mAssetStatusCache = assetStatusCache;
293 instance.mMetaCache = metaCache;
294 instance.mIsGluonMode = isGluonMode;
295 instance.mProgressControls = progressControls;
296 instance.mWorkspaceWindow = workspaceWindow;
297 instance.mViewHost = viewHost;
298 instance.mWorkspaceOperationsMonitor = workspaceOperationsMonitor;
299 instance.mSaveAssets = saveAssets;
300 instance.mGuiMessage = guiMessage;
301 instance.mMergeViewLauncher = mergeViewLauncher;
302 instance.mGluonViewSwitcher = gluonViewSwitcher;
303 instance.mEnterKeyAction = instance.OkButtonWithCheckinAction;
304 instance.AddControlConsumingEnterKey(CHECKIN_TEXTAREA_NAME);
305 instance.mEscapeKeyAction = instance.CancelButtonAction;
306 return instance;
307 }
308
309 WorkspaceInfo mWkInfo;
310 List<string> mPaths;
311 IAssetStatusCache mAssetStatusCache;
312 MetaCache mMetaCache;
313 bool mIsGluonMode;
314 bool mTextAreaFocused;
315 string mComment;
316
317 bool mIsRunningCheckin;
318 Vector2 mFileListScrollPosition;
319
320 // IMGUI evaluates every frame, need to make sure feature tracks get sent only once
321 bool mSentCheckinTrackEvent = false;
322 bool mSentCancelTrackEvent = false;
323
324 ProgressControlsForDialogs mProgressControls;
325
326 IWorkspaceWindow mWorkspaceWindow;
327 WorkspaceOperationsMonitor mWorkspaceOperationsMonitor;
328 ISaveAssets mSaveAssets;
329 ViewHost mViewHost;
330 IMergeViewLauncher mMergeViewLauncher;
331 IGluonViewSwitcher mGluonViewSwitcher;
332 GuiMessage.IGuiMessage mGuiMessage;
333
334 const string CHECKIN_TEXTAREA_NAME = "checkin_textarea";
335
336 class MetaCache
337 {
338 internal bool HasMeta(string path)
339 {
340 return mCache.Contains(MetaPath.GetMetaPath(path));
341 }
342
343 internal List<string> GetExistingMeta(List<string> paths)
344 {
345 List<string> result = new List<string>();
346
347 foreach (string path in paths)
348 {
349 string metaPath = MetaPath.GetMetaPath(path);
350
351 if (!mCache.Contains(metaPath))
352 continue;
353
354 result.Add(metaPath);
355 }
356
357 return result;
358 }
359
360 internal void Build(List<string> paths)
361 {
362 HashSet<string> indexedKeys = BuildIndexedKeys(paths);
363
364 for (int i = paths.Count - 1; i >= 0; i--)
365 {
366 string currentPath = paths[i];
367
368 if (!MetaPath.IsMetaPath(currentPath))
369 continue;
370
371 string realPath = MetaPath.GetPathFromMetaPath(currentPath);
372
373 if (!indexedKeys.Contains(realPath))
374 continue;
375
376 // found foo.c and foo.c.meta
377 // with the same chage types - move .meta to cache
378 mCache.Add(currentPath);
379 paths.RemoveAt(i);
380 }
381 }
382
383 static HashSet<string> BuildIndexedKeys(List<string> paths)
384 {
385 HashSet<string> result = new HashSet<string>();
386
387 foreach (string path in paths)
388 {
389 if (MetaPath.IsMetaPath(path))
390 continue;
391
392 result.Add(path);
393 }
394
395 return result;
396 }
397
398 HashSet<string> mCache = new HashSet<string>();
399 }
400 }
401}