Use forward slashes in the includes for consistency and also remove double slashes
[TortoiseGit.git] / src / TortoiseShell / IconOverlay.cpp
blobdb1d029a784e8bbacbfab373ffff431448cc8eb5
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2009-2013, 2015-2017 - TortoiseGit
4 // Copyright (C) 2003-2008, 2017 - TortoiseSVN
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software Foundation,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "stdafx.h"
21 #include "ShellExt.h"
22 #include "Guids.h"
23 #include "PreserveChdir.h"
24 #include "UnicodeUtils.h"
25 #include "GitStatus.h"
26 #include "../TGitCache/CacheInterface.h"
27 #include "GitAdminDir.h"
28 #include "StringUtils.h"
30 // "The Shell calls IShellIconOverlayIdentifier::GetOverlayInfo to request the
31 // location of the handler's icon overlay. The icon overlay handler returns
32 // the name of the file containing the overlay image, and its index within
33 // that file. The Shell then adds the icon overlay to the system image list."
35 STDMETHODIMP CShellExt::GetOverlayInfo(LPWSTR pwszIconFile, int cchMax, int* pIndex, DWORD* pdwFlags)
37 __try
39 return GetOverlayInfo_Wrap(pwszIconFile, cchMax, pIndex, pdwFlags);
41 __except(CCrashReport::Instance().SendReport(GetExceptionInformation()))
44 return E_FAIL;
47 STDMETHODIMP CShellExt::GetOverlayInfo_Wrap(LPWSTR pwszIconFile, int cchMax, int* pIndex, DWORD* pdwFlags)
49 if (!pwszIconFile)
50 return E_POINTER;
51 if (!pIndex)
52 return E_POINTER;
53 if (!pdwFlags)
54 return E_POINTER;
55 if(cchMax < 1)
56 return E_INVALIDARG;
58 // Set "out parameters" since we return S_OK later.
59 *pwszIconFile = 0;
60 *pIndex = 0;
61 *pdwFlags = 0;
63 // Now here's where we can find out if due to lack of enough overlay
64 // slots some of our overlays won't be shown.
65 // To do that we have to mark every overlay handler that's successfully
66 // loaded, so we can later check if some are missing
67 switch (m_State)
69 case FileStateVersioned : g_normalovlloaded = true; break;
70 case FileStateModified : g_modifiedovlloaded = true; break;
71 case FileStateConflict : g_conflictedovlloaded = true; break;
72 case FileStateDeleted : g_deletedovlloaded = true; break;
73 case FileStateReadOnly : g_readonlyovlloaded = true; break;
74 case FileStateLockedOverlay : g_lockedovlloaded = true; break;
75 case FileStateAddedOverlay : g_addedovlloaded = true; break;
76 case FileStateIgnoredOverlay : g_ignoredovlloaded = true; break;
77 case FileStateUnversionedOverlay : g_unversionedovlloaded = true; break;
80 // we don't have to set the icon file and/or the index here:
81 // the icons are handled by the TortoiseOverlays dll.
82 return S_OK;
85 STDMETHODIMP CShellExt::GetPriority(int *pPriority)
87 __try
89 return GetPriority_Wrap(pPriority);
91 __except(CCrashReport::Instance().SendReport(GetExceptionInformation()))
94 return E_FAIL;
97 STDMETHODIMP CShellExt::GetPriority_Wrap(int *pPriority)
99 if (!pPriority)
100 return E_POINTER;
102 switch (m_State)
104 case FileStateConflict:
105 *pPriority = 0;
106 break;
107 case FileStateModified:
108 *pPriority = 1;
109 break;
110 case FileStateDeleted:
111 *pPriority = 2;
112 break;
113 case FileStateReadOnly:
114 *pPriority = 3;
115 break;
116 case FileStateLockedOverlay:
117 *pPriority = 4;
118 break;
119 case FileStateAddedOverlay:
120 *pPriority = 5;
121 break;
122 case FileStateVersioned:
123 *pPriority = 6;
124 break;
125 default:
126 *pPriority = 100;
127 return S_FALSE;
129 return S_OK;
132 // "Before painting an object's icon, the Shell passes the object's name to
133 // each icon overlay handler's IShellIconOverlayIdentifier::IsMemberOf
134 // method. If a handler wants to have its icon overlay displayed,
135 // it returns S_OK. The Shell then calls the handler's
136 // IShellIconOverlayIdentifier::GetOverlayInfo method to determine which icon
137 // to display."
139 STDMETHODIMP CShellExt::IsMemberOf(LPCWSTR pwszPath, DWORD dwAttrib)
141 __try
143 return IsMemberOf_Wrap(pwszPath, dwAttrib);
145 __except(CCrashReport::Instance().SendReport(GetExceptionInformation()))
148 return E_FAIL;
151 STDMETHODIMP CShellExt::IsMemberOf_Wrap(LPCWSTR pwszPath, DWORD /*dwAttrib*/)
153 if (!pwszPath)
154 return E_INVALIDARG;
155 const TCHAR* pPath = pwszPath;
156 // the shell sometimes asks overlays for invalid paths, e.g. for network
157 // printers (in that case the path is "0", at least for me here).
158 if (wcslen(pPath) < 2)
159 return S_FALSE;
160 PreserveChdir preserveChdir;
161 git_wc_status_kind status = git_wc_status_none;
162 bool readonlyoverlay = false;
163 bool lockedoverlay = false;
165 // since the shell calls each and every overlay handler with the same filepath
166 // we use a small 'fast' cache of just one path here.
167 // To make sure that cache expires, clear it as soon as one handler is used.
169 AutoLocker lock(g_csGlobalCOMGuard);
170 if (wcscmp(pPath, g_filepath.c_str()) == 0)
172 status = g_filestatus;
173 readonlyoverlay = g_readonlyoverlay;
174 lockedoverlay = g_lockedoverlay;
176 else
178 if (!g_ShellCache.IsPathAllowed(pPath))
180 if ((m_State == FileStateVersioned) && g_ShellCache.ShowExcludedAsNormal() &&
181 (PathGetDriveNumber(pPath)>1) &&
182 PathIsDirectory(pPath) && g_ShellCache.HasGITAdminDir(pPath, true))
184 return S_OK;
186 return S_FALSE;
189 auto cacheType = g_ShellCache.GetCacheType();
190 if (g_ShellCache.IsOnlyNonElevated() && g_ShellCache.IsProcessElevated())
192 cacheType = ShellCache::none;
193 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": elevated overlays blocked\n");
195 switch (cacheType)
197 case ShellCache::exe:
199 CTGitPath tpath(pPath);
200 if(!tpath.HasAdminDir())
202 status = git_wc_status_none;
203 break;
205 if(tpath.IsAdminDir())
207 status = git_wc_status_none;
208 break;
210 TGITCacheResponse itemStatus;
211 SecureZeroMemory(&itemStatus, sizeof(itemStatus));
212 if (m_remoteCacheLink.GetStatusFromRemoteCache(tpath, &itemStatus, true))
214 if (itemStatus.m_bAssumeValid)
215 readonlyoverlay = true;
216 if (itemStatus.m_bSkipWorktree)
217 lockedoverlay = true;
218 status = (git_wc_status_kind)itemStatus.m_status;
221 break;
222 case ShellCache::dll:
223 case ShellCache::dllFull:
225 // Look in our caches for this item
226 const FileStatusCacheEntry * s = m_CachedStatus.GetCachedItem(CTGitPath(pPath));
227 if (s)
229 status = s->status;
230 if (s->assumeValid)
231 readonlyoverlay = true;
232 if (s->skipWorktree)
233 lockedoverlay = true;
235 else
237 // No cached status available
239 // since the dwAttrib param of the IsMemberOf() function does not
240 // have the SFGAO_FOLDER flag set at all (it's 0 for files and folders!)
241 // we have to check if the path is a folder ourselves :(
242 if (PathIsDirectory(pPath))
244 if (g_ShellCache.HasGITAdminDir(pPath, TRUE))
246 if ((!g_ShellCache.IsRecursive()) && (!g_ShellCache.IsFolderOverlay()))
247 status = git_wc_status_normal;
248 else
250 s = m_CachedStatus.GetFullStatus(CTGitPath(pPath), TRUE);
251 status = s->status;
254 else
255 status = git_wc_status_none;
257 else if (CStringUtils::EndsWith(pPath, GitAdminDir::GetAdminDirName()))
258 status = git_wc_status_none;
259 else
261 s = m_CachedStatus.GetFullStatus(CTGitPath(pPath), FALSE);
262 status = s->status;
263 if (s->assumeValid)
264 readonlyoverlay = true;
265 if (s->skipWorktree)
266 lockedoverlay = true;
271 break;
272 default:
273 case ShellCache::none:
275 // no cache means we only show a 'versioned' overlay on folders
276 // with an admin directory
277 if (PathIsDirectory(pPath))
279 if (g_ShellCache.HasGITAdminDir(pPath, TRUE))
280 status = git_wc_status_normal;
281 else
282 status = git_wc_status_none;
284 else
285 status = git_wc_status_none;
287 break;
289 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": Status %d for file %s\n", status, pwszPath);
291 g_filepath.clear();
292 g_filepath = pPath;
293 g_filestatus = status;
294 g_readonlyoverlay = readonlyoverlay;
295 g_lockedoverlay = lockedoverlay;
297 //the priority system of the shell doesn't seem to work as expected (or as I expected):
298 //as it seems that if one handler returns S_OK then that handler is used, no matter
299 //if other handlers would return S_OK too (they're never called on my machine!)
300 //So we return S_OK for ONLY ONE handler!
302 switch (status)
304 // note: we can show other overlays if due to lack of enough free overlay
305 // slots some of our overlays aren't loaded. But we assume that
306 // at least the 'normal' overlay is available.
307 // if the 'modified' overlay isn't available, we show the 'normal' overlay,
308 // but in this case the overlays don't really provide anything useful anymore.
309 case git_wc_status_none:
310 return S_FALSE;
311 case git_wc_status_unversioned:
312 if (g_ShellCache.ShowUnversionedOverlay() && g_unversionedovlloaded && (m_State == FileStateUnversionedOverlay))
314 g_filepath.clear();
315 return S_OK;
317 return S_FALSE;
318 case git_wc_status_ignored:
319 if (g_ShellCache.ShowIgnoredOverlay() && g_ignoredovlloaded && (m_State == FileStateIgnoredOverlay))
321 g_filepath.clear();
322 return S_OK;
324 return S_FALSE;
325 case git_wc_status_normal:
326 // skip-worktree aka locked has higher priority than assume-valid
327 if ((lockedoverlay)&&(g_lockedovlloaded))
329 if (m_State == FileStateLockedOverlay)
331 g_filepath.clear();
332 return S_OK;
334 else
335 return S_FALSE;
337 else if ((readonlyoverlay)&&(g_readonlyovlloaded))
339 if (m_State == FileStateReadOnly)
341 g_filepath.clear();
342 return S_OK;
344 else
345 return S_FALSE;
347 else if (m_State == FileStateVersioned)
349 g_filepath.clear();
350 return S_OK;
352 else
353 return S_FALSE;
354 case git_wc_status_deleted:
355 if (g_deletedovlloaded)
357 if (m_State == FileStateDeleted)
359 g_filepath.clear();
360 return S_OK;
362 else
363 return S_FALSE;
365 else
367 // the 'deleted' overlay isn't available (due to lack of enough
368 // overlay slots). So just show the 'modified' overlay instead.
369 if (m_State == FileStateModified)
371 g_filepath.clear();
372 return S_OK;
374 else
375 return S_FALSE;
377 case git_wc_status_modified:
378 if (g_modifiedovlloaded)
380 if (m_State == FileStateModified)
382 g_filepath.clear();
383 return S_OK;
385 else
386 return S_FALSE;
388 else
390 if (m_State == FileStateVersioned)
392 g_filepath.clear();
393 return S_OK;
395 else
396 return S_FALSE;
398 case git_wc_status_added:
399 if (g_addedovlloaded)
401 if (m_State== FileStateAddedOverlay)
403 g_filepath.clear();
404 return S_OK;
406 else
407 return S_FALSE;
409 else
411 // the 'added' overlay isn't available (due to lack of enough
412 // overlay slots). So just show the 'modified' overlay instead.
413 if (m_State == FileStateModified)
415 g_filepath.clear();
416 return S_OK;
418 else
419 return S_FALSE;
421 case git_wc_status_conflicted:
422 if (g_conflictedovlloaded)
424 if (m_State == FileStateConflict)
426 g_filepath.clear();
427 return S_OK;
429 else
430 return S_FALSE;
432 else
434 // the 'conflicted' overlay isn't available (due to lack of enough
435 // overlay slots). So just show the 'modified' overlay instead.
436 if (m_State == FileStateModified)
438 g_filepath.clear();
439 return S_OK;
441 else
442 return S_FALSE;
444 default:
445 return S_FALSE;
446 } // switch (status)
447 //return S_FALSE;