Fixed issue #1220: ext/CrashServer/CommonLibs/Zlib/Zlib.vcproj immediate dir Win32...
[TortoiseGit.git] / src / TortoiseShell / IconOverlay.cpp
blobca17afb16c6e514fe5620ca54a5e83df8500674e
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 PreserveChdir preserveChdir;
37 // Now here's where we can find out if due to lack of enough overlay
38 // slots some of our overlays won't be shown.
39 // To do that we have to mark every overlay handler that's successfully
40 // loaded, so we can later check if some are missing
41 switch (m_State)
43 case FileStateVersioned : g_normalovlloaded = true; break;
44 case FileStateModified : g_modifiedovlloaded = true; break;
45 case FileStateConflict : g_conflictedovlloaded = true; break;
46 case FileStateDeleted : g_deletedovlloaded = true; break;
47 case FileStateReadOnly : g_readonlyovlloaded = true; break;
48 case FileStateLockedOverlay : g_lockedovlloaded = true; break;
49 case FileStateAddedOverlay : g_addedovlloaded = true; break;
50 case FileStateIgnoredOverlay : g_ignoredovlloaded = true; break;
51 case FileStateUnversionedOverlay : g_unversionedovlloaded = true; break;
54 // we don't have to set the icon file and/or the index here:
55 // the icons are handled by the TortoiseOverlays dll.
56 return S_OK;
59 STDMETHODIMP CShellExt::GetPriority(int *pPriority)
61 switch (m_State)
63 case FileStateConflict:
64 *pPriority = 0;
65 break;
66 case FileStateModified:
67 *pPriority = 1;
68 break;
69 case FileStateDeleted:
70 *pPriority = 2;
71 break;
72 case FileStateReadOnly:
73 *pPriority = 3;
74 break;
75 case FileStateLockedOverlay:
76 *pPriority = 4;
77 break;
78 case FileStateAddedOverlay:
79 *pPriority = 5;
80 break;
81 case FileStateVersioned:
82 *pPriority = 6;
83 break;
84 default:
85 *pPriority = 100;
86 return S_FALSE;
88 return S_OK;
91 // "Before painting an object's icon, the Shell passes the object's name to
92 // each icon overlay handler's IShellIconOverlayIdentifier::IsMemberOf
93 // method. If a handler wants to have its icon overlay displayed,
94 // it returns S_OK. The Shell then calls the handler's
95 // IShellIconOverlayIdentifier::GetOverlayInfo method to determine which icon
96 // to display."
98 STDMETHODIMP CShellExt::IsMemberOf(LPCWSTR pwszPath, DWORD /*dwAttrib*/)
100 PreserveChdir preserveChdir;
101 git_wc_status_kind status = git_wc_status_none;
102 bool readonlyoverlay = false;
103 bool lockedoverlay = false;
104 if (pwszPath == NULL)
105 return S_FALSE;
106 const TCHAR* pPath = pwszPath;
108 // the shell sometimes asks overlays for invalid paths, e.g. for network
109 // printers (in that case the path is "0", at least for me here).
110 if (_tcslen(pPath)<2)
111 return S_FALSE;
112 // since the shell calls each and every overlay handler with the same filepath
113 // we use a small 'fast' cache of just one path here.
114 // To make sure that cache expires, clear it as soon as one handler is used.
116 AutoLocker lock(g_csGlobalCOMGuard);
117 if (_tcscmp(pPath, g_filepath.c_str())==0)
119 status = g_filestatus;
120 readonlyoverlay = g_readonlyoverlay;
121 lockedoverlay = g_lockedoverlay;
123 else
125 if (!g_ShellCache.IsPathAllowed(pPath))
127 int drivenumber = -1;
128 if ((m_State == FileStateVersioned) && g_ShellCache.ShowExcludedAsNormal() &&
129 ((drivenumber=PathGetDriveNumber(pPath))!=0)&&(drivenumber!=1) &&
130 PathIsDirectory(pPath) && g_ShellCache.HasGITAdminDir(pPath, true))
132 return S_OK;
134 return S_FALSE;
137 switch (g_ShellCache.GetCacheType())
139 case ShellCache::exe:
141 CTGitPath tpath(pPath);
142 if(!tpath.HasAdminDir())
144 status = git_wc_status_none;
145 break;
147 if(tpath.IsAdminDir())
149 status = git_wc_status_none;
150 break;
152 TGITCacheResponse itemStatus;
153 SecureZeroMemory(&itemStatus, sizeof(itemStatus));
154 if (m_remoteCacheLink.GetStatusFromRemoteCache(tpath, &itemStatus, true))
156 status = GitStatus::GetMoreImportant(itemStatus.m_status.text_status, itemStatus.m_status.prop_status);
159 break;
160 case ShellCache::dll:
161 case ShellCache::dllFull:
163 // Look in our caches for this item
164 const FileStatusCacheEntry * s = m_CachedStatus.GetCachedItem(CTGitPath(pPath));
165 if (s)
167 status = s->status;
169 else
171 // No cached status available
173 // since the dwAttrib param of the IsMemberOf() function does not
174 // have the SFGAO_FOLDER flag set at all (it's 0 for files and folders!)
175 // we have to check if the path is a folder ourselves :(
176 if (PathIsDirectory(pPath))
178 if (g_ShellCache.HasGITAdminDir(pPath, TRUE))
180 if ((!g_ShellCache.IsRecursive()) && (!g_ShellCache.IsFolderOverlay()))
182 status = git_wc_status_normal;
184 else
186 const FileStatusCacheEntry * s = m_CachedStatus.GetFullStatus(CTGitPath(pPath), TRUE);
187 status = s->status;
190 else
192 status = git_wc_status_none;
195 else
197 const FileStatusCacheEntry * s = m_CachedStatus.GetFullStatus(CTGitPath(pPath), FALSE);
198 status = s->status;
203 break;
204 default:
205 case ShellCache::none:
207 // no cache means we only show a 'versioned' overlay on folders
208 // with an admin directory
209 if (PathIsDirectory(pPath))
211 if (g_ShellCache.HasGITAdminDir(pPath, TRUE))
213 status = git_wc_status_normal;
215 else
217 status = git_wc_status_none;
220 else
222 status = git_wc_status_none;
225 break;
227 ATLTRACE(_T("Status %d for file %s\n"), status, pwszPath);
229 g_filepath.clear();
230 g_filepath = pPath;
231 g_filestatus = status;
232 g_readonlyoverlay = readonlyoverlay;
233 g_lockedoverlay = lockedoverlay;
235 //the priority system of the shell doesn't seem to work as expected (or as I expected):
236 //as it seems that if one handler returns S_OK then that handler is used, no matter
237 //if other handlers would return S_OK too (they're never called on my machine!)
238 //So we return S_OK for ONLY ONE handler!
240 switch (status)
242 // note: we can show other overlays if due to lack of enough free overlay
243 // slots some of our overlays aren't loaded. But we assume that
244 // at least the 'normal' and 'modified' overlay are available.
245 case git_wc_status_none:
246 return S_FALSE;
247 case git_wc_status_unversioned:
248 if (g_ShellCache.ShowUnversionedOverlay() && g_unversionedovlloaded && (m_State == FileStateUnversionedOverlay))
250 g_filepath.clear();
251 return S_OK;
253 return S_FALSE;
254 case git_wc_status_ignored:
255 if (g_ShellCache.ShowIgnoredOverlay() && g_ignoredovlloaded && (m_State == FileStateIgnoredOverlay))
257 g_filepath.clear();
258 return S_OK;
260 return S_FALSE;
261 case git_wc_status_normal:
262 case git_wc_status_external:
263 case git_wc_status_incomplete:
264 if ((readonlyoverlay)&&(g_readonlyovlloaded))
266 if (m_State == FileStateReadOnly)
268 g_filepath.clear();
269 return S_OK;
271 else
272 return S_FALSE;
274 else if ((lockedoverlay)&&(g_lockedovlloaded))
276 if (m_State == FileStateLockedOverlay)
278 g_filepath.clear();
279 return S_OK;
281 else
282 return S_FALSE;
284 else if (m_State == FileStateVersioned)
286 g_filepath.clear();
287 return S_OK;
289 else
290 return S_FALSE;
291 case git_wc_status_missing:
292 case git_wc_status_deleted:
293 if (g_deletedovlloaded)
295 if (m_State == FileStateDeleted)
297 g_filepath.clear();
298 return S_OK;
300 else
301 return S_FALSE;
303 else
305 // the 'deleted' overlay isn't available (due to lack of enough
306 // overlay slots). So just show the 'modified' overlay instead.
307 if (m_State == FileStateModified)
309 g_filepath.clear();
310 return S_OK;
312 else
313 return S_FALSE;
315 case git_wc_status_replaced:
316 case git_wc_status_modified:
317 if (m_State == FileStateModified)
319 g_filepath.clear();
320 return S_OK;
322 else
323 return S_FALSE;
324 case git_wc_status_merged:
325 if (m_State == FileStateReadOnly)
327 g_filepath.clear();
328 return S_OK;
330 else
331 return S_FALSE;
332 case git_wc_status_added:
333 if (g_addedovlloaded)
335 if (m_State== FileStateAddedOverlay)
337 g_filepath.clear();
338 return S_OK;
340 else
341 return S_FALSE;
343 else
345 // the 'added' overlay isn't available (due to lack of enough
346 // overlay slots). So just show the 'modified' overlay instead.
347 if (m_State == FileStateModified)
349 g_filepath.clear();
350 return S_OK;
352 else
353 return S_FALSE;
355 case git_wc_status_conflicted:
356 case git_wc_status_obstructed:
357 if (g_conflictedovlloaded)
359 if (m_State == FileStateConflict)
361 g_filepath.clear();
362 return S_OK;
364 else
365 return S_FALSE;
367 else
369 // the 'conflicted' overlay isn't available (due to lack of enough
370 // overlay slots). So just show the 'modified' overlay instead.
371 if (m_State == FileStateModified)
373 g_filepath.clear();
374 return S_OK;
376 else
377 return S_FALSE;
379 default:
380 return S_FALSE;
381 } // switch (status)
382 //return S_FALSE;