Add context menu to delete all tags in Reference Browser
[TortoiseGit.git] / src / TortoiseShell / IconOverlay.cpp
blob3a1fa4d73c841393042e6954de8cbff4546917b1
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2009-2012 - 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.
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"
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)
35 __try
37 return GetOverlayInfo_Wrap(pwszIconFile, cchMax, pIndex, pdwFlags);
39 __except(CCrashReport::Instance().SendReport(GetExceptionInformation()))
42 return E_FAIL;
45 STDMETHODIMP CShellExt::GetOverlayInfo_Wrap(LPWSTR pwszIconFile, int cchMax, int* pIndex, DWORD* pdwFlags)
47 if(pwszIconFile == 0)
48 return E_POINTER;
49 if(pIndex == 0)
50 return E_POINTER;
51 if(pdwFlags == 0)
52 return E_POINTER;
53 if(cchMax < 1)
54 return E_INVALIDARG;
56 // Set "out parameters" since we return S_OK later.
57 *pwszIconFile = 0;
58 *pIndex = 0;
59 *pdwFlags = 0;
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
65 switch (m_State)
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.
80 return S_OK;
83 STDMETHODIMP CShellExt::GetPriority(int *pPriority)
85 __try
87 return GetPriority_Wrap(pPriority);
89 __except(CCrashReport::Instance().SendReport(GetExceptionInformation()))
92 return E_FAIL;
95 STDMETHODIMP CShellExt::GetPriority_Wrap(int *pPriority)
97 if (pPriority == 0)
98 return E_POINTER;
100 switch (m_State)
102 case FileStateConflict:
103 *pPriority = 0;
104 break;
105 case FileStateModified:
106 *pPriority = 1;
107 break;
108 case FileStateDeleted:
109 *pPriority = 2;
110 break;
111 case FileStateReadOnly:
112 *pPriority = 3;
113 break;
114 case FileStateLockedOverlay:
115 *pPriority = 4;
116 break;
117 case FileStateAddedOverlay:
118 *pPriority = 5;
119 break;
120 case FileStateVersioned:
121 *pPriority = 6;
122 break;
123 default:
124 *pPriority = 100;
125 return S_FALSE;
127 return S_OK;
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
135 // to display."
137 STDMETHODIMP CShellExt::IsMemberOf(LPCWSTR pwszPath, DWORD dwAttrib)
139 __try
141 return IsMemberOf_Wrap(pwszPath, dwAttrib);
143 __except(CCrashReport::Instance().SendReport(GetExceptionInformation()))
146 return E_FAIL;
149 STDMETHODIMP CShellExt::IsMemberOf_Wrap(LPCWSTR pwszPath, DWORD /*dwAttrib*/)
151 if (pwszPath == NULL)
152 return E_INVALIDARG;
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)
157 return S_FALSE;
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;
174 else
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))
182 return S_OK;
184 return S_FALSE;
187 switch (g_ShellCache.GetCacheType())
189 case ShellCache::exe:
191 CTGitPath tpath(pPath);
192 if(!tpath.HasAdminDir())
194 status = git_wc_status_none;
195 break;
197 if(tpath.IsAdminDir())
199 status = git_wc_status_none;
200 break;
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);
213 break;
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));
219 if (s)
221 status = s->status;
222 if (s->assumeValid)
223 readonlyoverlay = true;
224 if (s->skipWorktree)
225 lockedoverlay = true;
227 else
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;
242 else
244 const FileStatusCacheEntry * s = m_CachedStatus.GetFullStatus(CTGitPath(pPath), TRUE);
245 status = s->status;
248 else
250 status = git_wc_status_none;
253 else
255 const FileStatusCacheEntry * s = m_CachedStatus.GetFullStatus(CTGitPath(pPath), FALSE);
256 status = s->status;
257 if (s->assumeValid)
258 readonlyoverlay = true;
259 if (s->skipWorktree)
260 lockedoverlay = true;
265 break;
266 default:
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;
277 else
279 status = git_wc_status_none;
282 else
284 status = git_wc_status_none;
287 break;
289 ATLTRACE(_T("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' and 'modified' overlay are available.
307 case git_wc_status_none:
308 return S_FALSE;
309 case git_wc_status_unversioned:
310 if (g_ShellCache.ShowUnversionedOverlay() && g_unversionedovlloaded && (m_State == FileStateUnversionedOverlay))
312 g_filepath.clear();
313 return S_OK;
315 return S_FALSE;
316 case git_wc_status_ignored:
317 if (g_ShellCache.ShowIgnoredOverlay() && g_ignoredovlloaded && (m_State == FileStateIgnoredOverlay))
319 g_filepath.clear();
320 return S_OK;
322 return S_FALSE;
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)
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_missing:
355 case git_wc_status_deleted:
356 if (g_deletedovlloaded)
358 if (m_State == FileStateDeleted)
360 g_filepath.clear();
361 return S_OK;
363 else
364 return S_FALSE;
366 else
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)
372 g_filepath.clear();
373 return S_OK;
375 else
376 return S_FALSE;
378 case git_wc_status_replaced:
379 case git_wc_status_modified:
380 if (m_State == FileStateModified)
382 g_filepath.clear();
383 return S_OK;
385 else
386 return S_FALSE;
387 case git_wc_status_merged:
388 if (m_State == FileStateReadOnly)
390 g_filepath.clear();
391 return S_OK;
393 else
394 return S_FALSE;
395 case git_wc_status_added:
396 if (g_addedovlloaded)
398 if (m_State== FileStateAddedOverlay)
400 g_filepath.clear();
401 return S_OK;
403 else
404 return S_FALSE;
406 else
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)
412 g_filepath.clear();
413 return S_OK;
415 else
416 return S_FALSE;
418 case git_wc_status_conflicted:
419 case git_wc_status_obstructed:
420 if (g_conflictedovlloaded)
422 if (m_State == FileStateConflict)
424 g_filepath.clear();
425 return S_OK;
427 else
428 return S_FALSE;
430 else
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)
436 g_filepath.clear();
437 return S_OK;
439 else
440 return S_FALSE;
442 default:
443 return S_FALSE;
444 } // switch (status)
445 //return S_FALSE;