OSDN Git Service

Complete Submodule Update work
[tortoisegit/TortoiseGitJp.git] / src / TortoiseShell / IconOverlay.cpp
1 // TortoiseSVN - a Windows shell extension for easy version control\r
2 \r
3 // Copyright (C) 2003-2008 - TortoiseSVN\r
4 \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
9 \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
14 \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
18 //\r
19 #include "stdafx.h"\r
20 #include "ShellExt.h"\r
21 #include "Guids.h"\r
22 #include "PreserveChdir.h"\r
23 #include "UnicodeUtils.h"\r
24 #include "GitStatus.h"\r
25 #include "..\TGitCache\CacheInterface.h"\r
26 \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
31 \r
32 STDMETHODIMP CShellExt::GetOverlayInfo(LPWSTR /*pwszIconFile*/, int /*cchMax*/, int * /*pIndex*/, DWORD * /*pdwFlags*/)\r
33 {\r
34         PreserveChdir preserveChdir;\r
35 \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
40         switch (m_State)\r
41         {\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
51         }\r
52 \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
55     return S_OK;\r
56 };\r
57 \r
58 STDMETHODIMP CShellExt::GetPriority(int *pPriority)\r
59 {\r
60         switch (m_State)\r
61         {\r
62                 case FileStateConflict:\r
63                         *pPriority = 0;\r
64                         break;\r
65                 case FileStateModified:\r
66                         *pPriority = 1;\r
67                         break;\r
68                 case FileStateDeleted:\r
69                         *pPriority = 2;\r
70                         break;\r
71                 case FileStateReadOnly:\r
72                         *pPriority = 3;\r
73                         break;\r
74                 case FileStateLockedOverlay:\r
75                         *pPriority = 4;\r
76                         break;\r
77                 case FileStateAddedOverlay:\r
78                         *pPriority = 5;\r
79                         break;\r
80                 case FileStateVersioned:\r
81                         *pPriority = 6;\r
82                         break;\r
83                 default:\r
84                         *pPriority = 100;\r
85                         return S_FALSE;\r
86         }\r
87     return S_OK;\r
88 }\r
89 \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
95 //  to display."\r
96 \r
97 STDMETHODIMP CShellExt::IsMemberOf(LPCWSTR pwszPath, DWORD /*dwAttrib*/)\r
98 {\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
104                 return S_FALSE;\r
105         const TCHAR* pPath = pwszPath;\r
106 \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
110                 return S_FALSE;\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
114 \r
115         AutoLocker lock(g_csGlobalCOMGuard);\r
116         if (_tcscmp(pPath, g_filepath.c_str())==0)\r
117         {\r
118                 status = g_filestatus;\r
119                 readonlyoverlay = g_readonlyoverlay;\r
120                 lockedoverlay = g_lockedoverlay;\r
121         }\r
122         else\r
123         {\r
124                 if (!g_ShellCache.IsPathAllowed(pPath))\r
125                 {\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
130                         {\r
131                                 return S_OK;\r
132                         }\r
133                         return S_FALSE;\r
134                 }\r
135 \r
136                 switch (g_ShellCache.GetCacheType())\r
137                 {\r
138                 case ShellCache::exe:\r
139                         {\r
140                                 TSVNCacheResponse itemStatus;\r
141                                 SecureZeroMemory(&itemStatus, sizeof(itemStatus));\r
142                                 if (m_remoteCacheLink.GetStatusFromRemoteCache(CTGitPath(pPath), &itemStatus, true))\r
143                                 {\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
149                                 }\r
150                         }\r
151                         break;\r
152                 case ShellCache::dll:\r
153                 case ShellCache::dllFull:\r
154                         {\r
155                                 // Look in our caches for this item \r
156                                 const FileStatusCacheEntry * s = m_CachedStatus.GetCachedItem(CTGitPath(pPath));\r
157                                 if (s)\r
158                                 {\r
159                                         status = s->status;\r
160                                 }\r
161                                 else\r
162                                 {\r
163                                         // No cached status available \r
164 \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
169                                         {\r
170                                                 if (g_ShellCache.HasSVNAdminDir(pPath, TRUE))\r
171                                                 {\r
172                                                         if ((!g_ShellCache.IsRecursive()) && (!g_ShellCache.IsFolderOverlay()))\r
173                                                         {\r
174                                                                 status = git_wc_status_normal;\r
175                                                         }\r
176                                                         else\r
177                                                         {\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
184                                                                 {\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
188                                                                 }*/\r
189                                                         }\r
190                                                 }\r
191                                                 else\r
192                                                 {\r
193                                                         status = git_wc_status_none;\r
194                                                 }\r
195                                         }\r
196                                         else\r
197                                         {\r
198                                                 const FileStatusCacheEntry * s = m_CachedStatus.GetFullStatus(CTGitPath(pPath), FALSE);\r
199                                                 status = s->status;\r
200                                         }\r
201                                 }\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
206                         }\r
207 \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
212                         break;\r
213                 default:\r
214                 case ShellCache::none:\r
215                         {\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
219                                 {\r
220                                         if (g_ShellCache.HasSVNAdminDir(pPath, TRUE))\r
221                                         {\r
222                                                 status = git_wc_status_normal;\r
223                                         }\r
224                                         else\r
225                                         {\r
226                                                 status = git_wc_status_none;\r
227                                         }\r
228                                 }\r
229                                 else\r
230                                 {\r
231                                         status = git_wc_status_none;\r
232                                 }\r
233                         }\r
234                         break;\r
235                 }\r
236                 ATLTRACE(_T("Status %d for file %s\n"), status, pwszPath);\r
237         }\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
243 \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
248 \r
249         switch (status)\r
250         {\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
255                         return S_FALSE;\r
256                 case git_wc_status_unversioned:\r
257                         if (g_ShellCache.ShowUnversionedOverlay() && g_unversionedovlloaded && (m_State == FileStateUnversionedOverlay))\r
258                         {\r
259                                 g_filepath.clear();\r
260                                 return S_OK;\r
261                         }\r
262                         return S_FALSE;\r
263                 case git_wc_status_ignored:\r
264                         if (g_ShellCache.ShowIgnoredOverlay() && g_ignoredovlloaded && (m_State == FileStateIgnoredOverlay))\r
265                         {\r
266                                 g_filepath.clear();\r
267                                 return S_OK;\r
268                         }\r
269                         return S_FALSE;\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
274                         {\r
275                                 if (m_State == FileStateReadOnly)\r
276                                 {\r
277                                         g_filepath.clear();\r
278                                         return S_OK;\r
279                                 }\r
280                                 else\r
281                                         return S_FALSE;\r
282                         }\r
283                         else if ((lockedoverlay)&&(g_lockedovlloaded))\r
284                         {\r
285                                 if (m_State == FileStateLockedOverlay)\r
286                                 {\r
287                                         g_filepath.clear();\r
288                                         return S_OK;\r
289                                 }\r
290                                 else\r
291                                         return S_FALSE;\r
292                         }\r
293                         else if (m_State == FileStateVersioned)\r
294                         {\r
295                                 g_filepath.clear();\r
296                                 return S_OK;\r
297                         }\r
298                         else\r
299                                 return S_FALSE;\r
300                 case git_wc_status_missing:\r
301                 case git_wc_status_deleted:\r
302                         if (g_deletedovlloaded)\r
303                         {\r
304                                 if (m_State == FileStateDeleted)\r
305                                 {\r
306                                         g_filepath.clear();\r
307                                         return S_OK;\r
308                                 }\r
309                                 else\r
310                                         return S_FALSE;\r
311                         }\r
312                         else\r
313                         {\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
317                                 {\r
318                                         g_filepath.clear();\r
319                                         return S_OK;\r
320                                 }\r
321                                 else\r
322                                         return S_FALSE;\r
323                         }\r
324                 case git_wc_status_replaced:\r
325                 case git_wc_status_modified:\r
326                         if (m_State == FileStateModified)\r
327                         {\r
328                                 g_filepath.clear();\r
329                                 return S_OK;\r
330                         }\r
331                         else\r
332                                 return S_FALSE;\r
333                 case git_wc_status_merged:\r
334                         if (m_State == FileStateReadOnly)\r
335                         {\r
336                                 g_filepath.clear();\r
337                                 return S_OK;\r
338                         }\r
339                         else\r
340                                 return S_FALSE;\r
341                 case git_wc_status_added:\r
342                         if (g_addedovlloaded)\r
343                         {\r
344                                 if (m_State== FileStateAddedOverlay)\r
345                                 {\r
346                                         g_filepath.clear();\r
347                                         return S_OK;\r
348                                 }\r
349                                 else\r
350                                         return S_FALSE;\r
351                         }\r
352                         else\r
353                         {\r
354                                 // the 'added' overlay isn't available (due to lack of enough\r
355                                 // overlay slots). So just show the 'modified' overlay instead.\r
356                                 if (m_State == FileStateModified)\r
357                                 {\r
358                                         g_filepath.clear();\r
359                                         return S_OK;\r
360                                 }\r
361                                 else\r
362                                         return S_FALSE;\r
363                         }\r
364                 case git_wc_status_conflicted:\r
365                 case git_wc_status_obstructed:\r
366                         if (g_conflictedovlloaded)\r
367                         {\r
368                                 if (m_State == FileStateConflict)\r
369                                 {\r
370                                         g_filepath.clear();\r
371                                         return S_OK;\r
372                                 }\r
373                                 else\r
374                                         return S_FALSE;\r
375                         }\r
376                         else\r
377                         {\r
378                                 // the 'conflicted' overlay isn't available (due to lack of enough\r
379                                 // overlay slots). So just show the 'modified' overlay instead.\r
380                                 if (m_State == FileStateModified)\r
381                                 {\r
382                                         g_filepath.clear();\r
383                                         return S_OK;\r
384                                 }\r
385                                 else\r
386                                         return S_FALSE;\r
387                         }\r
388                 default:\r
389                         return S_FALSE;\r
390         } // switch (status)\r
391     //return S_FALSE;\r
392 }\r
393 \r