1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2009-2013 - TortoiseGit
4 // Copyright (C) 2003-2008 - 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"
28 // "The Shell calls IShellIconOverlayIdentifier::GetOverlayInfo to request the
29 // location of the handler's icon overlay. The icon overlay handler returns
30 // the name of the file containing the overlay image, and its index within
31 // that file. The Shell then adds the icon overlay to the system image list."
33 STDMETHODIMP
CShellExt::GetOverlayInfo(LPWSTR pwszIconFile
, int cchMax
, int* pIndex
, DWORD
* pdwFlags
)
37 return GetOverlayInfo_Wrap(pwszIconFile
, cchMax
, pIndex
, pdwFlags
);
39 __except(CCrashReport::Instance().SendReport(GetExceptionInformation()))
45 STDMETHODIMP
CShellExt::GetOverlayInfo_Wrap(LPWSTR pwszIconFile
, int cchMax
, int* pIndex
, DWORD
* pdwFlags
)
56 // Set "out parameters" since we return S_OK later.
61 // Now here's where we can find out if due to lack of enough overlay
62 // slots some of our overlays won't be shown.
63 // To do that we have to mark every overlay handler that's successfully
64 // loaded, so we can later check if some are missing
67 case FileStateVersioned
: g_normalovlloaded
= true; break;
68 case FileStateModified
: g_modifiedovlloaded
= true; break;
69 case FileStateConflict
: g_conflictedovlloaded
= true; break;
70 case FileStateDeleted
: g_deletedovlloaded
= true; break;
71 case FileStateReadOnly
: g_readonlyovlloaded
= true; break;
72 case FileStateLockedOverlay
: g_lockedovlloaded
= true; break;
73 case FileStateAddedOverlay
: g_addedovlloaded
= true; break;
74 case FileStateIgnoredOverlay
: g_ignoredovlloaded
= true; break;
75 case FileStateUnversionedOverlay
: g_unversionedovlloaded
= true; break;
78 // we don't have to set the icon file and/or the index here:
79 // the icons are handled by the TortoiseOverlays dll.
83 STDMETHODIMP
CShellExt::GetPriority(int *pPriority
)
87 return GetPriority_Wrap(pPriority
);
89 __except(CCrashReport::Instance().SendReport(GetExceptionInformation()))
95 STDMETHODIMP
CShellExt::GetPriority_Wrap(int *pPriority
)
102 case FileStateConflict
:
105 case FileStateModified
:
108 case FileStateDeleted
:
111 case FileStateReadOnly
:
114 case FileStateLockedOverlay
:
117 case FileStateAddedOverlay
:
120 case FileStateVersioned
:
130 // "Before painting an object's icon, the Shell passes the object's name to
131 // each icon overlay handler's IShellIconOverlayIdentifier::IsMemberOf
132 // method. If a handler wants to have its icon overlay displayed,
133 // it returns S_OK. The Shell then calls the handler's
134 // IShellIconOverlayIdentifier::GetOverlayInfo method to determine which icon
137 STDMETHODIMP
CShellExt::IsMemberOf(LPCWSTR pwszPath
, DWORD dwAttrib
)
141 return IsMemberOf_Wrap(pwszPath
, dwAttrib
);
143 __except(CCrashReport::Instance().SendReport(GetExceptionInformation()))
149 STDMETHODIMP
CShellExt::IsMemberOf_Wrap(LPCWSTR pwszPath
, DWORD
/*dwAttrib*/)
151 if (pwszPath
== NULL
)
153 const TCHAR
* pPath
= pwszPath
;
154 // the shell sometimes asks overlays for invalid paths, e.g. for network
155 // printers (in that case the path is "0", at least for me here).
156 if (_tcslen(pPath
)<2)
158 PreserveChdir preserveChdir
;
159 git_wc_status_kind status
= git_wc_status_none
;
160 bool readonlyoverlay
= false;
161 bool lockedoverlay
= false;
163 // since the shell calls each and every overlay handler with the same filepath
164 // we use a small 'fast' cache of just one path here.
165 // To make sure that cache expires, clear it as soon as one handler is used.
167 AutoLocker
lock(g_csGlobalCOMGuard
);
168 if (_tcscmp(pPath
, g_filepath
.c_str())==0)
170 status
= g_filestatus
;
171 readonlyoverlay
= g_readonlyoverlay
;
172 lockedoverlay
= g_lockedoverlay
;
176 if (!g_ShellCache
.IsPathAllowed(pPath
))
178 if ((m_State
== FileStateVersioned
) && g_ShellCache
.ShowExcludedAsNormal() &&
179 (PathGetDriveNumber(pPath
)>1) &&
180 PathIsDirectory(pPath
) && g_ShellCache
.HasGITAdminDir(pPath
, true))
187 switch (g_ShellCache
.GetCacheType())
189 case ShellCache::exe
:
191 CTGitPath
tpath(pPath
);
192 if(!tpath
.HasAdminDir())
194 status
= git_wc_status_none
;
197 if(tpath
.IsAdminDir())
199 status
= git_wc_status_none
;
202 TGITCacheResponse itemStatus
;
203 SecureZeroMemory(&itemStatus
, sizeof(itemStatus
));
204 if (m_remoteCacheLink
.GetStatusFromRemoteCache(tpath
, &itemStatus
, true))
206 if (itemStatus
.m_bAssumeValid
)
207 readonlyoverlay
= true;
208 if (itemStatus
.m_bSkipWorktree
)
209 lockedoverlay
= true;
210 status
= GitStatus::GetMoreImportant(itemStatus
.m_status
.text_status
, itemStatus
.m_status
.prop_status
);
214 case ShellCache::dll
:
215 case ShellCache::dllFull
:
217 // Look in our caches for this item
218 const FileStatusCacheEntry
* s
= m_CachedStatus
.GetCachedItem(CTGitPath(pPath
));
223 readonlyoverlay
= true;
225 lockedoverlay
= true;
229 // No cached status available
231 // since the dwAttrib param of the IsMemberOf() function does not
232 // have the SFGAO_FOLDER flag set at all (it's 0 for files and folders!)
233 // we have to check if the path is a folder ourselves :(
234 if (PathIsDirectory(pPath
))
236 if (g_ShellCache
.HasGITAdminDir(pPath
, TRUE
))
238 if ((!g_ShellCache
.IsRecursive()) && (!g_ShellCache
.IsFolderOverlay()))
240 status
= git_wc_status_normal
;
244 const FileStatusCacheEntry
* s
= m_CachedStatus
.GetFullStatus(CTGitPath(pPath
), TRUE
);
250 status
= git_wc_status_none
;
255 const FileStatusCacheEntry
* s
= m_CachedStatus
.GetFullStatus(CTGitPath(pPath
), FALSE
);
258 readonlyoverlay
= true;
260 lockedoverlay
= true;
267 case ShellCache::none
:
269 // no cache means we only show a 'versioned' overlay on folders
270 // with an admin directory
271 if (PathIsDirectory(pPath
))
273 if (g_ShellCache
.HasGITAdminDir(pPath
, TRUE
))
275 status
= git_wc_status_normal
;
279 status
= git_wc_status_none
;
284 status
= git_wc_status_none
;
289 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__
) _T(": Status %d for file %s\n"), status
, pwszPath
);
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!
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' and 'modified' overlay are available.
307 case git_wc_status_none
:
309 case git_wc_status_unversioned
:
310 if (g_ShellCache
.ShowUnversionedOverlay() && g_unversionedovlloaded
&& (m_State
== FileStateUnversionedOverlay
))
316 case git_wc_status_ignored
:
317 if (g_ShellCache
.ShowIgnoredOverlay() && g_ignoredovlloaded
&& (m_State
== FileStateIgnoredOverlay
))
323 case git_wc_status_normal
:
324 case git_wc_status_external
:
325 case git_wc_status_incomplete
:
326 // skip-worktree aka locked has higher priority than assume-valid
327 if ((lockedoverlay
)&&(g_lockedovlloaded
))
329 if (m_State
== FileStateLockedOverlay
)
337 else if ((readonlyoverlay
)&&(g_readonlyovlloaded
))
339 if (m_State
== FileStateReadOnly
)
347 else if (m_State
== FileStateVersioned
)
354 case git_wc_status_missing
:
355 case git_wc_status_deleted
:
356 if (g_deletedovlloaded
)
358 if (m_State
== FileStateDeleted
)
368 // the 'deleted' overlay isn't available (due to lack of enough
369 // overlay slots). So just show the 'modified' overlay instead.
370 if (m_State
== FileStateModified
)
378 case git_wc_status_replaced
:
379 case git_wc_status_modified
:
380 if (m_State
== FileStateModified
)
387 case git_wc_status_merged
:
388 if (m_State
== FileStateReadOnly
)
395 case git_wc_status_added
:
396 if (g_addedovlloaded
)
398 if (m_State
== FileStateAddedOverlay
)
408 // the 'added' overlay isn't available (due to lack of enough
409 // overlay slots). So just show the 'modified' overlay instead.
410 if (m_State
== FileStateModified
)
418 case git_wc_status_conflicted
:
419 case git_wc_status_obstructed
:
420 if (g_conflictedovlloaded
)
422 if (m_State
== FileStateConflict
)
432 // the 'conflicted' overlay isn't available (due to lack of enough
433 // overlay slots). So just show the 'modified' overlay instead.
434 if (m_State
== FileStateModified
)