Merge branch 'bare-repo'
[TortoiseGit.git] / src / TortoiseShell / ColumnProvider.cpp
blob9ce1fcd269c7e8623f38bfcd71815fab0c131413
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.
20 #include "stdafx.h"
21 #include "ShellExt.h"
22 #include "guids.h"
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,
36 // for instance.
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
47 return S_FALSE;
49 ShellCache::CacheType cachetype = g_ShellCache.GetCacheType();
50 if (cachetype == ShellCache::none)
51 return S_FALSE;
53 LoadLangDll();
54 switch (dwIndex)
56 case 0: // Git Status
57 GetColumnInfo(dwIndex, psci, 15, IDS_COLTITLESTATUS, IDS_COLDESCSTATUS);
58 break;
59 case 1: // Git Revision
60 GetColumnInfo(dwIndex, psci, 40, IDS_COLTITLEREV, IDS_COLDESCREV);
61 break;
62 case 2: // Git Url
63 GetColumnInfo(dwIndex, psci, 30, IDS_COLTITLEURL, IDS_COLDESCURL);
64 break;
65 case 3: // Git Short Url
66 GetColumnInfo(dwIndex, psci, 30, IDS_COLTITLESHORTURL, IDS_COLDESCSHORTURL);
67 break;
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);
79 break;
80 case 5: // SVN eol-style
81 GetColumnInfo(dwIndex, psci, 30, IDS_COLTITLEEOLSTYLE, IDS_COLDESCEOLSTYLE);
82 break;
83 default:
84 return S_FALSE;
87 return S_OK;
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;
94 psci->vt = VT_BSTR;
95 psci->fmt = LVCFMT_LEFT;
96 psci->cChars = characterCount;
97 psci->csFlags = ColumnFlags;
99 MAKESTRING(title);
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))
110 return S_FALSE;
112 LoadLangDll();
113 ShellCache::CacheType cachetype = g_ShellCache.GetCacheType();
114 if (pscid->fmtid == CLSID_Tortoisegit_UPTODATE)
116 stdstring szInfo;
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);
123 switch (pscid->pid)
125 case 0: // Git Status
126 GetColumnStatus(path, pscd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
127 GitStatus::GetStatusString(g_hResInst, filestatus, buf, _countof(buf), (WORD)CRegStdDWORD(_T("Software\\TortoiseGit\\LanguageID"), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT)));
128 szInfo = buf;
129 break;
130 case 1: // Git Revision
131 #if 0
132 GetColumnStatus(path, pscd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
133 if (columnrev >= 0)
135 V_VT(pvarData) = VT_I4;
136 V_I4(pvarData) = columnrev;
138 #endif
139 return S_OK;
140 break;
141 case 2: // Git Url
142 GetColumnStatus(path, pscd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
143 szInfo = itemurl;
144 break;
145 case 3: // Git Short Url
146 GetColumnStatus(path, pscd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
147 szInfo = itemshorturl;
148 break;
149 case 5: // Git eol-style
150 #if 0
151 if (cachetype == ShellCache::none)
152 return S_FALSE;
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());
164 #endif
165 break;
166 default:
167 return S_FALSE;
169 const WCHAR * wsInfo = szInfo.c_str();
170 V_VT(pvarData) = VT_BSTR;
171 V_BSTR(pvarData) = SysAllocString(wsInfo);
172 return S_OK;
174 if (pscid->fmtid == FMTID_SummaryInformation)
176 stdstring szInfo;
177 const TCHAR * path = pscd->wszFile;
179 if (cachetype == ShellCache::none)
180 return S_FALSE;
181 switch (pscid->pid)
183 case PIDSI_AUTHOR: // Author and Git Author
184 GetColumnStatus(path, pscd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
185 szInfo = columnauthor;
186 break;
187 default:
188 return S_FALSE;
190 wide_string wsInfo = szInfo;
191 V_VT(pvarData) = VT_BSTR;
192 V_BSTR(pvarData) = SysAllocString(wsInfo.c_str());
193 return S_OK;
196 return S_FALSE;
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())
206 return S_OK;
207 std::wstring path = psci->wszFolder;
208 if (!path.empty())
210 if (! g_ShellCache.HasGITAdminDir(path.c_str(), TRUE))
211 return E_FAIL;
213 columnfilepath = _T("");
214 return S_OK;
217 void CShellExt::GetColumnStatus(const TCHAR * path, BOOL bIsDir)
219 PreserveChdir preserveChdir;
220 if (_tcscmp(path, columnfilepath.c_str())==0)
221 return;
222 LoadLangDll();
223 columnfilepath = path;
224 const FileStatusCacheEntry * status = NULL;
225 TGITCacheResponse itemStatus;
226 ShellCache::CacheType t = ShellCache::exe;
227 AutoLocker lock(g_csGlobalCOMGuard);
228 t = g_ShellCache.GetCacheType();
230 switch (t)
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);
239 else
241 filestatus = git_wc_status_none;
242 columnauthor.clear();
243 columnrev = GIT_INVALID_REVNUM;
244 itemurl.clear();
245 itemshorturl.clear();
246 owner.clear();
247 return;
250 break;
251 case ShellCache::dll:
252 case ShellCache::dllFull:
254 status = m_CachedStatus.GetFullStatus(CTGitPath(path), bIsDir, TRUE);
255 filestatus = status->status;
257 break;
258 default:
259 case ShellCache::none:
261 if (g_ShellCache.HasGITAdminDir(path, bIsDir))
262 filestatus = git_wc_status_normal;
263 else
264 filestatus = git_wc_status_none;
265 columnauthor.clear();
266 columnrev = GIT_INVALID_REVNUM;
267 itemurl.clear();
268 itemshorturl.clear();
269 owner.clear();
270 return;
272 break;
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);
282 else
284 if (status)
286 columnauthor = UTF8ToWide(status->author);
287 columnrev = status->rev;
288 itemurl = UTF8ToWide(status->url);
289 owner = UTF8ToWide(status->owner);
292 #if 0
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, '/');
306 if (ptr == NULL)
307 ptr = _tcsrchr(urlComponents.lpszUrlPath, '\\');
308 if (ptr)
310 *ptr = '\0';
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
316 // like :)
317 /*ptr = _tcsstr(urlComponents.lpszUrlPath, _T("/trunk"));
318 if (ptr == NULL)
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"));
323 if (ptr == NULL)
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"));
328 if (ptr == NULL)
329 ptr = _tcsstr(urlComponents.lpszUrlPath, _T("\\tags"));
330 if ((ptr)&&(*(ptr+5) != 0)&&(*(ptr+5) != '/')&&(*(ptr+5) != '\\'))
331 ptr = NULL;
334 if (ptr)
335 itemshorturl = ptr;
336 else*/
337 itemshorturl = urlComponents.lpszUrlPath;
339 else
340 itemshorturl = _T(" ");
342 else
343 itemshorturl = _T(" ");
345 if (status)
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);
359 #endif