1 // TortoiseSVN - a Windows shell extension for easy version control
\r
3 // Copyright (C) 2003-2008 - TortoiseSVN
\r
5 // This program is free software; you can redistribute it and/or
\r
6 // modify it under the terms of the GNU General Public License
\r
7 // as published by the Free Software Foundation; either version 2
\r
8 // of the License, or (at your option) any later version.
\r
10 // This program is distributed in the hope that it will be useful,
\r
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
13 // GNU General Public License for more details.
\r
15 // You should have received a copy of the GNU General Public License
\r
16 // along with this program; if not, write to the Free Software Foundation,
\r
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\r
20 #include "ShellExt.h"
\r
22 #include "PreserveChdir.h"
\r
23 #include "UnicodeUtils.h"
\r
24 #include "GitStatus.h"
\r
25 #include "..\TGitCache\CacheInterface.h"
\r
27 // "The Shell calls IShellIconOverlayIdentifier::GetOverlayInfo to request the
\r
28 // location of the handler's icon overlay. The icon overlay handler returns
\r
29 // the name of the file containing the overlay image, and its index within
\r
30 // that file. The Shell then adds the icon overlay to the system image list."
\r
32 STDMETHODIMP CShellExt::GetOverlayInfo(LPWSTR /*pwszIconFile*/, int /*cchMax*/, int * /*pIndex*/, DWORD * /*pdwFlags*/)
\r
34 PreserveChdir preserveChdir;
\r
36 // Now here's where we can find out if due to lack of enough overlay
\r
37 // slots some of our overlays won't be shown.
\r
38 // To do that we have to mark every overlay handler that's successfully
\r
39 // loaded, so we can later check if some are missing
\r
42 case FileStateVersioned : g_normalovlloaded = true; break;
\r
43 case FileStateModified : g_modifiedovlloaded = true; break;
\r
44 case FileStateConflict : g_conflictedovlloaded = true; break;
\r
45 case FileStateDeleted : g_deletedovlloaded = true; break;
\r
46 case FileStateReadOnly : g_readonlyovlloaded = true; break;
\r
47 case FileStateLockedOverlay : g_lockedovlloaded = true; break;
\r
48 case FileStateAddedOverlay : g_addedovlloaded = true; break;
\r
49 case FileStateIgnoredOverlay : g_ignoredovlloaded = true; break;
\r
50 case FileStateUnversionedOverlay : g_unversionedovlloaded = true; break;
\r
53 // we don't have to set the icon file and/or the index here:
\r
54 // the icons are handled by the TortoiseOverlays dll.
\r
58 STDMETHODIMP CShellExt::GetPriority(int *pPriority)
\r
62 case FileStateConflict:
\r
65 case FileStateModified:
\r
68 case FileStateDeleted:
\r
71 case FileStateReadOnly:
\r
74 case FileStateLockedOverlay:
\r
77 case FileStateAddedOverlay:
\r
80 case FileStateVersioned:
\r
90 // "Before painting an object's icon, the Shell passes the object's name to
\r
91 // each icon overlay handler's IShellIconOverlayIdentifier::IsMemberOf
\r
92 // method. If a handler wants to have its icon overlay displayed,
\r
93 // it returns S_OK. The Shell then calls the handler's
\r
94 // IShellIconOverlayIdentifier::GetOverlayInfo method to determine which icon
\r
97 STDMETHODIMP CShellExt::IsMemberOf(LPCWSTR pwszPath, DWORD /*dwAttrib*/)
\r
99 PreserveChdir preserveChdir;
\r
100 git_wc_status_kind status = git_wc_status_none;
\r
101 bool readonlyoverlay = false;
\r
102 bool lockedoverlay = false;
\r
103 if (pwszPath == NULL)
\r
105 const TCHAR* pPath = pwszPath;
\r
107 // the shell sometimes asks overlays for invalid paths, e.g. for network
\r
108 // printers (in that case the path is "0", at least for me here).
\r
109 if (_tcslen(pPath)<2)
\r
111 // since the shell calls each and every overlay handler with the same filepath
\r
112 // we use a small 'fast' cache of just one path here.
\r
113 // To make sure that cache expires, clear it as soon as one handler is used.
\r
115 AutoLocker lock(g_csGlobalCOMGuard);
\r
116 if (_tcscmp(pPath, g_filepath.c_str())==0)
\r
118 status = g_filestatus;
\r
119 readonlyoverlay = g_readonlyoverlay;
\r
120 lockedoverlay = g_lockedoverlay;
\r
124 if (!g_ShellCache.IsPathAllowed(pPath))
\r
126 int drivenumber = -1;
\r
127 if ((m_State == FileStateVersioned) && g_ShellCache.ShowExcludedAsNormal() &&
\r
128 ((drivenumber=PathGetDriveNumber(pPath))!=0)&&(drivenumber!=1) &&
\r
129 PathIsDirectory(pPath) && g_ShellCache.HasSVNAdminDir(pPath, true))
\r
136 switch (g_ShellCache.GetCacheType())
\r
138 case ShellCache::exe:
\r
140 TSVNCacheResponse itemStatus;
\r
141 SecureZeroMemory(&itemStatus, sizeof(itemStatus));
\r
142 if (m_remoteCacheLink.GetStatusFromRemoteCache(CTGitPath(pPath), &itemStatus, true))
\r
144 status = GitStatus::GetMoreImportant(itemStatus.m_status.text_status, itemStatus.m_status.prop_status);
\r
145 /* if ((itemStatus.m_kind == git_node_file)&&(status == git_wc_status_normal)&&((itemStatus.m_needslock && itemStatus.m_owner[0]==0)||(itemStatus.m_readonly)))
\r
146 readonlyoverlay = true;
\r
147 if (itemStatus.m_owner[0]!=0)
\r
148 lockedoverlay = true;*/
\r
152 case ShellCache::dll:
\r
153 case ShellCache::dllFull:
\r
155 // Look in our caches for this item
\r
156 const FileStatusCacheEntry * s = m_CachedStatus.GetCachedItem(CTGitPath(pPath));
\r
159 status = s->status;
\r
163 // No cached status available
\r
165 // since the dwAttrib param of the IsMemberOf() function does not
\r
166 // have the SFGAO_FOLDER flag set at all (it's 0 for files and folders!)
\r
167 // we have to check if the path is a folder ourselves :(
\r
168 if (PathIsDirectory(pPath))
\r
170 if (g_ShellCache.HasSVNAdminDir(pPath, TRUE))
\r
172 if ((!g_ShellCache.IsRecursive()) && (!g_ShellCache.IsFolderOverlay()))
\r
174 status = git_wc_status_normal;
\r
178 const FileStatusCacheEntry * s = m_CachedStatus.GetFullStatus(CTGitPath(pPath), TRUE);
\r
179 status = s->status;
\r
180 // GitFolderStatus does not list unversioned files/dir so they would always end up as normal below
\r
181 // so let's assume file/dir is unversioned if not found in cache
\r
182 /*// sub-dirs that are empty (or contain no versioned files) are reported as unversioned (and should be kept as such)
\r
183 if (status != git_wc_status_unversioned)
\r
185 // if get status fails then display status as 'normal' on folder (since it contains .git)
\r
186 // TODO: works for svn since each folder has .svn, not sure if git needs additinoal processing
\r
187 status = GitStatus::GetMoreImportant(git_wc_status_normal, status);
\r
193 status = git_wc_status_none;
\r
198 const FileStatusCacheEntry * s = m_CachedStatus.GetFullStatus(CTGitPath(pPath), FALSE);
\r
199 status = s->status;
\r
202 if ((s)&&(status == git_wc_status_normal)&&(s->needslock)&&(s->owner[0]==0))
\r
203 readonlyoverlay = true;
\r
204 if ((s)&&(s->owner[0]!=0))
\r
205 lockedoverlay = true;
\r
208 // index based version does not enumerate unversioned files, so default to unversioned
\r
209 if (g_ShellCache.GetCacheType() == ShellCache::dll
\r
210 && status == git_wc_status_none && g_ShellCache.HasSVNAdminDir(pPath, true))
\r
211 status = git_wc_status_unversioned;
\r
214 case ShellCache::none:
\r
216 // no cache means we only show a 'versioned' overlay on folders
\r
217 // with an admin directory
\r
218 if (PathIsDirectory(pPath))
\r
220 if (g_ShellCache.HasSVNAdminDir(pPath, TRUE))
\r
222 status = git_wc_status_normal;
\r
226 status = git_wc_status_none;
\r
231 status = git_wc_status_none;
\r
236 ATLTRACE(_T("Status %d for file %s\n"), status, pwszPath);
\r
238 g_filepath.clear();
\r
239 g_filepath = pPath;
\r
240 g_filestatus = status;
\r
241 g_readonlyoverlay = readonlyoverlay;
\r
242 g_lockedoverlay = lockedoverlay;
\r
244 //the priority system of the shell doesn't seem to work as expected (or as I expected):
\r
245 //as it seems that if one handler returns S_OK then that handler is used, no matter
\r
246 //if other handlers would return S_OK too (they're never called on my machine!)
\r
247 //So we return S_OK for ONLY ONE handler!
\r
251 // note: we can show other overlays if due to lack of enough free overlay
\r
252 // slots some of our overlays aren't loaded. But we assume that
\r
253 // at least the 'normal' and 'modified' overlay are available.
\r
254 case git_wc_status_none:
\r
256 case git_wc_status_unversioned:
\r
257 if (g_ShellCache.ShowUnversionedOverlay() && g_unversionedovlloaded && (m_State == FileStateUnversionedOverlay))
\r
259 g_filepath.clear();
\r
263 case git_wc_status_ignored:
\r
264 if (g_ShellCache.ShowIgnoredOverlay() && g_ignoredovlloaded && (m_State == FileStateIgnoredOverlay))
\r
266 g_filepath.clear();
\r
270 case git_wc_status_normal:
\r
271 case git_wc_status_external:
\r
272 case git_wc_status_incomplete:
\r
273 if ((readonlyoverlay)&&(g_readonlyovlloaded))
\r
275 if (m_State == FileStateReadOnly)
\r
277 g_filepath.clear();
\r
283 else if ((lockedoverlay)&&(g_lockedovlloaded))
\r
285 if (m_State == FileStateLockedOverlay)
\r
287 g_filepath.clear();
\r
293 else if (m_State == FileStateVersioned)
\r
295 g_filepath.clear();
\r
300 case git_wc_status_missing:
\r
301 case git_wc_status_deleted:
\r
302 if (g_deletedovlloaded)
\r
304 if (m_State == FileStateDeleted)
\r
306 g_filepath.clear();
\r
314 // the 'deleted' overlay isn't available (due to lack of enough
\r
315 // overlay slots). So just show the 'modified' overlay instead.
\r
316 if (m_State == FileStateModified)
\r
318 g_filepath.clear();
\r
324 case git_wc_status_replaced:
\r
325 case git_wc_status_modified:
\r
326 case git_wc_status_merged:
\r
327 if (m_State == FileStateModified)
\r
329 g_filepath.clear();
\r
334 case git_wc_status_added:
\r
335 if (g_addedovlloaded)
\r
337 if (m_State== FileStateAddedOverlay)
\r
339 g_filepath.clear();
\r
347 // the 'added' overlay isn't available (due to lack of enough
\r
348 // overlay slots). So just show the 'modified' overlay instead.
\r
349 if (m_State == FileStateModified)
\r
351 g_filepath.clear();
\r
357 case git_wc_status_conflicted:
\r
358 case git_wc_status_obstructed:
\r
359 if (g_conflictedovlloaded)
\r
361 if (m_State == FileStateConflict)
\r
363 g_filepath.clear();
\r
371 // the 'conflicted' overlay isn't available (due to lack of enough
\r
372 // overlay slots). So just show the 'modified' overlay instead.
\r
373 if (m_State == FileStateModified)
\r
375 g_filepath.clear();
\r
383 } // switch (status)
\r