OSDN Git Service

Add LogList To GitBlame
[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 "..\TSVNCache\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 #if 0\r
136                 switch (g_ShellCache.GetCacheType())\r
137                 {\r
138                 case ShellCache::exe:\r
139                         {\r
140 #if 0\r
141                                 TSVNCacheResponse itemStatus;\r
142                                 SecureZeroMemory(&itemStatus, sizeof(itemStatus));\r
143                                 if (m_remoteCacheLink.GetStatusFromRemoteCache(CTSVNPath(pPath), &itemStatus, true))\r
144                                 {\r
145                                         status = SVNStatus::GetMoreImportant(itemStatus.m_status.text_status, itemStatus.m_status.prop_status);\r
146                                         if ((itemStatus.m_kind == git_node_file)&&(status == git_wc_status_normal)&&((itemStatus.m_needslock && itemStatus.m_owner[0]==0)||(itemStatus.m_readonly)))\r
147                                                 readonlyoverlay = true;\r
148                                         if (itemStatus.m_owner[0]!=0)\r
149                                                 lockedoverlay = true;\r
150                                 }\r
151 #endif \r
152                         }\r
153 //                      break;\r
154                 case ShellCache::dll:\r
155                         {\r
156                                 // Look in our caches for this item \r
157 #if 0\r
158                                 const FileStatusCacheEntry * s = m_CachedStatus.GetCachedItem(CTSVNPath(pPath));\r
159                                 if (s)\r
160                                 {\r
161                                         status = s->status;\r
162                                 }\r
163                                 else\r
164                                 {\r
165                                         // No cached status available \r
166 \r
167                                         // since the dwAttrib param of the IsMemberOf() function does not\r
168                                         // have the SFGAO_FOLDER flag set at all (it's 0 for files and folders!)\r
169                                         // we have to check if the path is a folder ourselves :(\r
170                                         if (PathIsDirectory(pPath))\r
171                                         {\r
172                                                 if (g_ShellCache.HasSVNAdminDir(pPath, TRUE))\r
173                                                 {\r
174                                                         if ((!g_ShellCache.IsRecursive()) && (!g_ShellCache.IsFolderOverlay()))\r
175                                                         {\r
176                                                                 status = git_wc_status_normal;\r
177                                                         }\r
178                                                         else\r
179                                                         {\r
180                                                                 const FileStatusCacheEntry * s = m_CachedStatus.GetFullStatus(CTSVNPath(pPath), TRUE);\r
181                                                                 status = s->status;\r
182                                                                 status = SVNStatus::GetMoreImportant(git_wc_status_normal, status);\r
183                                                         }\r
184                                                 }\r
185                                                 else\r
186                                                 {\r
187                                                         status = git_wc_status_none;\r
188                                                 }\r
189                                         }\r
190                                         else\r
191                                         {\r
192                                                 const FileStatusCacheEntry * s = m_CachedStatus.GetFullStatus(CTSVNPath(pPath), FALSE);\r
193                                                 status = s->status;\r
194                                         }\r
195                                 }\r
196                                 if ((s)&&(status == git_wc_status_normal)&&(s->needslock)&&(s->owner[0]==0))\r
197                                         readonlyoverlay = true;\r
198                                 if ((s)&&(s->owner[0]!=0))\r
199                                         lockedoverlay = true;\r
200 #endif\r
201                         }\r
202 \r
203                         break;\r
204                 default:\r
205                 case ShellCache::none:\r
206                         {\r
207                                 // no cache means we only show a 'versioned' overlay on folders\r
208                                 // with an admin directory\r
209                                 if (PathIsDirectory(pPath))\r
210                                 {\r
211                                         if (g_ShellCache.HasSVNAdminDir(pPath, TRUE))\r
212                                         {\r
213                                                 status = git_wc_status_normal;\r
214                                         }\r
215                                         else\r
216                                         {\r
217                                                 status = git_wc_status_none;\r
218                                         }\r
219                                 }\r
220                                 else\r
221                                 {\r
222                                         status = git_wc_status_none;\r
223                                 }\r
224                         }\r
225                         break;\r
226                 }\r
227                 ATLTRACE(_T("Status %d for file %s\n"), status, pwszPath);\r
228 #endif \r
229         }\r
230 \r
231         if (PathIsDirectory(pPath))\r
232         {\r
233                 if (g_ShellCache.HasSVNAdminDir(pPath, TRUE))\r
234                 {\r
235                         status = git_wc_status_normal;          \r
236                 }else\r
237                 {\r
238                         status = git_wc_status_none;\r
239                 }\r
240         }\r
241         g_filepath.clear();\r
242         g_filepath = pPath;\r
243         g_filestatus = status;\r
244         g_readonlyoverlay = readonlyoverlay;\r
245         g_lockedoverlay = lockedoverlay;\r
246         \r
247         //the priority system of the shell doesn't seem to work as expected (or as I expected):\r
248         //as it seems that if one handler returns S_OK then that handler is used, no matter\r
249         //if other handlers would return S_OK too (they're never called on my machine!)\r
250         //So we return S_OK for ONLY ONE handler!\r
251         switch (status)\r
252         {\r
253                 // note: we can show other overlays if due to lack of enough free overlay\r
254                 // slots some of our overlays aren't loaded. But we assume that\r
255                 // at least the 'normal' and 'modified' overlay are available.\r
256                 case git_wc_status_none:\r
257                         return S_FALSE;\r
258                 case git_wc_status_unversioned:\r
259                         if (g_ShellCache.ShowUnversionedOverlay() && g_unversionedovlloaded && (m_State == FileStateUnversionedOverlay))\r
260                         {\r
261                                 g_filepath.clear();\r
262                                 return S_OK;\r
263                         }\r
264                         return S_FALSE;\r
265                 case git_wc_status_ignored:\r
266                         if (g_ShellCache.ShowIgnoredOverlay() && g_ignoredovlloaded && (m_State == FileStateIgnoredOverlay))\r
267                         {\r
268                                 g_filepath.clear();\r
269                                 return S_OK;\r
270                         }\r
271                         return S_FALSE;\r
272                 case git_wc_status_normal:\r
273                 case git_wc_status_external:\r
274                 case git_wc_status_incomplete:\r
275                         if ((readonlyoverlay)&&(g_readonlyovlloaded))\r
276                         {\r
277                                 if (m_State == FileStateReadOnly)\r
278                                 {\r
279                                         g_filepath.clear();\r
280                                         return S_OK;\r
281                                 }\r
282                                 else\r
283                                         return S_FALSE;\r
284                         }\r
285                         else if ((lockedoverlay)&&(g_lockedovlloaded))\r
286                         {\r
287                                 if (m_State == FileStateLockedOverlay)\r
288                                 {\r
289                                         g_filepath.clear();\r
290                                         return S_OK;\r
291                                 }\r
292                                 else\r
293                                         return S_FALSE;\r
294                         }\r
295                         else if (m_State == FileStateVersioned)\r
296                         {\r
297                                 g_filepath.clear();\r
298                                 return S_OK;\r
299                         }\r
300                         else\r
301                                 return S_FALSE;\r
302                 case git_wc_status_missing:\r
303                 case git_wc_status_deleted:\r
304                         if (g_deletedovlloaded)\r
305                         {\r
306                                 if (m_State == FileStateDeleted)\r
307                                 {\r
308                                         g_filepath.clear();\r
309                                         return S_OK;\r
310                                 }\r
311                                 else\r
312                                         return S_FALSE;\r
313                         }\r
314                         else\r
315                         {\r
316                                 // the 'deleted' overlay isn't available (due to lack of enough\r
317                                 // overlay slots). So just show the 'modified' overlay instead.\r
318                                 if (m_State == FileStateModified)\r
319                                 {\r
320                                         g_filepath.clear();\r
321                                         return S_OK;\r
322                                 }\r
323                                 else\r
324                                         return S_FALSE;\r
325                         }\r
326                 case git_wc_status_replaced:\r
327                 case git_wc_status_modified:\r
328                 case git_wc_status_merged:\r
329                         if (m_State == FileStateModified)\r
330                         {\r
331                                 g_filepath.clear();\r
332                                 return S_OK;\r
333                         }\r
334                         else\r
335                                 return S_FALSE;\r
336                 case git_wc_status_added:\r
337                         if (g_addedovlloaded)\r
338                         {\r
339                                 if (m_State== FileStateAddedOverlay)\r
340                                 {\r
341                                         g_filepath.clear();\r
342                                         return S_OK;\r
343                                 }\r
344                                 else\r
345                                         return S_FALSE;\r
346                         }\r
347                         else\r
348                         {\r
349                                 // the 'added' overlay isn't available (due to lack of enough\r
350                                 // overlay slots). So just show the 'modified' overlay instead.\r
351                                 if (m_State == FileStateModified)\r
352                                 {\r
353                                         g_filepath.clear();\r
354                                         return S_OK;\r
355                                 }\r
356                                 else\r
357                                         return S_FALSE;\r
358                         }\r
359                 case git_wc_status_conflicted:\r
360                 case git_wc_status_obstructed:\r
361                         if (g_conflictedovlloaded)\r
362                         {\r
363                                 if (m_State == FileStateConflict)\r
364                                 {\r
365                                         g_filepath.clear();\r
366                                         return S_OK;\r
367                                 }\r
368                                 else\r
369                                         return S_FALSE;\r
370                         }\r
371                         else\r
372                         {\r
373                                 // the 'conflicted' overlay isn't available (due to lack of enough\r
374                                 // overlay slots). So just show the 'modified' overlay instead.\r
375                                 if (m_State == FileStateModified)\r
376                                 {\r
377                                         g_filepath.clear();\r
378                                         return S_OK;\r
379                                 }\r
380                                 else\r
381                                         return S_FALSE;\r
382                         }\r
383                 default:\r
384                         return S_FALSE;\r
385         } // switch (status)\r
386     //return S_FALSE;\r
387 }\r
388 \r