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