1
// TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2009-2013, 2015-2020 - 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.
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
)
46 // Set "out parameters" since we return S_OK later.
51 // Now here's where we can find out if due to lack of enough overlay
52 // slots some of our overlays won't be shown.
53 // To do that we have to mark every overlay handler that's successfully
54 // loaded, so we can later check if some are missing
57 case FileStateVersioned
: g_normalovlloaded
= true; break;
58 case FileStateModified
: g_modifiedovlloaded
= true; break;
59 case FileStateConflict
: g_conflictedovlloaded
= true; break;
60 case FileStateDeleted
: g_deletedovlloaded
= true; break;
61 case FileStateReadOnly
: g_readonlyovlloaded
= true; break;
62 case FileStateLockedOverlay
: g_lockedovlloaded
= true; break;
63 case FileStateAddedOverlay
: g_addedovlloaded
= true; break;
64 case FileStateIgnoredOverlay
: g_ignoredovlloaded
= true; break;
65 case FileStateUnversionedOverlay
: g_unversionedovlloaded
= true; break;
68 // we don't have to set the icon file and/or the index here:
69 // the icons are handled by the TortoiseOverlays dll.
73 STDMETHODIMP
CShellExt::GetPriority(int *pPriority
)
80 case FileStateConflict
:
83 case FileStateModified
:
86 case FileStateDeleted
:
89 case FileStateReadOnly
:
92 case FileStateLockedOverlay
:
95 case FileStateAddedOverlay
:
98 case FileStateVersioned
:
108 // "Before painting an object's icon, the Shell passes the object's name to
109 // each icon overlay handler's IShellIconOverlayIdentifier::IsMemberOf
110 // method. If a handler wants to have its icon overlay displayed,
111 // it returns S_OK. The Shell then calls the handler's
112 // IShellIconOverlayIdentifier::GetOverlayInfo method to determine which icon
115 STDMETHODIMP
CShellExt::IsMemberOf(LPCWSTR pwszPath
, DWORD
/*dwAttrib*/)
119 const wchar_t* pPath
= pwszPath
;
120 // the shell sometimes asks overlays for invalid paths, e.g. for network
121 // printers (in that case the path is "0", at least for me here).
122 if (wcslen(pPath
) < 2)
124 PreserveChdir preserveChdir
;
125 git_wc_status_kind status
= git_wc_status_none
;
126 bool readonlyoverlay
= false;
127 bool lockedoverlay
= false;
129 // since the shell calls each and every overlay handler with the same filepath
130 // we use a small 'fast' cache of just one path here.
131 // To make sure that cache expires, clear it as soon as one handler is used.
133 AutoLocker
lock(g_csGlobalCOMGuard
);
134 if (wcscmp(pPath
, g_filepath
.c_str()) == 0)
136 status
= g_filestatus
;
137 readonlyoverlay
= g_readonlyoverlay
;
138 lockedoverlay
= g_lockedoverlay
;
142 if (!g_ShellCache
.IsPathAllowed(pPath
))
144 if ((m_State
== FileStateVersioned
) && g_ShellCache
.ShowExcludedAsNormal() &&
145 (PathGetDriveNumber(pPath
)>1) &&
146 PathIsDirectory(pPath
) && g_ShellCache
.HasGITAdminDir(pPath
, true))
153 auto cacheType
= g_ShellCache
.GetCacheType();
154 if (g_ShellCache
.IsOnlyNonElevated() && g_ShellCache
.IsProcessElevated())
156 cacheType
= ShellCache::none
;
157 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__
) L
": elevated overlays blocked\n");
161 case ShellCache::exe
:
163 CTGitPath
tpath(pPath
);
164 if(!tpath
.HasAdminDir())
166 status
= git_wc_status_none
;
169 if(tpath
.IsAdminDir())
171 status
= git_wc_status_none
;
174 TGITCacheResponse itemStatus
= { 0 };
175 if (m_remoteCacheLink
.GetStatusFromRemoteCache(tpath
, &itemStatus
, true))
177 if (itemStatus
.m_bAssumeValid
)
178 readonlyoverlay
= true;
179 if (itemStatus
.m_bSkipWorktree
)
180 lockedoverlay
= true;
181 status
= static_cast<git_wc_status_kind
>(itemStatus
.m_status
);
185 case ShellCache::dll
:
186 case ShellCache::dllFull
:
188 // Look in our caches for this item
189 const FileStatusCacheEntry
* s
= m_CachedStatus
.GetCachedItem(CTGitPath(pPath
));
194 readonlyoverlay
= true;
196 lockedoverlay
= true;
200 // No cached status available
202 // since the dwAttrib param of the IsMemberOf() function does not
203 // have the SFGAO_FOLDER flag set at all (it's 0 for files and folders!)
204 // we have to check if the path is a folder ourselves :(
205 if (PathIsDirectory(pPath
))
207 if (g_ShellCache
.HasGITAdminDir(pPath
, TRUE
))
209 if ((!g_ShellCache
.IsRecursive()) && (!g_ShellCache
.IsFolderOverlay()))
210 status
= git_wc_status_normal
;
213 s
= m_CachedStatus
.GetFullStatus(CTGitPath(pPath
), TRUE
);
218 status
= git_wc_status_none
;
220 else if (CStringUtils::EndsWith(pPath
, GitAdminDir::GetAdminDirName()))
221 status
= git_wc_status_none
;
224 s
= m_CachedStatus
.GetFullStatus(CTGitPath(pPath
), FALSE
);
227 readonlyoverlay
= true;
229 lockedoverlay
= true;
236 case ShellCache::none
:
238 // no cache means we only show a 'versioned' overlay on folders
239 // with an admin directory
240 if (PathIsDirectory(pPath
))
242 if (g_ShellCache
.HasGITAdminDir(pPath
, TRUE
))
243 status
= git_wc_status_normal
;
245 status
= git_wc_status_none
;
248 status
= git_wc_status_none
;
252 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__
) L
": Status %d for file %s\n", status
, pwszPath
);
256 g_filestatus
= status
;
257 g_readonlyoverlay
= readonlyoverlay
;
258 g_lockedoverlay
= lockedoverlay
;
260 //the priority system of the shell doesn't seem to work as expected (or as I expected):
261 //as it seems that if one handler returns S_OK then that handler is used, no matter
262 //if other handlers would return S_OK too (they're never called on my machine!)
263 //So we return S_OK for ONLY ONE handler!
267 // note: we can show other overlays if due to lack of enough free overlay
268 // slots some of our overlays aren't loaded. But we assume that
269 // at least the 'normal' overlay is available.
270 // if the 'modified' overlay isn't available, we show the 'normal' overlay,
271 // but in this case the overlays don't really provide anything useful anymore.
272 case git_wc_status_none
:
274 case git_wc_status_unversioned
:
275 if (g_ShellCache
.ShowUnversionedOverlay() && g_unversionedovlloaded
&& (m_State
== FileStateUnversionedOverlay
))
281 case git_wc_status_ignored
:
282 if (g_ShellCache
.ShowIgnoredOverlay() && g_ignoredovlloaded
&& (m_State
== FileStateIgnoredOverlay
))
288 case git_wc_status_normal
:
289 // skip-worktree aka locked has higher priority than assume-valid
290 if ((lockedoverlay
)&&(g_lockedovlloaded
))
292 if (m_State
== FileStateLockedOverlay
)
300 else if ((readonlyoverlay
)&&(g_readonlyovlloaded
))
302 if (m_State
== FileStateReadOnly
)
310 else if (m_State
== FileStateVersioned
)
317 case git_wc_status_deleted
:
318 if (g_deletedovlloaded
)
320 if (m_State
== FileStateDeleted
)
330 // the 'deleted' overlay isn't available (due to lack of enough
331 // overlay slots). So just show the 'modified' overlay instead.
332 if (m_State
== FileStateModified
)
340 case git_wc_status_modified
:
341 if (g_modifiedovlloaded
)
343 if (m_State
== FileStateModified
)
353 if (m_State
== FileStateVersioned
)
361 case git_wc_status_added
:
362 if (g_addedovlloaded
)
364 if (m_State
== FileStateAddedOverlay
)
374 // the 'added' overlay isn't available (due to lack of enough
375 // overlay slots). So just show the 'modified' overlay instead.
376 if (m_State
== FileStateModified
)
384 case git_wc_status_conflicted
:
385 if (g_conflictedovlloaded
)
387 if (m_State
== FileStateConflict
)
397 // the 'conflicted' overlay isn't available (due to lack of enough
398 // overlay slots). So just show the 'modified' overlay instead.
399 if (m_State
== FileStateModified
)