1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2003-2008 - TortoiseSVN
4 // Copyright (C) 2008-2011 - TortoiseGit
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software Foundation,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "PreserveChdir.h"
24 //#include "SVNProperties.h"
25 #include "UnicodeUtils.h"
26 #include "GitStatus.h"
27 #include "PathUtils.h"
28 #include "..\TGitCache\CacheInterface.h"
31 const static int ColumnFlags
= SHCOLSTATE_TYPE_STR
| SHCOLSTATE_ONBYDEFAULT
;
33 // Defines that revision numbers occupy at most MAX_REV_STRING_LEN characters.
34 // There are Perforce repositories out there that have several 100,000 revs.
35 // So, don't be too restrictive by limiting this to 6 digits + 1 separator,
38 // Because shorter strings will be extended to have exactly MAX_REV_STRING_LEN
39 // characters, large numbers will produce large strings. These, in turn, will
40 // affect column auto sizing. This setting is a reasonable compromise.
42 // IColumnProvider members
43 STDMETHODIMP
CShellExt::GetColumnInfo(DWORD dwIndex
, SHCOLUMNINFO
*psci
)
45 PreserveChdir preserveChdir
;
46 if (dwIndex
> 0) // TODO: keep for now to be able to hide unimplemented columns
49 ShellCache::CacheType cachetype
= g_ShellCache
.GetCacheType();
50 if (cachetype
== ShellCache::none
)
57 GetColumnInfo(dwIndex
, psci
, 15, IDS_COLTITLESTATUS
, IDS_COLDESCSTATUS
);
59 case 1: // Git Revision
60 GetColumnInfo(dwIndex
, psci
, 40, IDS_COLTITLEREV
, IDS_COLDESCREV
);
63 GetColumnInfo(dwIndex
, psci
, 30, IDS_COLTITLEURL
, IDS_COLDESCURL
);
65 case 3: // Git Short Url
66 GetColumnInfo(dwIndex
, psci
, 30, IDS_COLTITLESHORTURL
, IDS_COLDESCSHORTURL
);
68 case 4: // Author and Git Author
69 psci
->scid
.fmtid
= FMTID_SummaryInformation
; // predefined FMTID
70 psci
->scid
.pid
= PIDSI_AUTHOR
; // Predefined - author
71 psci
->vt
= VT_LPSTR
; // We'll return the data as a string
72 psci
->fmt
= LVCFMT_LEFT
; // Text will be left-aligned in the column
73 psci
->csFlags
= SHCOLSTATE_TYPE_STR
; // Data should be sorted as strings
74 psci
->cChars
= 32; // Default col width in chars
75 MAKESTRING(IDS_COLTITLEAUTHOR
);
76 lstrcpynW(psci
->wszTitle
, stringtablebuffer
, MAX_COLUMN_NAME_LEN
);
77 MAKESTRING(IDS_COLDESCAUTHOR
);
78 lstrcpynW(psci
->wszDescription
, stringtablebuffer
, MAX_COLUMN_DESC_LEN
);
80 case 5: // SVN eol-style
81 GetColumnInfo(dwIndex
, psci
, 30, IDS_COLTITLEEOLSTYLE
, IDS_COLDESCEOLSTYLE
);
90 void CShellExt::GetColumnInfo(DWORD dwIndex
, SHCOLUMNINFO
*psci
, UINT characterCount
, UINT title
, UINT description
)
92 psci
->scid
.fmtid
= CLSID_Tortoisegit_UPTODATE
;
93 psci
->scid
.pid
= dwIndex
;
95 psci
->fmt
= LVCFMT_LEFT
;
96 psci
->cChars
= characterCount
;
97 psci
->csFlags
= ColumnFlags
;
100 lstrcpynW(psci
->wszTitle
, stringtablebuffer
, MAX_COLUMN_NAME_LEN
);
101 MAKESTRING(description
);
102 lstrcpynW(psci
->wszDescription
, stringtablebuffer
, MAX_COLUMN_DESC_LEN
);
105 STDMETHODIMP
CShellExt::GetItemData(LPCSHCOLUMNID pscid
, LPCSHCOLUMNDATA pscd
, VARIANT
*pvarData
)
107 PreserveChdir preserveChdir
;
108 if (!g_ShellCache
.IsPathAllowed((TCHAR
*)pscd
->wszFile
))
113 ShellCache::CacheType cachetype
= g_ShellCache
.GetCacheType();
114 if (pscid
->fmtid
== CLSID_Tortoisegit_UPTODATE
)
117 const TCHAR
* path
= (TCHAR
*)pscd
->wszFile
;
119 // reserve for the path + trailing \0
121 TCHAR buf
[MAX_STATUS_STRING_LENGTH
+1];
122 SecureZeroMemory(buf
, MAX_STATUS_STRING_LENGTH
);
125 case 0: // Git Status
126 GetColumnStatus(path
, pscd
->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
);
127 GitStatus::GetStatusString(g_hResInst
, filestatus
, buf
, sizeof(buf
)/sizeof(TCHAR
), (WORD
)CRegStdDWORD(_T("Software\\TortoiseGit\\LanguageID"), MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
)));
130 case 1: // Git Revision
132 GetColumnStatus(path
, pscd
->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
);
135 V_VT(pvarData
) = VT_I4
;
136 V_I4(pvarData
) = columnrev
;
142 GetColumnStatus(path
, pscd
->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
);
145 case 3: // Git Short Url
146 GetColumnStatus(path
, pscd
->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
);
147 szInfo
= itemshorturl
;
149 case 5: // Git eol-style
151 if (cachetype
== ShellCache::none
)
153 if (g_ShellCache
.IsPathAllowed(path
))
155 SVNProperties props
= SVNProperties(CTSVNPath(path
), false);
156 for (int i
=0; i
<props
.GetCount(); i
++)
158 if (props
.GetItemName(i
).compare(_T("svn:eol-style"))==0)
160 szInfo
= MultibyteToWide((char *)props
.GetItemValue(i
).c_str());
169 const WCHAR
* wsInfo
= szInfo
.c_str();
170 V_VT(pvarData
) = VT_BSTR
;
171 V_BSTR(pvarData
) = SysAllocString(wsInfo
);
174 if (pscid
->fmtid
== FMTID_SummaryInformation
)
177 const TCHAR
* path
= pscd
->wszFile
;
179 if (cachetype
== ShellCache::none
)
183 case PIDSI_AUTHOR
: // Author and Git Author
184 GetColumnStatus(path
, pscd
->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
);
185 szInfo
= columnauthor
;
190 wide_string wsInfo
= szInfo
;
191 V_VT(pvarData
) = VT_BSTR
;
192 V_BSTR(pvarData
) = SysAllocString(wsInfo
.c_str());
199 STDMETHODIMP
CShellExt::Initialize(LPCSHCOLUMNINIT psci
)
201 // psci->wszFolder (WCHAR) holds the path to the folder to be displayed
202 // Should check to see if its a "SVN" folder and if not return E_FAIL
204 PreserveChdir preserveChdir
;
205 if (g_ShellCache
.IsColumnsEveryWhere())
207 std::wstring path
= psci
->wszFolder
;
210 if (! g_ShellCache
.HasSVNAdminDir(path
.c_str(), TRUE
))
213 columnfilepath
= _T("");
217 void CShellExt::GetColumnStatus(const TCHAR
* path
, BOOL bIsDir
)
219 PreserveChdir preserveChdir
;
220 if (_tcscmp(path
, columnfilepath
.c_str())==0)
223 columnfilepath
= path
;
224 const FileStatusCacheEntry
* status
= NULL
;
225 TSVNCacheResponse itemStatus
;
226 ShellCache::CacheType t
= ShellCache::exe
;
227 AutoLocker
lock(g_csGlobalCOMGuard
);
228 t
= g_ShellCache
.GetCacheType();
232 case ShellCache::exe
:
234 SecureZeroMemory(&itemStatus
, sizeof(itemStatus
));
235 if(m_remoteCacheLink
.GetStatusFromRemoteCache(CTGitPath(path
), &itemStatus
, true))
237 filestatus
= GitStatus::GetMoreImportant(itemStatus
.m_status
.text_status
, itemStatus
.m_status
.prop_status
);
241 filestatus
= git_wc_status_none
;
242 columnauthor
.clear();
243 columnrev
= GIT_INVALID_REVNUM
;
245 itemshorturl
.clear();
251 case ShellCache::dll
:
252 case ShellCache::dllFull
:
254 status
= m_CachedStatus
.GetFullStatus(CTGitPath(path
), bIsDir
, TRUE
);
255 filestatus
= status
->status
;
259 case ShellCache::none
:
261 if (g_ShellCache
.HasSVNAdminDir(path
, bIsDir
))
262 filestatus
= git_wc_status_normal
;
264 filestatus
= git_wc_status_none
;
265 columnauthor
.clear();
266 columnrev
= GIT_INVALID_REVNUM
;
268 itemshorturl
.clear();
275 if (t
== ShellCache::exe
)
277 columnauthor
= UTF8ToWide(itemStatus
.m_author
);
278 columnrev
= itemStatus
.m_entry
.cmt_rev
;
279 itemurl
= UTF8ToWide(itemStatus
.m_url
);
280 owner
= UTF8ToWide(itemStatus
.m_owner
);
286 columnauthor
= UTF8ToWide(status
->author
);
287 columnrev
= status
->rev
;
288 itemurl
= UTF8ToWide(status
->url
);
289 owner
= UTF8ToWide(status
->owner
);
293 TCHAR urlpath
[INTERNET_MAX_URL_LENGTH
+1];
295 URL_COMPONENTS urlComponents
;
296 memset(&urlComponents
, 0, sizeof(URL_COMPONENTS
));
297 urlComponents
.dwStructSize
= sizeof(URL_COMPONENTS
);
298 urlComponents
.dwUrlPathLength
= INTERNET_MAX_URL_LENGTH
;
299 urlComponents
.lpszUrlPath
= urlpath
;
300 if (InternetCrackUrl(itemurl
.c_str(), 0, ICU_DECODE
, &urlComponents
))
302 // since the short url is shown as an additional column where the
303 // file/foldername is shown too, we strip that name from the url
304 // to make the url even shorter.
305 TCHAR
* ptr
= _tcsrchr(urlComponents
.lpszUrlPath
, '/');
307 ptr
= _tcsrchr(urlComponents
.lpszUrlPath
, '\\');
311 // to shorten the url even more, we check for 'trunk', 'branches' and 'tags'
312 // and simply assume that these are the folders attached to the repository
313 // root. If we find those, we strip the whole path before those folders too.
314 // Note: this will strip too much if such a folder is *below* the repository
315 // root - but it's called 'short url' and we're free to shorten it the way we
317 /*ptr = _tcsstr(urlComponents.lpszUrlPath, _T("/trunk"));
319 ptr = _tcsstr(urlComponents.lpszUrlPath, _T("\\trunk"));
320 if ((ptr == NULL)||((*(ptr+6) != 0)&&(*(ptr+6) != '/')&&(*(ptr+6) != '\\')))
322 ptr = _tcsstr(urlComponents.lpszUrlPath, _T("/branches"));
324 ptr = _tcsstr(urlComponents.lpszUrlPath, _T("\\branches"));
325 if ((ptr == NULL)||((*(ptr+9) != 0)&&(*(ptr+9) != '/')&&(*(ptr+9) != '\\')))
327 ptr = _tcsstr(urlComponents.lpszUrlPath, _T("/tags"));
329 ptr = _tcsstr(urlComponents.lpszUrlPath, _T("\\tags"));
330 if ((ptr)&&(*(ptr+5) != 0)&&(*(ptr+5) != '/')&&(*(ptr+5) != '\\'))
337 itemshorturl
= urlComponents
.lpszUrlPath
;
340 itemshorturl
= _T(" ");
343 itemshorturl
= _T(" ");
347 char url
[INTERNET_MAX_URL_LENGTH
];
348 strcpy_s(url
, INTERNET_MAX_URL_LENGTH
, status
->url
);
349 CPathUtils::Unescape(url
);
350 itemurl
= UTF8ToWide(url
);
352 else if (t
== ShellCache::exe
)
354 char url
[INTERNET_MAX_URL_LENGTH
];
355 strcpy_s(url
, INTERNET_MAX_URL_LENGTH
, itemStatus
.m_url
);
356 CPathUtils::Unescape(url
);
357 itemurl
= UTF8ToWide(url
);