TGitCache test application basic work.
[TortoiseGit.git] / src / TortoiseShell / IconOverlay.cpp
blob03d7a3a39a139b227df2b14be04bb9de11f71682
1 // TortoiseSVN - 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.
19 #include "stdafx.h"
20 #include "ShellExt.h"
21 #include "Guids.h"
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
40 switch (m_State)
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.
55 return S_OK;
58 STDMETHODIMP CShellExt::GetPriority(int *pPriority)
60 switch (m_State)
62 case FileStateConflict:
63 *pPriority = 0;
64 break;
65 case FileStateModified:
66 *pPriority = 1;
67 break;
68 case FileStateDeleted:
69 *pPriority = 2;
70 break;
71 case FileStateReadOnly:
72 *pPriority = 3;
73 break;
74 case FileStateLockedOverlay:
75 *pPriority = 4;
76 break;
77 case FileStateAddedOverlay:
78 *pPriority = 5;
79 break;
80 case FileStateVersioned:
81 *pPriority = 6;
82 break;
83 default:
84 *pPriority = 100;
85 return S_FALSE;
87 return S_OK;
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
95 // to display."
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)
104 return S_FALSE;
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)
110 return S_FALSE;
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;
122 else
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.HasSVNAdminDir(pPath, true))
131 return S_OK;
133 return S_FALSE;
136 switch (g_ShellCache.GetCacheType())
138 case ShellCache::exe:
140 CTGitPath tpath(pPath);
141 if(!tpath.HasAdminDir())
143 status = git_wc_status_none;
144 break;
146 if(tpath.IsAdminDir())
148 status = git_wc_status_none;
149 break;
151 TSVNCacheResponse 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;*/
162 break;
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));
168 if (s)
170 status = s->status;
172 else
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.HasSVNAdminDir(pPath, TRUE))
183 if ((!g_ShellCache.IsRecursive()) && (!g_ShellCache.IsFolderOverlay()))
185 status = git_wc_status_normal;
187 else
189 const FileStatusCacheEntry * s = m_CachedStatus.GetFullStatus(CTGitPath(pPath), TRUE);
190 status = s->status;
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);
202 else
204 status = git_wc_status_none;
207 else
209 const FileStatusCacheEntry * s = m_CachedStatus.GetFullStatus(CTGitPath(pPath), FALSE);
210 status = s->status;
213 if ((s)&&(status == git_wc_status_normal)&&(s->needslock)&&(s->owner[0]==0))
214 readonlyoverlay = true;
215 if ((s)&&(s->owner[0]!=0))
216 lockedoverlay = true;
219 // index based version does not enumerate unversioned files, so default to unversioned
220 if (g_ShellCache.GetCacheType() == ShellCache::dll
221 && status == git_wc_status_none && g_ShellCache.HasSVNAdminDir(pPath, true))
222 status = git_wc_status_unversioned;
223 break;
224 default:
225 case ShellCache::none:
227 // no cache means we only show a 'versioned' overlay on folders
228 // with an admin directory
229 if (PathIsDirectory(pPath))
231 if (g_ShellCache.HasSVNAdminDir(pPath, TRUE))
233 status = git_wc_status_normal;
235 else
237 status = git_wc_status_none;
240 else
242 status = git_wc_status_none;
245 break;
247 ATLTRACE(_T("Status %d for file %s\n"), status, pwszPath);
249 g_filepath.clear();
250 g_filepath = pPath;
251 g_filestatus = status;
252 g_readonlyoverlay = readonlyoverlay;
253 g_lockedoverlay = lockedoverlay;
255 //the priority system of the shell doesn't seem to work as expected (or as I expected):
256 //as it seems that if one handler returns S_OK then that handler is used, no matter
257 //if other handlers would return S_OK too (they're never called on my machine!)
258 //So we return S_OK for ONLY ONE handler!
260 switch (status)
262 // note: we can show other overlays if due to lack of enough free overlay
263 // slots some of our overlays aren't loaded. But we assume that
264 // at least the 'normal' and 'modified' overlay are available.
265 case git_wc_status_none:
266 return S_FALSE;
267 case git_wc_status_unversioned:
268 if (g_ShellCache.ShowUnversionedOverlay() && g_unversionedovlloaded && (m_State == FileStateUnversionedOverlay))
270 g_filepath.clear();
271 return S_OK;
273 return S_FALSE;
274 case git_wc_status_ignored:
275 if (g_ShellCache.ShowIgnoredOverlay() && g_ignoredovlloaded && (m_State == FileStateIgnoredOverlay))
277 g_filepath.clear();
278 return S_OK;
280 return S_FALSE;
281 case git_wc_status_normal:
282 case git_wc_status_external:
283 case git_wc_status_incomplete:
284 if ((readonlyoverlay)&&(g_readonlyovlloaded))
286 if (m_State == FileStateReadOnly)
288 g_filepath.clear();
289 return S_OK;
291 else
292 return S_FALSE;
294 else if ((lockedoverlay)&&(g_lockedovlloaded))
296 if (m_State == FileStateLockedOverlay)
298 g_filepath.clear();
299 return S_OK;
301 else
302 return S_FALSE;
304 else if (m_State == FileStateVersioned)
306 g_filepath.clear();
307 return S_OK;
309 else
310 return S_FALSE;
311 case git_wc_status_missing:
312 case git_wc_status_deleted:
313 if (g_deletedovlloaded)
315 if (m_State == FileStateDeleted)
317 g_filepath.clear();
318 return S_OK;
320 else
321 return S_FALSE;
323 else
325 // the 'deleted' overlay isn't available (due to lack of enough
326 // overlay slots). So just show the 'modified' overlay instead.
327 if (m_State == FileStateModified)
329 g_filepath.clear();
330 return S_OK;
332 else
333 return S_FALSE;
335 case git_wc_status_replaced:
336 case git_wc_status_modified:
337 if (m_State == FileStateModified)
339 g_filepath.clear();
340 return S_OK;
342 else
343 return S_FALSE;
344 case git_wc_status_merged:
345 if (m_State == FileStateReadOnly)
347 g_filepath.clear();
348 return S_OK;
350 else
351 return S_FALSE;
352 case git_wc_status_added:
353 if (g_addedovlloaded)
355 if (m_State== FileStateAddedOverlay)
357 g_filepath.clear();
358 return S_OK;
360 else
361 return S_FALSE;
363 else
365 // the 'added' overlay isn't available (due to lack of enough
366 // overlay slots). So just show the 'modified' overlay instead.
367 if (m_State == FileStateModified)
369 g_filepath.clear();
370 return S_OK;
372 else
373 return S_FALSE;
375 case git_wc_status_conflicted:
376 case git_wc_status_obstructed:
377 if (g_conflictedovlloaded)
379 if (m_State == FileStateConflict)
381 g_filepath.clear();
382 return S_OK;
384 else
385 return S_FALSE;
387 else
389 // the 'conflicted' overlay isn't available (due to lack of enough
390 // overlay slots). So just show the 'modified' overlay instead.
391 if (m_State == FileStateModified)
393 g_filepath.clear();
394 return S_OK;
396 else
397 return S_FALSE;
399 default:
400 return S_FALSE;
401 } // switch (status)
402 //return S_FALSE;