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.
22 #include "PreserveChdir.h"
23 //#include "SVNProperties.h"
24 #include "UnicodeUtils.h"
25 #include "GitStatus.h"
26 #include "PathUtils.h"
27 #include "..\TGitCache\CacheInterface.h"
30 const static int ColumnFlags
= SHCOLSTATE_TYPE_STR
| SHCOLSTATE_ONBYDEFAULT
;
32 // Defines that revision numbers occupy at most MAX_REV_STRING_LEN characters.
33 // There are Perforce repositories out there that have several 100,000 revs.
34 // So, don't be too restrictive by limiting this to 6 digits + 1 separator,
37 // Because shorter strings will be extended to have exactly MAX_REV_STRING_LEN
38 // characters, large numbers will produce large strings. These, in turn, will
39 // affect column auto sizing. This setting is a reasonable compromise.
41 // Max rev correctly sorted: 99,999,999 for MAX_REV_STRING_LEN == 10
43 #define MAX_REV_STRING_LEN 10
45 // IColumnProvider members
46 STDMETHODIMP
CShellExt::GetColumnInfo(DWORD dwIndex
, SHCOLUMNINFO
*psci
)
48 PreserveChdir preserveChdir
;
52 ShellCache::CacheType cachetype
= g_ShellCache
.GetCacheType();
58 if (cachetype
== ShellCache::none
)
60 psci
->scid
.fmtid
= CLSID_Tortoisegit_UPTODATE
;
61 psci
->scid
.pid
= dwIndex
;
63 psci
->fmt
= LVCFMT_LEFT
;
65 psci
->csFlags
= ColumnFlags
;
67 MAKESTRING(IDS_COLTITLESTATUS
);
68 lstrcpynW(psci
->wszTitle
, stringtablebuffer
, MAX_COLUMN_NAME_LEN
);
69 MAKESTRING(IDS_COLDESCSTATUS
);
70 lstrcpynW(psci
->wszDescription
, stringtablebuffer
, MAX_COLUMN_DESC_LEN
);
72 case 1: // SVN Revision
73 if (cachetype
== ShellCache::none
)
75 psci
->scid
.fmtid
= CLSID_Tortoisegit_UPTODATE
;
76 psci
->scid
.pid
= dwIndex
;
78 psci
->fmt
= LVCFMT_RIGHT
;
80 psci
->csFlags
= SHCOLSTATE_TYPE_INT
| SHCOLSTATE_ONBYDEFAULT
;
82 MAKESTRING(IDS_COLTITLEREV
);
83 lstrcpynW(psci
->wszTitle
, stringtablebuffer
, MAX_COLUMN_NAME_LEN
);
84 MAKESTRING(IDS_COLDESCREV
);
85 lstrcpynW(psci
->wszDescription
, stringtablebuffer
, MAX_COLUMN_DESC_LEN
);
88 if (cachetype
== ShellCache::none
)
90 psci
->scid
.fmtid
= CLSID_Tortoisegit_UPTODATE
;
91 psci
->scid
.pid
= dwIndex
;
93 psci
->fmt
= LVCFMT_LEFT
;
95 psci
->csFlags
= ColumnFlags
;
97 MAKESTRING(IDS_COLTITLEURL
);
98 lstrcpynW(psci
->wszTitle
, stringtablebuffer
, MAX_COLUMN_NAME_LEN
);
99 MAKESTRING(IDS_COLDESCURL
);
100 lstrcpynW(psci
->wszDescription
, stringtablebuffer
, MAX_COLUMN_DESC_LEN
);
102 case 3: // SVN Short Url
103 if (cachetype
== ShellCache::none
)
105 psci
->scid
.fmtid
= CLSID_Tortoisegit_UPTODATE
;
106 psci
->scid
.pid
= dwIndex
;
108 psci
->fmt
= LVCFMT_LEFT
;
110 psci
->csFlags
= ColumnFlags
;
112 MAKESTRING(IDS_COLTITLESHORTURL
);
113 lstrcpynW(psci
->wszTitle
, stringtablebuffer
, MAX_COLUMN_NAME_LEN
);
114 MAKESTRING(IDS_COLDESCSHORTURL
);
115 lstrcpynW(psci
->wszDescription
, stringtablebuffer
, MAX_COLUMN_DESC_LEN
);
118 if (cachetype
== ShellCache::none
)
120 psci
->scid
.fmtid
= FMTID_SummaryInformation
; // predefined FMTID
121 psci
->scid
.pid
= PIDSI_AUTHOR
; // Predefined - author
122 psci
->vt
= VT_LPSTR
; // We'll return the data as a string
123 psci
->fmt
= LVCFMT_LEFT
; // Text will be left-aligned in the column
124 psci
->csFlags
= SHCOLSTATE_TYPE_STR
; // Data should be sorted as strings
125 psci
->cChars
= 32; // Default col width in chars
127 case 5: // SVN mime-type
128 if (cachetype
== ShellCache::none
)
130 psci
->scid
.fmtid
= CLSID_Tortoisegit_UPTODATE
;
131 psci
->scid
.pid
= dwIndex
;
133 psci
->fmt
= LVCFMT_LEFT
;
135 psci
->csFlags
= ColumnFlags
;
137 MAKESTRING(IDS_COLTITLEMIMETYPE
);
138 lstrcpynW(psci
->wszTitle
, stringtablebuffer
, MAX_COLUMN_NAME_LEN
);
139 MAKESTRING(IDS_COLDESCMIMETYPE
);
140 lstrcpynW(psci
->wszDescription
, stringtablebuffer
, MAX_COLUMN_DESC_LEN
);
142 case 6: // SVN Lock Owner
143 if (cachetype
== ShellCache::none
)
145 psci
->scid
.fmtid
= CLSID_Tortoisegit_UPTODATE
;
146 psci
->scid
.pid
= dwIndex
;
148 psci
->fmt
= LVCFMT_LEFT
;
150 psci
->csFlags
= ColumnFlags
;
152 MAKESTRING(IDS_COLTITLEOWNER
);
153 lstrcpynW(psci
->wszTitle
, stringtablebuffer
, MAX_COLUMN_NAME_LEN
);
154 MAKESTRING(IDS_COLDESCOWNER
);
155 lstrcpynW(psci
->wszDescription
, stringtablebuffer
, MAX_COLUMN_DESC_LEN
);
157 case 7: // SVN eol-style
158 if (cachetype
== ShellCache::none
)
160 psci
->scid
.fmtid
= CLSID_Tortoisegit_UPTODATE
;
161 psci
->scid
.pid
= dwIndex
;
163 psci
->fmt
= LVCFMT_LEFT
;
165 psci
->csFlags
= ColumnFlags
;
167 MAKESTRING(IDS_COLTITLEEOLSTYLE
);
168 lstrcpynW(psci
->wszTitle
, stringtablebuffer
, MAX_COLUMN_NAME_LEN
);
169 MAKESTRING(IDS_COLDESCEOLSTYLE
);
170 lstrcpynW(psci
->wszDescription
, stringtablebuffer
, MAX_COLUMN_DESC_LEN
);
172 case 8: // SVN Author
173 if (cachetype
== ShellCache::none
)
175 psci
->scid
.fmtid
= CLSID_Tortoisegit_UPTODATE
;
176 psci
->scid
.pid
= dwIndex
;
178 psci
->fmt
= LVCFMT_LEFT
;
180 psci
->csFlags
= ColumnFlags
;
182 MAKESTRING(IDS_COLTITLEAUTHOR
);
183 lstrcpynW(psci
->wszTitle
, stringtablebuffer
, MAX_COLUMN_NAME_LEN
);
184 MAKESTRING(IDS_COLDESCAUTHOR
);
185 lstrcpynW(psci
->wszDescription
, stringtablebuffer
, MAX_COLUMN_DESC_LEN
);
192 STDMETHODIMP
CShellExt::GetItemData(LPCSHCOLUMNID pscid
, LPCSHCOLUMNDATA pscd
, VARIANT
*pvarData
)
194 PreserveChdir preserveChdir
;
195 if (!g_ShellCache
.IsPathAllowed((TCHAR
*)pscd
->wszFile
))
200 ShellCache::CacheType cachetype
= g_ShellCache
.GetCacheType();
201 if (pscid
->fmtid
== CLSID_Tortoisegit_UPTODATE
&& pscid
->pid
< 8)
204 const TCHAR
* path
= (TCHAR
*)pscd
->wszFile
;
206 // reserve for the path + trailing \0
208 TCHAR buf
[MAX_STATUS_STRING_LENGTH
+1];
209 SecureZeroMemory(buf
, MAX_STATUS_STRING_LENGTH
);
212 case 0: // SVN Status
213 GetColumnStatus(path
, pscd
->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
);
214 GitStatus::GetStatusString(g_hResInst
, filestatus
, buf
, sizeof(buf
)/sizeof(TCHAR
), (WORD
)CRegStdWORD(_T("Software\\TortoiseGit\\LanguageID"), MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
)));
217 case 1: // SVN Revision
219 GetColumnStatus(path
, pscd
->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
);
222 V_VT(pvarData
) = VT_I4
;
223 V_I4(pvarData
) = columnrev
;
229 GetColumnStatus(path
, pscd
->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
);
232 case 3: // SVN Short Url
233 GetColumnStatus(path
, pscd
->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
);
234 szInfo
= itemshorturl
;
236 case 5: // SVN mime-type
238 if (cachetype
== ShellCache::none
)
240 if (g_ShellCache
.IsPathAllowed(path
))
242 SVNProperties props
= SVNProperties(CTSVNPath(path
), false);
243 for (int i
=0; i
<props
.GetCount(); i
++)
245 if (props
.GetItemName(i
).compare(_T("svn:mime-type"))==0)
247 szInfo
= MultibyteToWide((char *)props
.GetItemValue(i
).c_str());
253 case 6: // SVN Lock Owner
254 GetColumnStatus(path
, pscd
->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
);
257 case 7: // SVN eol-style
259 if (cachetype
== ShellCache::none
)
261 if (g_ShellCache
.IsPathAllowed(path
))
263 SVNProperties props
= SVNProperties(CTSVNPath(path
), false);
264 for (int i
=0; i
<props
.GetCount(); i
++)
266 if (props
.GetItemName(i
).compare(_T("svn:eol-style"))==0)
268 szInfo
= MultibyteToWide((char *)props
.GetItemValue(i
).c_str());
277 const WCHAR
* wsInfo
= szInfo
.c_str();
278 V_VT(pvarData
) = VT_BSTR
;
279 V_BSTR(pvarData
) = SysAllocString(wsInfo
);
282 if ((pscid
->fmtid
== FMTID_SummaryInformation
)||(pscid
->pid
== 8))
285 const TCHAR
* path
= pscd
->wszFile
;
287 if (cachetype
== ShellCache::none
)
291 case PIDSI_AUTHOR
: // author
293 GetColumnStatus(path
, pscd
->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
);
294 szInfo
= columnauthor
;
299 wide_string wsInfo
= szInfo
;
300 V_VT(pvarData
) = VT_BSTR
;
301 V_BSTR(pvarData
) = SysAllocString(wsInfo
.c_str());
308 STDMETHODIMP
CShellExt::Initialize(LPCSHCOLUMNINIT psci
)
310 // psci->wszFolder (WCHAR) holds the path to the folder to be displayed
311 // Should check to see if its a "SVN" folder and if not return E_FAIL
313 PreserveChdir preserveChdir
;
314 if (g_ShellCache
.IsColumnsEveryWhere())
316 std::wstring path
= psci
->wszFolder
;
319 if (! g_ShellCache
.HasSVNAdminDir(path
.c_str(), TRUE
))
322 columnfilepath
= _T("");
326 void CShellExt::GetColumnStatus(const TCHAR
* path
, BOOL bIsDir
)
329 PreserveChdir preserveChdir
;
330 if (_tcscmp(path
, columnfilepath
.c_str())==0)
333 columnfilepath
= path
;
334 const FileStatusCacheEntry
* status
= NULL
;
335 TSVNCacheResponse itemStatus
;
336 ShellCache::CacheType t
= ShellCache::exe
;
337 AutoLocker
lock(g_csGlobalCOMGuard
);
338 t
= g_ShellCache
.GetCacheType();
342 case ShellCache::exe
:
344 SecureZeroMemory(&itemStatus
, sizeof(itemStatus
));
345 if(m_remoteCacheLink
.GetStatusFromRemoteCache(CTGitPath(path
), &itemStatus
, true))
347 filestatus
= GitStatus::GetMoreImportant(itemStatus
.m_status
.text_status
, itemStatus
.m_status
.prop_status
);
351 filestatus
= git_wc_status_none
;
352 columnauthor
.clear();
353 columnrev
= GIT_INVALID_REVNUM
;
355 itemshorturl
.clear();
361 case ShellCache::dll
:
362 case ShellCache::dllFull
:
364 status
= m_CachedStatus
.GetFullStatus(CTGitPath(path
), bIsDir
, TRUE
);
365 filestatus
= status
->status
;
369 case ShellCache::none
:
371 if (g_ShellCache
.HasSVNAdminDir(path
, bIsDir
))
372 filestatus
= git_wc_status_normal
;
374 filestatus
= git_wc_status_none
;
375 columnauthor
.clear();
376 columnrev
= GIT_INVALID_REVNUM
;
378 itemshorturl
.clear();
385 if (t
== ShellCache::exe
)
387 columnauthor
= UTF8ToWide(itemStatus
.m_author
);
388 columnrev
= itemStatus
.m_entry
.cmt_rev
;
389 itemurl
= UTF8ToWide(itemStatus
.m_url
);
390 owner
= UTF8ToWide(itemStatus
.m_owner
);
396 columnauthor
= UTF8ToWide(status
->author
);
397 columnrev
= status
->rev
;
398 itemurl
= UTF8ToWide(status
->url
);
399 owner
= UTF8ToWide(status
->owner
);
402 TCHAR urlpath
[INTERNET_MAX_URL_LENGTH
+1];
404 URL_COMPONENTS urlComponents
;
405 memset(&urlComponents
, 0, sizeof(URL_COMPONENTS
));
406 urlComponents
.dwStructSize
= sizeof(URL_COMPONENTS
);
407 urlComponents
.dwUrlPathLength
= INTERNET_MAX_URL_LENGTH
;
408 urlComponents
.lpszUrlPath
= urlpath
;
409 if (InternetCrackUrl(itemurl
.c_str(), 0, ICU_DECODE
, &urlComponents
))
411 // since the short url is shown as an additional column where the
412 // file/foldername is shown too, we strip that name from the url
413 // to make the url even shorter.
414 TCHAR
* ptr
= _tcsrchr(urlComponents
.lpszUrlPath
, '/');
416 ptr
= _tcsrchr(urlComponents
.lpszUrlPath
, '\\');
420 // to shorten the url even more, we check for 'trunk', 'branches' and 'tags'
421 // and simply assume that these are the folders attached to the repository
422 // root. If we find those, we strip the whole path before those folders too.
423 // Note: this will strip too much if such a folder is *below* the repository
424 // root - but it's called 'short url' and we're free to shorten it the way we
426 /*ptr = _tcsstr(urlComponents.lpszUrlPath, _T("/trunk"));
428 ptr = _tcsstr(urlComponents.lpszUrlPath, _T("\\trunk"));
429 if ((ptr == NULL)||((*(ptr+6) != 0)&&(*(ptr+6) != '/')&&(*(ptr+6) != '\\')))
431 ptr = _tcsstr(urlComponents.lpszUrlPath, _T("/branches"));
433 ptr = _tcsstr(urlComponents.lpszUrlPath, _T("\\branches"));
434 if ((ptr == NULL)||((*(ptr+9) != 0)&&(*(ptr+9) != '/')&&(*(ptr+9) != '\\')))
436 ptr = _tcsstr(urlComponents.lpszUrlPath, _T("/tags"));
438 ptr = _tcsstr(urlComponents.lpszUrlPath, _T("\\tags"));
439 if ((ptr)&&(*(ptr+5) != 0)&&(*(ptr+5) != '/')&&(*(ptr+5) != '\\'))
446 itemshorturl
= urlComponents
.lpszUrlPath
;
449 itemshorturl
= _T(" ");
452 itemshorturl
= _T(" ");
456 char url
[INTERNET_MAX_URL_LENGTH
];
457 strcpy_s(url
, INTERNET_MAX_URL_LENGTH
, status
->url
);
458 CPathUtils::Unescape(url
);
459 itemurl
= UTF8ToWide(url
);
461 else if (t
== ShellCache::exe
)
463 char url
[INTERNET_MAX_URL_LENGTH
];
464 strcpy_s(url
, INTERNET_MAX_URL_LENGTH
, itemStatus
.m_url
);
465 CPathUtils::Unescape(url
);
466 itemurl
= UTF8ToWide(url
);