OSDN Git Service

b48671acfecf235e882f8d8685f4ad0f49d9ab33
[tortoisegit/TortoiseGitJp.git] / TortoiseShell / ColumnProvider.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 "SVNProperties.h"\r
24 #include "UnicodeUtils.h"\r
25 #include "SVNStatus.h"\r
26 #include "PathUtils.h"\r
27 #include "..\TSVNCache\CacheInterface.h"\r
28 \r
29 \r
30 const static int ColumnFlags = SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT;\r
31 \r
32 // Defines that revision numbers occupy at most MAX_REV_STRING_LEN characters.\r
33 // There are Perforce repositories out there that have several 100,000 revs.\r
34 // So, don't be too restrictive by limiting this to 6 digits + 1 separator, \r
35 // for instance. \r
36 //\r
37 // Because shorter strings will be extended to have exactly MAX_REV_STRING_LEN \r
38 // characters, large numbers will produce large strings. These, in turn, will\r
39 // affect column auto sizing. This setting is a reasonable compromise.\r
40 //\r
41 // Max rev correctly sorted: 99,999,999 for MAX_REV_STRING_LEN == 10\r
42 \r
43 #define MAX_REV_STRING_LEN 10\r
44 \r
45 // IColumnProvider members\r
46 STDMETHODIMP CShellExt::GetColumnInfo(DWORD dwIndex, SHCOLUMNINFO *psci)\r
47 {\r
48         PreserveChdir preserveChdir;\r
49         if (dwIndex > 8)\r
50                 return S_FALSE;\r
51 \r
52         ShellCache::CacheType cachetype = g_ShellCache.GetCacheType();\r
53         LoadLangDll();\r
54         wide_string ws;\r
55         switch (dwIndex)\r
56         {\r
57                 case 0: // SVN Status\r
58                         if (cachetype == ShellCache::none)\r
59                                 return S_FALSE;\r
60                         psci->scid.fmtid = CLSID_TortoiseSVN_UPTODATE;\r
61                         psci->scid.pid = dwIndex;\r
62                         psci->vt = VT_BSTR;\r
63                         psci->fmt = LVCFMT_LEFT;\r
64                         psci->cChars = 15;\r
65                         psci->csFlags = ColumnFlags;\r
66 \r
67                         MAKESTRING(IDS_COLTITLESTATUS);\r
68                         lstrcpynW(psci->wszTitle, stringtablebuffer, MAX_COLUMN_NAME_LEN);\r
69                         MAKESTRING(IDS_COLDESCSTATUS);\r
70                         lstrcpynW(psci->wszDescription, stringtablebuffer, MAX_COLUMN_DESC_LEN);\r
71                         break;\r
72                 case 1: // SVN Revision\r
73                         if (cachetype == ShellCache::none)\r
74                                 return S_FALSE;\r
75                         psci->scid.fmtid = CLSID_TortoiseSVN_UPTODATE;\r
76                         psci->scid.pid = dwIndex;\r
77                         psci->vt = VT_I4;\r
78                         psci->fmt = LVCFMT_RIGHT;\r
79                         psci->cChars = 15;\r
80                         psci->csFlags = SHCOLSTATE_TYPE_INT | SHCOLSTATE_ONBYDEFAULT;\r
81 \r
82                         MAKESTRING(IDS_COLTITLEREV);\r
83                         lstrcpynW(psci->wszTitle, stringtablebuffer, MAX_COLUMN_NAME_LEN);\r
84                         MAKESTRING(IDS_COLDESCREV);\r
85                         lstrcpynW(psci->wszDescription, stringtablebuffer, MAX_COLUMN_DESC_LEN);\r
86                         break;\r
87                 case 2: // SVN Url\r
88                         if (cachetype == ShellCache::none)\r
89                                 return S_FALSE;\r
90                         psci->scid.fmtid = CLSID_TortoiseSVN_UPTODATE;\r
91                         psci->scid.pid = dwIndex;\r
92                         psci->vt = VT_BSTR;\r
93                         psci->fmt = LVCFMT_LEFT;\r
94                         psci->cChars = 30;\r
95                         psci->csFlags = ColumnFlags;\r
96 \r
97                         MAKESTRING(IDS_COLTITLEURL);\r
98                         lstrcpynW(psci->wszTitle, stringtablebuffer, MAX_COLUMN_NAME_LEN);\r
99                         MAKESTRING(IDS_COLDESCURL);\r
100                         lstrcpynW(psci->wszDescription, stringtablebuffer, MAX_COLUMN_DESC_LEN);\r
101                         break;\r
102                 case 3: // SVN Short Url\r
103                         if (cachetype == ShellCache::none)\r
104                                 return S_FALSE;\r
105                         psci->scid.fmtid = CLSID_TortoiseSVN_UPTODATE;\r
106                         psci->scid.pid = dwIndex;\r
107                         psci->vt = VT_BSTR;\r
108                         psci->fmt = LVCFMT_LEFT;\r
109                         psci->cChars = 30;\r
110                         psci->csFlags = ColumnFlags;\r
111 \r
112                         MAKESTRING(IDS_COLTITLESHORTURL);\r
113                         lstrcpynW(psci->wszTitle, stringtablebuffer, MAX_COLUMN_NAME_LEN);\r
114                         MAKESTRING(IDS_COLDESCSHORTURL);\r
115                         lstrcpynW(psci->wszDescription, stringtablebuffer, MAX_COLUMN_DESC_LEN);\r
116                         break;\r
117                 case 4: // Author\r
118                         if (cachetype == ShellCache::none)\r
119                                 return S_FALSE;\r
120                         psci->scid.fmtid = FMTID_SummaryInformation;    // predefined FMTID\r
121                         psci->scid.pid   = PIDSI_AUTHOR;                                // Predefined - author\r
122                         psci->vt         = VT_LPSTR;                                    // We'll return the data as a string\r
123                         psci->fmt        = LVCFMT_LEFT;                                 // Text will be left-aligned in the column\r
124                         psci->csFlags    = SHCOLSTATE_TYPE_STR;                 // Data should be sorted as strings\r
125                         psci->cChars     = 32;                                                  // Default col width in chars\r
126                         break;\r
127                 case 5: // SVN mime-type\r
128                         if (cachetype == ShellCache::none)\r
129                                 return S_FALSE;\r
130                         psci->scid.fmtid = CLSID_TortoiseSVN_UPTODATE;\r
131                         psci->scid.pid = dwIndex;\r
132                         psci->vt = VT_BSTR;\r
133                         psci->fmt = LVCFMT_LEFT;\r
134                         psci->cChars = 30;\r
135                         psci->csFlags = ColumnFlags;\r
136 \r
137                         MAKESTRING(IDS_COLTITLEMIMETYPE);\r
138                         lstrcpynW(psci->wszTitle, stringtablebuffer, MAX_COLUMN_NAME_LEN);\r
139                         MAKESTRING(IDS_COLDESCMIMETYPE);\r
140                         lstrcpynW(psci->wszDescription, stringtablebuffer, MAX_COLUMN_DESC_LEN);\r
141                         break;\r
142                 case 6: // SVN Lock Owner\r
143                         if (cachetype == ShellCache::none)\r
144                                 return S_FALSE;\r
145                         psci->scid.fmtid = CLSID_TortoiseSVN_UPTODATE;\r
146                         psci->scid.pid = dwIndex;\r
147                         psci->vt = VT_BSTR;\r
148                         psci->fmt = LVCFMT_LEFT;\r
149                         psci->cChars = 30;\r
150                         psci->csFlags = ColumnFlags;\r
151 \r
152                         MAKESTRING(IDS_COLTITLEOWNER);\r
153                         lstrcpynW(psci->wszTitle, stringtablebuffer, MAX_COLUMN_NAME_LEN);\r
154                         MAKESTRING(IDS_COLDESCOWNER);\r
155                         lstrcpynW(psci->wszDescription, stringtablebuffer, MAX_COLUMN_DESC_LEN);\r
156                         break;\r
157                 case 7: // SVN eol-style\r
158                         if (cachetype == ShellCache::none)\r
159                                 return S_FALSE;\r
160                         psci->scid.fmtid = CLSID_TortoiseSVN_UPTODATE;\r
161                         psci->scid.pid = dwIndex;\r
162                         psci->vt = VT_BSTR;\r
163                         psci->fmt = LVCFMT_LEFT;\r
164                         psci->cChars = 30;\r
165                         psci->csFlags = ColumnFlags;\r
166 \r
167                         MAKESTRING(IDS_COLTITLEEOLSTYLE);\r
168                         lstrcpynW(psci->wszTitle, stringtablebuffer, MAX_COLUMN_NAME_LEN);\r
169                         MAKESTRING(IDS_COLDESCEOLSTYLE);\r
170                         lstrcpynW(psci->wszDescription, stringtablebuffer, MAX_COLUMN_DESC_LEN);\r
171                         break;\r
172                 case 8: // SVN Author\r
173                         if (cachetype == ShellCache::none)\r
174                                 return S_FALSE;\r
175                         psci->scid.fmtid = CLSID_TortoiseSVN_UPTODATE;\r
176                         psci->scid.pid = dwIndex;\r
177                         psci->vt = VT_BSTR;\r
178                         psci->fmt = LVCFMT_LEFT;\r
179                         psci->cChars = 30;\r
180                         psci->csFlags = ColumnFlags;\r
181 \r
182                         MAKESTRING(IDS_COLTITLEAUTHOR);\r
183                         lstrcpynW(psci->wszTitle, stringtablebuffer, MAX_COLUMN_NAME_LEN);\r
184                         MAKESTRING(IDS_COLDESCAUTHOR);\r
185                         lstrcpynW(psci->wszDescription, stringtablebuffer, MAX_COLUMN_DESC_LEN);\r
186                         break;\r
187         }\r
188 \r
189         return S_OK;\r
190 }\r
191 \r
192 STDMETHODIMP CShellExt::GetItemData(LPCSHCOLUMNID pscid, LPCSHCOLUMNDATA pscd, VARIANT *pvarData)\r
193 {\r
194         PreserveChdir preserveChdir;\r
195         if (!g_ShellCache.IsPathAllowed((TCHAR *)pscd->wszFile))\r
196         {\r
197                 return S_FALSE;\r
198         }\r
199         LoadLangDll();\r
200         ShellCache::CacheType cachetype = g_ShellCache.GetCacheType();\r
201         if (pscid->fmtid == CLSID_TortoiseSVN_UPTODATE && pscid->pid < 8) \r
202         {\r
203                 stdstring szInfo;\r
204                 const TCHAR * path = (TCHAR *)pscd->wszFile;\r
205 \r
206                 // reserve for the path + trailing \0\r
207 \r
208                 TCHAR buf[MAX_STATUS_STRING_LENGTH+1];\r
209                 SecureZeroMemory(buf, MAX_STATUS_STRING_LENGTH);\r
210                 switch (pscid->pid) \r
211                 {\r
212                         case 0: // SVN Status\r
213                                 GetColumnStatus(path, pscd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);\r
214                                 SVNStatus::GetStatusString(g_hResInst, filestatus, buf, sizeof(buf)/sizeof(TCHAR), (WORD)CRegStdWORD(_T("Software\\TortoiseSVN\\LanguageID"), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT)));\r
215                                 szInfo = buf;\r
216                                 break;\r
217                         case 1: // SVN Revision\r
218                                 GetColumnStatus(path, pscd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);\r
219                                 if (columnrev >= 0)\r
220                                 {\r
221                                         V_VT(pvarData) = VT_I4;\r
222                                         V_I4(pvarData) = columnrev;\r
223                                 }\r
224                                 return S_OK;\r
225                                 break;\r
226                         case 2: // SVN Url\r
227                                 GetColumnStatus(path, pscd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);\r
228                                 szInfo = itemurl;\r
229                                 break;\r
230                         case 3: // SVN Short Url\r
231                                 GetColumnStatus(path, pscd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);\r
232                                 szInfo = itemshorturl;\r
233                                 break;\r
234                         case 5: // SVN mime-type\r
235                                 if (cachetype == ShellCache::none)\r
236                                         return S_FALSE;\r
237                                 if (g_ShellCache.IsPathAllowed(path))\r
238                                 {\r
239                                         SVNProperties props = SVNProperties(CTSVNPath(path), false);\r
240                                         for (int i=0; i<props.GetCount(); i++)\r
241                                         {\r
242                                                 if (props.GetItemName(i).compare(_T("svn:mime-type"))==0)\r
243                                                 {\r
244                                                         szInfo = MultibyteToWide((char *)props.GetItemValue(i).c_str());\r
245                                                 }\r
246                                         }\r
247                                 }\r
248                                 break;\r
249                         case 6: // SVN Lock Owner\r
250                                 GetColumnStatus(path, pscd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);\r
251                                 szInfo = owner;\r
252                                 break;\r
253                         case 7: // SVN eol-style\r
254                                 if (cachetype == ShellCache::none)\r
255                                         return S_FALSE;\r
256                                 if (g_ShellCache.IsPathAllowed(path))\r
257                                 {\r
258                                         SVNProperties props = SVNProperties(CTSVNPath(path), false);\r
259                                         for (int i=0; i<props.GetCount(); i++)\r
260                                         {\r
261                                                 if (props.GetItemName(i).compare(_T("svn:eol-style"))==0)\r
262                                                 {\r
263                                                         szInfo = MultibyteToWide((char *)props.GetItemValue(i).c_str());\r
264                                                 }\r
265                                         }\r
266                                 }\r
267                                 break;\r
268                         default:\r
269                                 return S_FALSE;\r
270                 }\r
271                 const WCHAR * wsInfo = szInfo.c_str();\r
272                 V_VT(pvarData) = VT_BSTR;\r
273                 V_BSTR(pvarData) = SysAllocString(wsInfo);\r
274                 return S_OK;\r
275         }\r
276         if ((pscid->fmtid == FMTID_SummaryInformation)||(pscid->pid == 8))\r
277         {\r
278                 stdstring szInfo;\r
279                 const TCHAR * path = pscd->wszFile;\r
280 \r
281                 if (cachetype == ShellCache::none)\r
282                         return S_FALSE;\r
283                 switch (pscid->pid)\r
284                 {\r
285                 case PIDSI_AUTHOR:                      // author\r
286                 case 8:\r
287                         GetColumnStatus(path, pscd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);\r
288                         szInfo = columnauthor;\r
289                         break;\r
290                 default:\r
291                         return S_FALSE;\r
292                 }\r
293                 wide_string wsInfo = szInfo;\r
294                 V_VT(pvarData) = VT_BSTR;\r
295                 V_BSTR(pvarData) = SysAllocString(wsInfo.c_str());\r
296                 return S_OK;\r
297         }\r
298 \r
299         return S_FALSE;\r
300 }\r
301 \r
302 STDMETHODIMP CShellExt::Initialize(LPCSHCOLUMNINIT psci)\r
303 {\r
304         // psci->wszFolder (WCHAR) holds the path to the folder to be displayed\r
305         // Should check to see if its a "SVN" folder and if not return E_FAIL\r
306 \r
307         PreserveChdir preserveChdir;\r
308         if (g_ShellCache.IsColumnsEveryWhere())\r
309                 return S_OK;\r
310         std::wstring path = psci->wszFolder;\r
311         if (!path.empty())\r
312         {\r
313                 if (! g_ShellCache.HasSVNAdminDir(path.c_str(), TRUE))\r
314                         return E_FAIL;\r
315         }\r
316         columnfilepath = _T("");\r
317         return S_OK;\r
318 }\r
319 \r
320 void CShellExt::GetColumnStatus(const TCHAR * path, BOOL bIsDir)\r
321 {\r
322         PreserveChdir preserveChdir;\r
323         if (_tcscmp(path, columnfilepath.c_str())==0)\r
324                 return;\r
325         LoadLangDll();\r
326         columnfilepath = path;\r
327         const FileStatusCacheEntry * status = NULL;\r
328         TSVNCacheResponse itemStatus;\r
329         ShellCache::CacheType t = ShellCache::exe;\r
330         AutoLocker lock(g_csGlobalCOMGuard);\r
331         t = g_ShellCache.GetCacheType();\r
332 \r
333         switch (t)\r
334         {\r
335         case ShellCache::exe:\r
336                 {\r
337                         SecureZeroMemory(&itemStatus, sizeof(itemStatus));\r
338                         if(m_remoteCacheLink.GetStatusFromRemoteCache(CTSVNPath(path), &itemStatus, true))\r
339                         {\r
340                                 filestatus = SVNStatus::GetMoreImportant(itemStatus.m_status.text_status, itemStatus.m_status.prop_status);\r
341                         }\r
342                         else\r
343                         {\r
344                                 filestatus = svn_wc_status_none;\r
345                                 columnauthor.clear();\r
346                                 columnrev = 0;\r
347                                 itemurl.clear();\r
348                                 itemshorturl.clear();\r
349                                 owner.clear();\r
350                                 return; \r
351                         }\r
352                 }\r
353                 break;\r
354         case ShellCache::dll:\r
355                 {\r
356                         status = m_CachedStatus.GetFullStatus(CTSVNPath(path), bIsDir, TRUE);\r
357                         filestatus = status->status;\r
358                 }\r
359                 break;\r
360         default:\r
361         case ShellCache::none:\r
362                 {\r
363                         if (g_ShellCache.HasSVNAdminDir(path, bIsDir))\r
364                                 filestatus = svn_wc_status_normal;\r
365                         else\r
366                                 filestatus = svn_wc_status_none;\r
367                         columnauthor.clear();\r
368                         columnrev = 0;\r
369                         itemurl.clear();\r
370                         itemshorturl.clear();\r
371                         owner.clear();\r
372                         return; \r
373                 }\r
374                 break;\r
375         }\r
376 \r
377         if (t == ShellCache::exe)\r
378         {\r
379                 columnauthor = UTF8ToWide(itemStatus.m_author);\r
380                 columnrev = itemStatus.m_entry.cmt_rev;\r
381                 itemurl = UTF8ToWide(itemStatus.m_url);\r
382                 owner = UTF8ToWide(itemStatus.m_owner);\r
383         }\r
384         else\r
385         {\r
386                 if (status)\r
387                 {\r
388                         columnauthor = UTF8ToWide(status->author);\r
389                         columnrev = status->rev;\r
390                         itemurl = UTF8ToWide(status->url);\r
391                         owner = UTF8ToWide(status->owner);\r
392                 }\r
393         }\r
394         TCHAR urlpath[INTERNET_MAX_URL_LENGTH+1];\r
395 \r
396         URL_COMPONENTS urlComponents;\r
397         memset(&urlComponents, 0, sizeof(URL_COMPONENTS));\r
398         urlComponents.dwStructSize = sizeof(URL_COMPONENTS);\r
399         urlComponents.dwUrlPathLength = INTERNET_MAX_URL_LENGTH;\r
400         urlComponents.lpszUrlPath = urlpath;\r
401         if (InternetCrackUrl(itemurl.c_str(), 0, ICU_DECODE, &urlComponents))\r
402         {\r
403                 // since the short url is shown as an additional column where the\r
404                 // file/foldername is shown too, we strip that name from the url\r
405                 // to make the url even shorter.\r
406                 TCHAR * ptr = _tcsrchr(urlComponents.lpszUrlPath, '/');\r
407                 if (ptr == NULL)\r
408                         ptr = _tcsrchr(urlComponents.lpszUrlPath, '\\');\r
409                 if (ptr)\r
410                 {\r
411                         *ptr = '\0';\r
412                         // to shorten the url even more, we check for 'trunk', 'branches' and 'tags'\r
413                         // and simply assume that these are the folders attached to the repository\r
414                         // root. If we find those, we strip the whole path before those folders too.\r
415                         // Note: this will strip too much if such a folder is *below* the repository\r
416                         // root - but it's called 'short url' and we're free to shorten it the way we\r
417                         // like :)\r
418                         ptr = _tcsstr(urlComponents.lpszUrlPath, _T("/trunk"));\r
419                         if (ptr == NULL)\r
420                                 ptr = _tcsstr(urlComponents.lpszUrlPath, _T("\\trunk"));\r
421                         if ((ptr == NULL)||((*(ptr+6) != 0)&&(*(ptr+6) != '/')&&(*(ptr+6) != '\\')))\r
422                         {\r
423                                 ptr = _tcsstr(urlComponents.lpszUrlPath, _T("/branches"));\r
424                                 if (ptr == NULL)\r
425                                         ptr = _tcsstr(urlComponents.lpszUrlPath, _T("\\branches"));\r
426                                 if ((ptr == NULL)||((*(ptr+9) != 0)&&(*(ptr+9) != '/')&&(*(ptr+9) != '\\')))\r
427                                 {\r
428                                         ptr = _tcsstr(urlComponents.lpszUrlPath, _T("/tags"));\r
429                                         if (ptr == NULL)\r
430                                                 ptr = _tcsstr(urlComponents.lpszUrlPath, _T("\\tags"));\r
431                                         if ((ptr)&&(*(ptr+5) != 0)&&(*(ptr+5) != '/')&&(*(ptr+5) != '\\'))\r
432                                                 ptr = NULL;\r
433                                 }\r
434                         }\r
435                         if (ptr)\r
436                                 itemshorturl = ptr;\r
437                         else\r
438                                 itemshorturl = urlComponents.lpszUrlPath;\r
439                 }\r
440                 else \r
441                         itemshorturl = _T(" ");\r
442         }\r
443         else\r
444                 itemshorturl = _T(" ");\r
445 \r
446         if (status)\r
447         {\r
448                 char url[INTERNET_MAX_URL_LENGTH];\r
449                 strcpy_s(url, INTERNET_MAX_URL_LENGTH, status->url);\r
450                 CPathUtils::Unescape(url);\r
451                 itemurl = UTF8ToWide(url);\r
452         }\r
453         else if (t == ShellCache::exe)\r
454         {\r
455                 char url[INTERNET_MAX_URL_LENGTH];\r
456                 strcpy_s(url, INTERNET_MAX_URL_LENGTH, itemStatus.m_url);\r
457                 CPathUtils::Unescape(url);\r
458                 itemurl = UTF8ToWide(url);\r
459         }\r
460 }\r
461 \r