OSDN Git Service

Init Version
authorFrank Li <Frank.li@freescale.com>
Wed, 5 Nov 2008 13:57:36 +0000 (21:57 +0800)
committerFrank Li <Frank.li@freescale.com>
Wed, 5 Nov 2008 13:57:36 +0000 (21:57 +0800)
36 files changed:
TortoiseShell/ColumnProvider.cpp [new file with mode: 0644]
TortoiseShell/ContextMenu.cpp [new file with mode: 0644]
TortoiseShell/Documentation.h [new file with mode: 0644]
TortoiseShell/Globals.h [new file with mode: 0644]
TortoiseShell/Guids.h [new file with mode: 0644]
TortoiseShell/IconOverlay.cpp [new file with mode: 0644]
TortoiseShell/ItemIDList.cpp [new file with mode: 0644]
TortoiseShell/ItemIDList.h [new file with mode: 0644]
TortoiseShell/PIDL.cpp [new file with mode: 0644]
TortoiseShell/PIDL.h [new file with mode: 0644]
TortoiseShell/PreserveChdir.cpp [new file with mode: 0644]
TortoiseShell/PreserveChdir.h [new file with mode: 0644]
TortoiseShell/RemoteCacheLink.cpp [new file with mode: 0644]
TortoiseShell/RemoteCacheLink.h [new file with mode: 0644]
TortoiseShell/SVNPropertyPage.cpp [new file with mode: 0644]
TortoiseShell/SVNPropertyPage.h [new file with mode: 0644]
TortoiseShell/ShellCache.h [new file with mode: 0644]
TortoiseShell/ShellExt.cpp [new file with mode: 0644]
TortoiseShell/ShellExt.def [new file with mode: 0644]
TortoiseShell/ShellExt.h [new file with mode: 0644]
TortoiseShell/ShellExtClassFactory.cpp [new file with mode: 0644]
TortoiseShell/ShellExtClassFactory.h [new file with mode: 0644]
TortoiseShell/TortoiseSVN.cpp [new file with mode: 0644]
TortoiseShell/TortoiseShell.vcproj [new file with mode: 0644]
TortoiseShell/TortoiseStub.c [new file with mode: 0644]
TortoiseShell/TortoiseStub.def [new file with mode: 0644]
TortoiseShell/TortoiseStub.rc [new file with mode: 0644]
TortoiseShell/TortoiseStub.vcproj [new file with mode: 0644]
TortoiseShell/deregister.registry [new file with mode: 0644]
TortoiseShell/register.registry [new file with mode: 0644]
TortoiseShell/registerrelease.registry [new file with mode: 0644]
TortoiseShell/resource.h [new file with mode: 0644]
TortoiseShell/resource.rc [new file with mode: 0644]
TortoiseShell/resource.rc2 [new file with mode: 0644]
TortoiseShell/stdafx.cpp [new file with mode: 0644]
TortoiseShell/stdafx.h [new file with mode: 0644]

diff --git a/TortoiseShell/ColumnProvider.cpp b/TortoiseShell/ColumnProvider.cpp
new file mode 100644 (file)
index 0000000..b48671a
--- /dev/null
@@ -0,0 +1,461 @@
+// TortoiseSVN - a Windows shell extension for easy version control\r
+\r
+// Copyright (C) 2003-2008 - TortoiseSVN\r
+\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software Foundation,\r
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+//\r
+#include "stdafx.h"\r
+#include "ShellExt.h"\r
+#include "guids.h"\r
+#include "PreserveChdir.h"\r
+#include "SVNProperties.h"\r
+#include "UnicodeUtils.h"\r
+#include "SVNStatus.h"\r
+#include "PathUtils.h"\r
+#include "..\TSVNCache\CacheInterface.h"\r
+\r
+\r
+const static int ColumnFlags = SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT;\r
+\r
+// Defines that revision numbers occupy at most MAX_REV_STRING_LEN characters.\r
+// There are Perforce repositories out there that have several 100,000 revs.\r
+// So, don't be too restrictive by limiting this to 6 digits + 1 separator, \r
+// for instance. \r
+//\r
+// Because shorter strings will be extended to have exactly MAX_REV_STRING_LEN \r
+// characters, large numbers will produce large strings. These, in turn, will\r
+// affect column auto sizing. This setting is a reasonable compromise.\r
+//\r
+// Max rev correctly sorted: 99,999,999 for MAX_REV_STRING_LEN == 10\r
+\r
+#define MAX_REV_STRING_LEN 10\r
+\r
+// IColumnProvider members\r
+STDMETHODIMP CShellExt::GetColumnInfo(DWORD dwIndex, SHCOLUMNINFO *psci)\r
+{\r
+       PreserveChdir preserveChdir;\r
+       if (dwIndex > 8)\r
+               return S_FALSE;\r
+\r
+       ShellCache::CacheType cachetype = g_ShellCache.GetCacheType();\r
+       LoadLangDll();\r
+       wide_string ws;\r
+       switch (dwIndex)\r
+       {\r
+               case 0: // SVN Status\r
+                       if (cachetype == ShellCache::none)\r
+                               return S_FALSE;\r
+                       psci->scid.fmtid = CLSID_TortoiseSVN_UPTODATE;\r
+                       psci->scid.pid = dwIndex;\r
+                       psci->vt = VT_BSTR;\r
+                       psci->fmt = LVCFMT_LEFT;\r
+                       psci->cChars = 15;\r
+                       psci->csFlags = ColumnFlags;\r
+\r
+                       MAKESTRING(IDS_COLTITLESTATUS);\r
+                       lstrcpynW(psci->wszTitle, stringtablebuffer, MAX_COLUMN_NAME_LEN);\r
+                       MAKESTRING(IDS_COLDESCSTATUS);\r
+                       lstrcpynW(psci->wszDescription, stringtablebuffer, MAX_COLUMN_DESC_LEN);\r
+                       break;\r
+               case 1: // SVN Revision\r
+                       if (cachetype == ShellCache::none)\r
+                               return S_FALSE;\r
+                       psci->scid.fmtid = CLSID_TortoiseSVN_UPTODATE;\r
+                       psci->scid.pid = dwIndex;\r
+                       psci->vt = VT_I4;\r
+                       psci->fmt = LVCFMT_RIGHT;\r
+                       psci->cChars = 15;\r
+                       psci->csFlags = SHCOLSTATE_TYPE_INT | SHCOLSTATE_ONBYDEFAULT;\r
+\r
+                       MAKESTRING(IDS_COLTITLEREV);\r
+                       lstrcpynW(psci->wszTitle, stringtablebuffer, MAX_COLUMN_NAME_LEN);\r
+                       MAKESTRING(IDS_COLDESCREV);\r
+                       lstrcpynW(psci->wszDescription, stringtablebuffer, MAX_COLUMN_DESC_LEN);\r
+                       break;\r
+               case 2: // SVN Url\r
+                       if (cachetype == ShellCache::none)\r
+                               return S_FALSE;\r
+                       psci->scid.fmtid = CLSID_TortoiseSVN_UPTODATE;\r
+                       psci->scid.pid = dwIndex;\r
+                       psci->vt = VT_BSTR;\r
+                       psci->fmt = LVCFMT_LEFT;\r
+                       psci->cChars = 30;\r
+                       psci->csFlags = ColumnFlags;\r
+\r
+                       MAKESTRING(IDS_COLTITLEURL);\r
+                       lstrcpynW(psci->wszTitle, stringtablebuffer, MAX_COLUMN_NAME_LEN);\r
+                       MAKESTRING(IDS_COLDESCURL);\r
+                       lstrcpynW(psci->wszDescription, stringtablebuffer, MAX_COLUMN_DESC_LEN);\r
+                       break;\r
+               case 3: // SVN Short Url\r
+                       if (cachetype == ShellCache::none)\r
+                               return S_FALSE;\r
+                       psci->scid.fmtid = CLSID_TortoiseSVN_UPTODATE;\r
+                       psci->scid.pid = dwIndex;\r
+                       psci->vt = VT_BSTR;\r
+                       psci->fmt = LVCFMT_LEFT;\r
+                       psci->cChars = 30;\r
+                       psci->csFlags = ColumnFlags;\r
+\r
+                       MAKESTRING(IDS_COLTITLESHORTURL);\r
+                       lstrcpynW(psci->wszTitle, stringtablebuffer, MAX_COLUMN_NAME_LEN);\r
+                       MAKESTRING(IDS_COLDESCSHORTURL);\r
+                       lstrcpynW(psci->wszDescription, stringtablebuffer, MAX_COLUMN_DESC_LEN);\r
+                       break;\r
+               case 4: // Author\r
+                       if (cachetype == ShellCache::none)\r
+                               return S_FALSE;\r
+                       psci->scid.fmtid = FMTID_SummaryInformation;    // predefined FMTID\r
+                       psci->scid.pid   = PIDSI_AUTHOR;                                // Predefined - author\r
+                       psci->vt         = VT_LPSTR;                                    // We'll return the data as a string\r
+                       psci->fmt        = LVCFMT_LEFT;                                 // Text will be left-aligned in the column\r
+                       psci->csFlags    = SHCOLSTATE_TYPE_STR;                 // Data should be sorted as strings\r
+                       psci->cChars     = 32;                                                  // Default col width in chars\r
+                       break;\r
+               case 5: // SVN mime-type\r
+                       if (cachetype == ShellCache::none)\r
+                               return S_FALSE;\r
+                       psci->scid.fmtid = CLSID_TortoiseSVN_UPTODATE;\r
+                       psci->scid.pid = dwIndex;\r
+                       psci->vt = VT_BSTR;\r
+                       psci->fmt = LVCFMT_LEFT;\r
+                       psci->cChars = 30;\r
+                       psci->csFlags = ColumnFlags;\r
+\r
+                       MAKESTRING(IDS_COLTITLEMIMETYPE);\r
+                       lstrcpynW(psci->wszTitle, stringtablebuffer, MAX_COLUMN_NAME_LEN);\r
+                       MAKESTRING(IDS_COLDESCMIMETYPE);\r
+                       lstrcpynW(psci->wszDescription, stringtablebuffer, MAX_COLUMN_DESC_LEN);\r
+                       break;\r
+               case 6: // SVN Lock Owner\r
+                       if (cachetype == ShellCache::none)\r
+                               return S_FALSE;\r
+                       psci->scid.fmtid = CLSID_TortoiseSVN_UPTODATE;\r
+                       psci->scid.pid = dwIndex;\r
+                       psci->vt = VT_BSTR;\r
+                       psci->fmt = LVCFMT_LEFT;\r
+                       psci->cChars = 30;\r
+                       psci->csFlags = ColumnFlags;\r
+\r
+                       MAKESTRING(IDS_COLTITLEOWNER);\r
+                       lstrcpynW(psci->wszTitle, stringtablebuffer, MAX_COLUMN_NAME_LEN);\r
+                       MAKESTRING(IDS_COLDESCOWNER);\r
+                       lstrcpynW(psci->wszDescription, stringtablebuffer, MAX_COLUMN_DESC_LEN);\r
+                       break;\r
+               case 7: // SVN eol-style\r
+                       if (cachetype == ShellCache::none)\r
+                               return S_FALSE;\r
+                       psci->scid.fmtid = CLSID_TortoiseSVN_UPTODATE;\r
+                       psci->scid.pid = dwIndex;\r
+                       psci->vt = VT_BSTR;\r
+                       psci->fmt = LVCFMT_LEFT;\r
+                       psci->cChars = 30;\r
+                       psci->csFlags = ColumnFlags;\r
+\r
+                       MAKESTRING(IDS_COLTITLEEOLSTYLE);\r
+                       lstrcpynW(psci->wszTitle, stringtablebuffer, MAX_COLUMN_NAME_LEN);\r
+                       MAKESTRING(IDS_COLDESCEOLSTYLE);\r
+                       lstrcpynW(psci->wszDescription, stringtablebuffer, MAX_COLUMN_DESC_LEN);\r
+                       break;\r
+               case 8: // SVN Author\r
+                       if (cachetype == ShellCache::none)\r
+                               return S_FALSE;\r
+                       psci->scid.fmtid = CLSID_TortoiseSVN_UPTODATE;\r
+                       psci->scid.pid = dwIndex;\r
+                       psci->vt = VT_BSTR;\r
+                       psci->fmt = LVCFMT_LEFT;\r
+                       psci->cChars = 30;\r
+                       psci->csFlags = ColumnFlags;\r
+\r
+                       MAKESTRING(IDS_COLTITLEAUTHOR);\r
+                       lstrcpynW(psci->wszTitle, stringtablebuffer, MAX_COLUMN_NAME_LEN);\r
+                       MAKESTRING(IDS_COLDESCAUTHOR);\r
+                       lstrcpynW(psci->wszDescription, stringtablebuffer, MAX_COLUMN_DESC_LEN);\r
+                       break;\r
+       }\r
+\r
+       return S_OK;\r
+}\r
+\r
+STDMETHODIMP CShellExt::GetItemData(LPCSHCOLUMNID pscid, LPCSHCOLUMNDATA pscd, VARIANT *pvarData)\r
+{\r
+       PreserveChdir preserveChdir;\r
+       if (!g_ShellCache.IsPathAllowed((TCHAR *)pscd->wszFile))\r
+       {\r
+               return S_FALSE;\r
+       }\r
+       LoadLangDll();\r
+       ShellCache::CacheType cachetype = g_ShellCache.GetCacheType();\r
+       if (pscid->fmtid == CLSID_TortoiseSVN_UPTODATE && pscid->pid < 8) \r
+       {\r
+               stdstring szInfo;\r
+               const TCHAR * path = (TCHAR *)pscd->wszFile;\r
+\r
+               // reserve for the path + trailing \0\r
+\r
+               TCHAR buf[MAX_STATUS_STRING_LENGTH+1];\r
+               SecureZeroMemory(buf, MAX_STATUS_STRING_LENGTH);\r
+               switch (pscid->pid) \r
+               {\r
+                       case 0: // SVN Status\r
+                               GetColumnStatus(path, pscd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);\r
+                               SVNStatus::GetStatusString(g_hResInst, filestatus, buf, sizeof(buf)/sizeof(TCHAR), (WORD)CRegStdWORD(_T("Software\\TortoiseSVN\\LanguageID"), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT)));\r
+                               szInfo = buf;\r
+                               break;\r
+                       case 1: // SVN Revision\r
+                               GetColumnStatus(path, pscd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);\r
+                               if (columnrev >= 0)\r
+                               {\r
+                                       V_VT(pvarData) = VT_I4;\r
+                                       V_I4(pvarData) = columnrev;\r
+                               }\r
+                               return S_OK;\r
+                               break;\r
+                       case 2: // SVN Url\r
+                               GetColumnStatus(path, pscd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);\r
+                               szInfo = itemurl;\r
+                               break;\r
+                       case 3: // SVN Short Url\r
+                               GetColumnStatus(path, pscd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);\r
+                               szInfo = itemshorturl;\r
+                               break;\r
+                       case 5: // SVN mime-type\r
+                               if (cachetype == ShellCache::none)\r
+                                       return S_FALSE;\r
+                               if (g_ShellCache.IsPathAllowed(path))\r
+                               {\r
+                                       SVNProperties props = SVNProperties(CTSVNPath(path), false);\r
+                                       for (int i=0; i<props.GetCount(); i++)\r
+                                       {\r
+                                               if (props.GetItemName(i).compare(_T("svn:mime-type"))==0)\r
+                                               {\r
+                                                       szInfo = MultibyteToWide((char *)props.GetItemValue(i).c_str());\r
+                                               }\r
+                                       }\r
+                               }\r
+                               break;\r
+                       case 6: // SVN Lock Owner\r
+                               GetColumnStatus(path, pscd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);\r
+                               szInfo = owner;\r
+                               break;\r
+                       case 7: // SVN eol-style\r
+                               if (cachetype == ShellCache::none)\r
+                                       return S_FALSE;\r
+                               if (g_ShellCache.IsPathAllowed(path))\r
+                               {\r
+                                       SVNProperties props = SVNProperties(CTSVNPath(path), false);\r
+                                       for (int i=0; i<props.GetCount(); i++)\r
+                                       {\r
+                                               if (props.GetItemName(i).compare(_T("svn:eol-style"))==0)\r
+                                               {\r
+                                                       szInfo = MultibyteToWide((char *)props.GetItemValue(i).c_str());\r
+                                               }\r
+                                       }\r
+                               }\r
+                               break;\r
+                       default:\r
+                               return S_FALSE;\r
+               }\r
+               const WCHAR * wsInfo = szInfo.c_str();\r
+               V_VT(pvarData) = VT_BSTR;\r
+               V_BSTR(pvarData) = SysAllocString(wsInfo);\r
+               return S_OK;\r
+       }\r
+       if ((pscid->fmtid == FMTID_SummaryInformation)||(pscid->pid == 8))\r
+       {\r
+               stdstring szInfo;\r
+               const TCHAR * path = pscd->wszFile;\r
+\r
+               if (cachetype == ShellCache::none)\r
+                       return S_FALSE;\r
+               switch (pscid->pid)\r
+               {\r
+               case PIDSI_AUTHOR:                      // author\r
+               case 8:\r
+                       GetColumnStatus(path, pscd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);\r
+                       szInfo = columnauthor;\r
+                       break;\r
+               default:\r
+                       return S_FALSE;\r
+               }\r
+               wide_string wsInfo = szInfo;\r
+               V_VT(pvarData) = VT_BSTR;\r
+               V_BSTR(pvarData) = SysAllocString(wsInfo.c_str());\r
+               return S_OK;\r
+       }\r
+\r
+       return S_FALSE;\r
+}\r
+\r
+STDMETHODIMP CShellExt::Initialize(LPCSHCOLUMNINIT psci)\r
+{\r
+       // psci->wszFolder (WCHAR) holds the path to the folder to be displayed\r
+       // Should check to see if its a "SVN" folder and if not return E_FAIL\r
+\r
+       PreserveChdir preserveChdir;\r
+       if (g_ShellCache.IsColumnsEveryWhere())\r
+               return S_OK;\r
+       std::wstring path = psci->wszFolder;\r
+       if (!path.empty())\r
+       {\r
+               if (! g_ShellCache.HasSVNAdminDir(path.c_str(), TRUE))\r
+                       return E_FAIL;\r
+       }\r
+       columnfilepath = _T("");\r
+       return S_OK;\r
+}\r
+\r
+void CShellExt::GetColumnStatus(const TCHAR * path, BOOL bIsDir)\r
+{\r
+       PreserveChdir preserveChdir;\r
+       if (_tcscmp(path, columnfilepath.c_str())==0)\r
+               return;\r
+       LoadLangDll();\r
+       columnfilepath = path;\r
+       const FileStatusCacheEntry * status = NULL;\r
+       TSVNCacheResponse itemStatus;\r
+       ShellCache::CacheType t = ShellCache::exe;\r
+       AutoLocker lock(g_csGlobalCOMGuard);\r
+       t = g_ShellCache.GetCacheType();\r
+\r
+       switch (t)\r
+       {\r
+       case ShellCache::exe:\r
+               {\r
+                       SecureZeroMemory(&itemStatus, sizeof(itemStatus));\r
+                       if(m_remoteCacheLink.GetStatusFromRemoteCache(CTSVNPath(path), &itemStatus, true))\r
+                       {\r
+                               filestatus = SVNStatus::GetMoreImportant(itemStatus.m_status.text_status, itemStatus.m_status.prop_status);\r
+                       }\r
+                       else\r
+                       {\r
+                               filestatus = svn_wc_status_none;\r
+                               columnauthor.clear();\r
+                               columnrev = 0;\r
+                               itemurl.clear();\r
+                               itemshorturl.clear();\r
+                               owner.clear();\r
+                               return; \r
+                       }\r
+               }\r
+               break;\r
+       case ShellCache::dll:\r
+               {\r
+                       status = m_CachedStatus.GetFullStatus(CTSVNPath(path), bIsDir, TRUE);\r
+                       filestatus = status->status;\r
+               }\r
+               break;\r
+       default:\r
+       case ShellCache::none:\r
+               {\r
+                       if (g_ShellCache.HasSVNAdminDir(path, bIsDir))\r
+                               filestatus = svn_wc_status_normal;\r
+                       else\r
+                               filestatus = svn_wc_status_none;\r
+                       columnauthor.clear();\r
+                       columnrev = 0;\r
+                       itemurl.clear();\r
+                       itemshorturl.clear();\r
+                       owner.clear();\r
+                       return; \r
+               }\r
+               break;\r
+       }\r
+\r
+       if (t == ShellCache::exe)\r
+       {\r
+               columnauthor = UTF8ToWide(itemStatus.m_author);\r
+               columnrev = itemStatus.m_entry.cmt_rev;\r
+               itemurl = UTF8ToWide(itemStatus.m_url);\r
+               owner = UTF8ToWide(itemStatus.m_owner);\r
+       }\r
+       else\r
+       {\r
+               if (status)\r
+               {\r
+                       columnauthor = UTF8ToWide(status->author);\r
+                       columnrev = status->rev;\r
+                       itemurl = UTF8ToWide(status->url);\r
+                       owner = UTF8ToWide(status->owner);\r
+               }\r
+       }\r
+       TCHAR urlpath[INTERNET_MAX_URL_LENGTH+1];\r
+\r
+       URL_COMPONENTS urlComponents;\r
+       memset(&urlComponents, 0, sizeof(URL_COMPONENTS));\r
+       urlComponents.dwStructSize = sizeof(URL_COMPONENTS);\r
+       urlComponents.dwUrlPathLength = INTERNET_MAX_URL_LENGTH;\r
+       urlComponents.lpszUrlPath = urlpath;\r
+       if (InternetCrackUrl(itemurl.c_str(), 0, ICU_DECODE, &urlComponents))\r
+       {\r
+               // since the short url is shown as an additional column where the\r
+               // file/foldername is shown too, we strip that name from the url\r
+               // to make the url even shorter.\r
+               TCHAR * ptr = _tcsrchr(urlComponents.lpszUrlPath, '/');\r
+               if (ptr == NULL)\r
+                       ptr = _tcsrchr(urlComponents.lpszUrlPath, '\\');\r
+               if (ptr)\r
+               {\r
+                       *ptr = '\0';\r
+                       // to shorten the url even more, we check for 'trunk', 'branches' and 'tags'\r
+                       // and simply assume that these are the folders attached to the repository\r
+                       // root. If we find those, we strip the whole path before those folders too.\r
+                       // Note: this will strip too much if such a folder is *below* the repository\r
+                       // root - but it's called 'short url' and we're free to shorten it the way we\r
+                       // like :)\r
+                       ptr = _tcsstr(urlComponents.lpszUrlPath, _T("/trunk"));\r
+                       if (ptr == NULL)\r
+                               ptr = _tcsstr(urlComponents.lpszUrlPath, _T("\\trunk"));\r
+                       if ((ptr == NULL)||((*(ptr+6) != 0)&&(*(ptr+6) != '/')&&(*(ptr+6) != '\\')))\r
+                       {\r
+                               ptr = _tcsstr(urlComponents.lpszUrlPath, _T("/branches"));\r
+                               if (ptr == NULL)\r
+                                       ptr = _tcsstr(urlComponents.lpszUrlPath, _T("\\branches"));\r
+                               if ((ptr == NULL)||((*(ptr+9) != 0)&&(*(ptr+9) != '/')&&(*(ptr+9) != '\\')))\r
+                               {\r
+                                       ptr = _tcsstr(urlComponents.lpszUrlPath, _T("/tags"));\r
+                                       if (ptr == NULL)\r
+                                               ptr = _tcsstr(urlComponents.lpszUrlPath, _T("\\tags"));\r
+                                       if ((ptr)&&(*(ptr+5) != 0)&&(*(ptr+5) != '/')&&(*(ptr+5) != '\\'))\r
+                                               ptr = NULL;\r
+                               }\r
+                       }\r
+                       if (ptr)\r
+                               itemshorturl = ptr;\r
+                       else\r
+                               itemshorturl = urlComponents.lpszUrlPath;\r
+               }\r
+               else \r
+                       itemshorturl = _T(" ");\r
+       }\r
+       else\r
+               itemshorturl = _T(" ");\r
+\r
+       if (status)\r
+       {\r
+               char url[INTERNET_MAX_URL_LENGTH];\r
+               strcpy_s(url, INTERNET_MAX_URL_LENGTH, status->url);\r
+               CPathUtils::Unescape(url);\r
+               itemurl = UTF8ToWide(url);\r
+       }\r
+       else if (t == ShellCache::exe)\r
+       {\r
+               char url[INTERNET_MAX_URL_LENGTH];\r
+               strcpy_s(url, INTERNET_MAX_URL_LENGTH, itemStatus.m_url);\r
+               CPathUtils::Unescape(url);\r
+               itemurl = UTF8ToWide(url);\r
+       }\r
+}\r
+\r
diff --git a/TortoiseShell/ContextMenu.cpp b/TortoiseShell/ContextMenu.cpp
new file mode 100644 (file)
index 0000000..bf98b69
--- /dev/null
@@ -0,0 +1,2403 @@
+// TortoiseSVN - a Windows shell extension for easy version control\r
+\r
+// Copyright (C) 2003-2008 - TortoiseSVN\r
+\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software Foundation,\r
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+//\r
+#include "stdafx.h"\r
+#include "ShellExt.h"\r
+#include "ItemIDList.h"\r
+#include "PreserveChdir.h"\r
+#include "UnicodeUtils.h"\r
+#include "SVNProperties.h"\r
+#include "SVNStatus.h"\r
+\r
+#define GetPIDLFolder(pida) (LPCITEMIDLIST)(((LPBYTE)pida)+(pida)->aoffset[0])\r
+#define GetPIDLItem(pida, i) (LPCITEMIDLIST)(((LPBYTE)pida)+(pida)->aoffset[i+1])\r
+\r
+int g_shellidlist=RegisterClipboardFormat(CFSTR_SHELLIDLIST);\r
+\r
+CShellExt::MenuInfo CShellExt::menuInfo[] =\r
+{\r
+       { ShellMenuCheckout,                                    MENUCHECKOUT,           IDI_CHECKOUT,                   IDS_MENUCHECKOUT,                       IDS_MENUDESCCHECKOUT,\r
+       ITEMIS_FOLDER, ITEMIS_INSVN|ITEMIS_FOLDERINSVN, 0, 0, 0, 0, 0, 0 },\r
+\r
+       { ShellMenuUpdate,                                              MENUUPDATE,                     IDI_UPDATE,                             IDS_MENUUPDATE,                         IDS_MENUDESCUPDATE,                             \r
+       ITEMIS_INSVN,   ITEMIS_ADDED, ITEMIS_FOLDERINSVN, 0, 0, 0, 0, 0 },\r
+\r
+       { ShellMenuCommit,                                              MENUCOMMIT,                     IDI_COMMIT,                             IDS_MENUCOMMIT,                         IDS_MENUDESCCOMMIT,\r
+       ITEMIS_INSVN, 0, ITEMIS_FOLDERINSVN, 0, 0, 0, 0, 0 },\r
+\r
+       { ShellSeparator, 0, 0, 0, 0, 0, 0, 0, 0},\r
+\r
+       { ShellMenuDiff,                                                MENUDIFF,                       IDI_DIFF,                               IDS_MENUDIFF,                           IDS_MENUDESCDIFF,\r
+       ITEMIS_INSVN|ITEMIS_ONLYONE, ITEMIS_FOLDER|ITEMIS_NORMAL, ITEMIS_TWO, 0, 0, 0, 0, 0 },\r
+\r
+       { ShellMenuPrevDiff,                                    MENUPREVDIFF,                   IDI_DIFF,                               IDS_MENUPREVDIFF,                       IDS_MENUDESCPREVDIFF,\r
+       ITEMIS_INSVN|ITEMIS_ONLYONE, ITEMIS_FOLDER, 0, 0, 0, 0, 0, 0 },\r
+\r
+       { ShellMenuUrlDiff,                                             MENUURLDIFF,            IDI_DIFF,                               IDS_MENUURLDIFF,                        IDS_MENUDESCURLDIFF,\r
+       ITEMIS_INSVN|ITEMIS_ONLYONE|ITEMIS_EXTENDED, 0, ITEMIS_FOLDERINSVN|ITEMIS_EXTENDED|ITEMIS_ONLYONE, 0, 0, 0, 0, 0 },\r
+\r
+       { ShellMenuLog,                                                 MENULOG,                        IDI_LOG,                                IDS_MENULOG,                            IDS_MENUDESCLOG,\r
+       ITEMIS_INSVN|ITEMIS_ONLYONE, ITEMIS_ADDED, ITEMIS_FOLDER|ITEMIS_FOLDERINSVN|ITEMIS_ONLYONE, ITEMIS_ADDED, ITEMIS_FOLDERINSVN|ITEMIS_ONLYONE, ITEMIS_ADDED, 0, 0 },\r
+\r
+       { ShellMenuRepoBrowse,                                  MENUREPOBROWSE,         IDI_REPOBROWSE,                 IDS_MENUREPOBROWSE,                     IDS_MENUDESCREPOBROWSE,\r
+       ITEMIS_ONLYONE, 0, ITEMIS_FOLDERINSVN|ITEMIS_ONLYONE, 0, 0, 0, 0, 0 },\r
+\r
+       { ShellMenuShowChanged,                                 MENUSHOWCHANGED,        IDI_SHOWCHANGED,                IDS_MENUSHOWCHANGED,            IDS_MENUDESCSHOWCHANGED,\r
+       ITEMIS_INSVN|ITEMIS_ONLYONE, 0, ITEMIS_FOLDER|ITEMIS_FOLDERINSVN|ITEMIS_ONLYONE, 0, 0, 0, 0, 0},\r
+\r
+       { ShellMenuRevisionGraph,                               MENUREVISIONGRAPH,      IDI_REVISIONGRAPH,              IDS_MENUREVISIONGRAPH,          IDS_MENUDESCREVISIONGRAPH,\r
+       ITEMIS_INSVN|ITEMIS_ONLYONE, ITEMIS_ADDED, ITEMIS_FOLDER|ITEMIS_FOLDERINSVN|ITEMIS_ONLYONE, ITEMIS_ADDED, 0, 0, 0, 0},\r
+\r
+       { ShellSeparator, 0, 0, 0, 0, 0, 0, 0, 0},\r
+\r
+       { ShellMenuConflictEditor,                              MENUCONFLICTEDITOR,     IDI_CONFLICT,                   IDS_MENUCONFLICT,                       IDS_MENUDESCCONFLICT,\r
+       ITEMIS_INSVN|ITEMIS_CONFLICTED, ITEMIS_FOLDER, 0, 0, 0, 0, 0, 0 },\r
+\r
+       { ShellMenuResolve,                                             MENURESOLVE,            IDI_RESOLVE,                    IDS_MENURESOLVE,                        IDS_MENUDESCRESOLVE,\r
+       ITEMIS_INSVN|ITEMIS_CONFLICTED, 0, ITEMIS_INSVN|ITEMIS_FOLDER, 0, ITEMIS_FOLDERINSVN, 0, 0, 0 },\r
+\r
+       { ShellMenuUpdateExt,                                   MENUUPDATEEXT,          IDI_UPDATE,                             IDS_MENUUPDATEEXT,                      IDS_MENUDESCUPDATEEXT,\r
+       ITEMIS_INSVN, ITEMIS_ADDED, ITEMIS_FOLDERINSVN, ITEMIS_ADDED, 0, 0, 0, 0 },\r
+\r
+       { ShellMenuRename,                                              MENURENAME,                     IDI_RENAME,                             IDS_MENURENAME,                         IDS_MENUDESCRENAME,\r
+       ITEMIS_INSVN|ITEMIS_ONLYONE|ITEMIS_INVERSIONEDFOLDER, 0, 0, 0, 0, 0, 0, 0 },\r
+\r
+       { ShellMenuRemove,                                              MENUREMOVE,                     IDI_DELETE,                             IDS_MENUREMOVE,                         IDS_MENUDESCREMOVE,\r
+       ITEMIS_INSVN|ITEMIS_INVERSIONEDFOLDER, ITEMIS_ADDED, 0, 0, 0, 0, 0, 0 },\r
+\r
+       { ShellMenuRemoveKeep,                                  MENUREMOVE,                     IDI_DELETE,                             IDS_MENUREMOVEKEEP,                     IDS_MENUDESCREMOVEKEEP,\r
+       ITEMIS_INSVN|ITEMIS_INVERSIONEDFOLDER|ITEMIS_EXTENDED, ITEMIS_ADDED, 0, 0, 0, 0, 0, 0 },\r
+\r
+       { ShellMenuRevert,                                              MENUREVERT,                     IDI_REVERT,                             IDS_MENUREVERT,                         IDS_MENUDESCREVERT,\r
+       ITEMIS_INSVN, ITEMIS_NORMAL|ITEMIS_ADDED, ITEMIS_FOLDERINSVN, ITEMIS_ADDED, 0, 0, 0, 0 },\r
+\r
+       { ShellMenuRevert,                                              MENUREVERT,                     IDI_REVERT,                             IDS_MENUUNDOADD,                        IDS_MENUDESCUNDOADD,\r
+       ITEMIS_ADDED, ITEMIS_NORMAL, ITEMIS_FOLDERINSVN|ITEMIS_ADDED, 0, 0, 0, 0, 0 },\r
+\r
+       { ShellMenuDelUnversioned,                              MENUDELUNVERSIONED,     IDI_DELUNVERSIONED,             IDS_MENUDELUNVERSIONED,         IDS_MENUDESCDELUNVERSIONED,\r
+       ITEMIS_FOLDER|ITEMIS_INSVN|ITEMIS_EXTENDED, 0, ITEMIS_FOLDER|ITEMIS_FOLDERINSVN|ITEMIS_EXTENDED, 0, 0, 0, 0, 0 },\r
+\r
+       { ShellMenuCleanup,                                             MENUCLEANUP,            IDI_CLEANUP,                    IDS_MENUCLEANUP,                        IDS_MENUDESCCLEANUP,\r
+       ITEMIS_INSVN|ITEMIS_FOLDER, 0, ITEMIS_FOLDERINSVN|ITEMIS_FOLDER, 0, 0, 0, 0, 0 },\r
+\r
+       { ShellMenuLock,                                                MENULOCK,                       IDI_LOCK,                               IDS_MENU_LOCK,                          IDS_MENUDESC_LOCK,\r
+       ITEMIS_INSVN, ITEMIS_LOCKED|ITEMIS_ADDED, ITEMIS_FOLDERINSVN, ITEMIS_LOCKED|ITEMIS_ADDED, 0, 0, 0, 0 },\r
+\r
+       { ShellMenuUnlock,                                              MENUUNLOCK,                     IDI_UNLOCK,                             IDS_MENU_UNLOCK,                        IDS_MENUDESC_UNLOCK,\r
+       ITEMIS_INSVN|ITEMIS_LOCKED, 0, ITEMIS_FOLDER|ITEMIS_INSVN, 0, ITEMIS_FOLDERINSVN, 0, 0, 0 },\r
+\r
+       { ShellMenuUnlockForce,                                 MENUUNLOCK,                     IDI_UNLOCK,                             IDS_MENU_UNLOCKFORCE,           IDS_MENUDESC_UNLOCKFORCE,\r
+       ITEMIS_INSVN|ITEMIS_LOCKED, 0, ITEMIS_FOLDER|ITEMIS_INSVN|ITEMIS_EXTENDED, 0, 0, 0, 0, 0 },\r
+\r
+       { ShellSeparator, 0, 0, 0, 0, 0, 0, 0, 0},\r
+\r
+       { ShellMenuCopy,                                                MENUCOPY,                       IDI_COPY,                               IDS_MENUBRANCH,                         IDS_MENUDESCCOPY,\r
+       ITEMIS_INSVN|ITEMIS_ONLYONE, ITEMIS_ADDED, ITEMIS_FOLDER|ITEMIS_FOLDERINSVN|ITEMIS_ONLYONE, 0, 0, 0, 0, 0 },\r
+\r
+       { ShellMenuSwitch,                                              MENUSWITCH,                     IDI_SWITCH,                             IDS_MENUSWITCH,                         IDS_MENUDESCSWITCH,\r
+       ITEMIS_INSVN|ITEMIS_ONLYONE, ITEMIS_ADDED, ITEMIS_FOLDER|ITEMIS_FOLDERINSVN|ITEMIS_ONLYONE, 0, 0, 0, 0, 0 },\r
+\r
+       { ShellMenuMerge,                                               MENUMERGE,                      IDI_MERGE,                              IDS_MENUMERGE,                          IDS_MENUDESCMERGE,\r
+       ITEMIS_INSVN|ITEMIS_ONLYONE, ITEMIS_ADDED, ITEMIS_FOLDER|ITEMIS_FOLDERINSVN|ITEMIS_ONLYONE, 0, 0, 0, 0, 0 },\r
+       { ShellMenuMergeAll,                                    MENUMERGEALL,           IDI_MERGE,                              IDS_MENUMERGEALL,                       IDS_MENUDESCMERGEALL,\r
+       ITEMIS_INSVN|ITEMIS_ONLYONE|ITEMIS_EXTENDED, ITEMIS_ADDED, ITEMIS_FOLDER|ITEMIS_FOLDERINSVN|ITEMIS_ONLYONE|ITEMIS_EXTENDED, 0, 0, 0, 0, 0 },\r
+\r
+       { ShellMenuExport,                                              MENUEXPORT,                     IDI_EXPORT,                             IDS_MENUEXPORT,                         IDS_MENUDESCEXPORT,\r
+       ITEMIS_FOLDER|ITEMIS_ONLYONE, 0, 0, 0, 0, 0, 0, 0 },\r
+\r
+       { ShellMenuRelocate,                                    MENURELOCATE,           IDI_RELOCATE,                   IDS_MENURELOCATE,                       IDS_MENUDESCRELOCATE,\r
+       ITEMIS_INSVN|ITEMIS_FOLDER|ITEMIS_FOLDERINSVN|ITEMIS_ONLYONE, 0, ITEMIS_FOLDERINSVN|ITEMIS_ONLYONE, 0, 0, 0, 0, 0 },\r
+\r
+       { ShellSeparator, 0, 0, 0, 0, 0, 0, 0, 0},\r
+\r
+       { ShellMenuCreateRepos,                                 MENUCREATEREPOS,        IDI_CREATEREPOS,                IDS_MENUCREATEREPOS,            IDS_MENUDESCCREATEREPOS,\r
+       ITEMIS_FOLDER, ITEMIS_INSVN|ITEMIS_FOLDERINSVN, 0, 0, 0, 0, 0, 0 },\r
+\r
+       { ShellMenuAdd,                                                 MENUADD,                        IDI_ADD,                                IDS_MENUADD,                            IDS_MENUDESCADD,\r
+       ITEMIS_INVERSIONEDFOLDER, ITEMIS_INSVN, ITEMIS_INSVN|ITEMIS_FOLDER, 0, ITEMIS_IGNORED, 0, ITEMIS_DELETED, ITEMIS_FOLDER|ITEMIS_ONLYONE },\r
+\r
+       { ShellMenuAddAsReplacement,                    MENUADD,                        IDI_ADD,                                IDS_MENUADDASREPLACEMENT,       IDS_MENUADDASREPLACEMENT,\r
+       ITEMIS_DELETED|ITEMIS_ONLYONE, ITEMIS_FOLDER, 0, 0, 0, 0, 0, 0 },\r
+\r
+       { ShellMenuImport,                                              MENUIMPORT,                     IDI_IMPORT,                             IDS_MENUIMPORT,                         IDS_MENUDESCIMPORT,\r
+       ITEMIS_FOLDER, ITEMIS_INSVN, 0, 0, 0, 0, 0, 0 },\r
+\r
+       { ShellMenuBlame,                                               MENUBLAME,                      IDI_BLAME,                              IDS_MENUBLAME,                          IDS_MENUDESCBLAME,\r
+       ITEMIS_INSVN|ITEMIS_ONLYONE, ITEMIS_FOLDER|ITEMIS_ADDED, 0, 0, 0, 0, 0, 0 },\r
+\r
+       { ShellMenuIgnoreSub,                                   MENUIGNORE,                     IDI_IGNORE,                             IDS_MENUIGNORE,                         IDS_MENUDESCIGNORE,\r
+       ITEMIS_INVERSIONEDFOLDER, ITEMIS_IGNORED|ITEMIS_INSVN, 0, 0, 0, 0, 0, 0 },\r
+\r
+       { ShellMenuUnIgnoreSub,                                 MENUIGNORE,                     IDI_IGNORE,                             IDS_MENUUNIGNORE,                       IDS_MENUDESCUNIGNORE,\r
+       ITEMIS_IGNORED, 0, 0, 0, 0, 0, 0, 0 },\r
+\r
+       { ShellSeparator, 0, 0, 0, 0, 0, 0, 0, 0},\r
+\r
+       { ShellMenuCreatePatch,                                 MENUCREATEPATCH,        IDI_CREATEPATCH,                IDS_MENUCREATEPATCH,            IDS_MENUDESCCREATEPATCH,\r
+       ITEMIS_INSVN, ITEMIS_NORMAL, ITEMIS_FOLDERINSVN, 0, 0, 0, 0, 0 },\r
+\r
+       { ShellMenuApplyPatch,                                  MENUAPPLYPATCH,         IDI_PATCH,                              IDS_MENUAPPLYPATCH,                     IDS_MENUDESCAPPLYPATCH,\r
+       ITEMIS_INSVN|ITEMIS_FOLDER|ITEMIS_FOLDERINSVN, ITEMIS_ADDED, ITEMIS_ONLYONE|ITEMIS_PATCHFILE, 0, ITEMIS_FOLDERINSVN, ITEMIS_ADDED, 0, 0 },\r
+\r
+       { ShellMenuProperties,                                  MENUPROPERTIES,         IDI_PROPERTIES,                 IDS_MENUPROPERTIES,                     IDS_MENUDESCPROPERTIES,\r
+       ITEMIS_INSVN, 0, ITEMIS_FOLDERINSVN, 0, 0, 0, 0, 0 },\r
+\r
+       { ShellSeparator, 0, 0, 0, 0, 0, 0, 0, 0},\r
+       { ShellMenuClipPaste,                                   MENUCLIPPASTE,          IDI_CLIPPASTE,                  IDS_MENUCLIPPASTE,                      IDS_MENUDESCCLIPPASTE,\r
+       ITEMIS_INSVN|ITEMIS_FOLDER|ITEMIS_PATHINCLIPBOARD, 0, 0, 0, 0, 0, 0, 0 },\r
+\r
+       { ShellSeparator, 0, 0, 0, 0, 0, 0, 0, 0},\r
+\r
+       { ShellMenuSettings,                                    MENUSETTINGS,           IDI_SETTINGS,                   IDS_MENUSETTINGS,                       IDS_MENUDESCSETTINGS,\r
+       ITEMIS_FOLDER, 0, 0, ITEMIS_FOLDER, 0, 0, 0, 0 },\r
+       { ShellMenuHelp,                                                MENUHELP,                       IDI_HELP,                               IDS_MENUHELP,                           IDS_MENUDESCHELP,\r
+       ITEMIS_FOLDER, 0, 0, ITEMIS_FOLDER, 0, 0, 0, 0 },\r
+       { ShellMenuAbout,                                               MENUABOUT,                      IDI_ABOUT,                              IDS_MENUABOUT,                          IDS_MENUDESCABOUT,\r
+       ITEMIS_FOLDER, 0, 0, ITEMIS_FOLDER, 0, 0, 0, 0 },\r
+\r
+       // the sub menus - they're not added like the the commands, therefore the menu ID is zero\r
+       // but they still need to be in here, because we use the icon and string information anyway.\r
+       { ShellSubMenu,                                                 NULL,                           IDI_APP,                                IDS_MENUSUBMENU,                        0,\r
+       0, 0, 0, 0, 0, 0, 0, 0 },\r
+       { ShellSubMenuFile,                                             NULL,                           IDI_MENUFILE,                   IDS_MENUSUBMENU,                        0,\r
+       0, 0, 0, 0, 0, 0, 0, 0 },\r
+       { ShellSubMenuFolder,                                   NULL,                           IDI_MENUFOLDER,                 IDS_MENUSUBMENU,                        0,\r
+       0, 0, 0, 0, 0, 0, 0, 0 },\r
+       { ShellSubMenuLink,                                             NULL,                           IDI_MENULINK,                   IDS_MENUSUBMENU,                        0,\r
+       0, 0, 0, 0, 0, 0, 0, 0 },\r
+       { ShellSubMenuMultiple,                                 NULL,                           IDI_MENUMULTIPLE,               IDS_MENUSUBMENU,                        0,\r
+       0, 0, 0, 0, 0, 0, 0, 0 },\r
+       // mark the last entry to tell the loop where to stop iterating over this array\r
+       { ShellMenuLastEntry,                                   0,                                      0,                                              0,                                                      0,\r
+       0, 0, 0, 0, 0, 0, 0, 0 },\r
+};\r
+\r
+\r
+STDMETHODIMP CShellExt::Initialize(LPCITEMIDLIST pIDFolder,\r
+                                   LPDATAOBJECT pDataObj,\r
+                                   HKEY /* hRegKey */)\r
+{\r
+       ATLTRACE("Shell :: Initialize\n");\r
+       PreserveChdir preserveChdir;\r
+       files_.clear();\r
+       folder_.erase();\r
+       uuidSource.erase();\r
+       uuidTarget.erase();\r
+       itemStates = 0;\r
+       itemStatesFolder = 0;\r
+       stdstring statuspath;\r
+       svn_wc_status_kind fetchedstatus = svn_wc_status_none;\r
+       // get selected files/folders\r
+       if (pDataObj)\r
+       {\r
+               STGMEDIUM medium;\r
+               FORMATETC fmte = {(CLIPFORMAT)g_shellidlist,\r
+                       (DVTARGETDEVICE FAR *)NULL, \r
+                       DVASPECT_CONTENT, \r
+                       -1, \r
+                       TYMED_HGLOBAL};\r
+               HRESULT hres = pDataObj->GetData(&fmte, &medium);\r
+\r
+               if (SUCCEEDED(hres) && medium.hGlobal)\r
+               {\r
+                       if (m_State == FileStateDropHandler)\r
+                       {\r
+                               FORMATETC etc = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };\r
+                               STGMEDIUM stg = { TYMED_HGLOBAL };\r
+                               if ( FAILED( pDataObj->GetData ( &etc, &stg )))\r
+                               {\r
+                                       ReleaseStgMedium ( &medium );\r
+                                       return E_INVALIDARG;\r
+                               }\r
+\r
+\r
+                               HDROP drop = (HDROP)GlobalLock(stg.hGlobal);\r
+                               if ( NULL == drop )\r
+                               {\r
+                                       ReleaseStgMedium ( &stg );\r
+                                       ReleaseStgMedium ( &medium );\r
+                                       return E_INVALIDARG;\r
+                               }\r
+\r
+                               int count = DragQueryFile(drop, (UINT)-1, NULL, 0);\r
+                               if (count == 1)\r
+                                       itemStates |= ITEMIS_ONLYONE;\r
+                               for (int i = 0; i < count; i++)\r
+                               {\r
+                                       // find the path length in chars\r
+                                       UINT len = DragQueryFile(drop, i, NULL, 0);\r
+                                       if (len == 0)\r
+                                               continue;\r
+                                       TCHAR * szFileName = new TCHAR[len+1];\r
+                                       if (0 == DragQueryFile(drop, i, szFileName, len+1))\r
+                                       {\r
+                                               delete [] szFileName;\r
+                                               continue;\r
+                                       }\r
+                                       stdstring str = stdstring(szFileName);\r
+                                       delete [] szFileName;\r
+                                       if ((str.empty() == false)&&(g_ShellCache.IsContextPathAllowed(szFileName)))\r
+                                       {\r
+                                               if (itemStates & ITEMIS_ONLYONE)\r
+                                               {\r
+                                                       CTSVNPath strpath;\r
+                                                       strpath.SetFromWin(str.c_str());\r
+                                                       itemStates |= (strpath.GetFileExtension().CompareNoCase(_T(".diff"))==0) ? ITEMIS_PATCHFILE : 0;\r
+                                                       itemStates |= (strpath.GetFileExtension().CompareNoCase(_T(".patch"))==0) ? ITEMIS_PATCHFILE : 0;\r
+                                               }\r
+                                               files_.push_back(str);\r
+                                               if (i == 0)\r
+                                               {\r
+                                                       //get the Subversion status of the item\r
+                                                       svn_wc_status_kind status = svn_wc_status_none;\r
+                                                       CTSVNPath askedpath;\r
+                                                       askedpath.SetFromWin(str.c_str());\r
+                                                       try\r
+                                                       {\r
+                                                               SVNStatus stat;\r
+                                                               stat.GetStatus(CTSVNPath(str.c_str()), false, true, true);\r
+                                                               if (stat.status)\r
+                                                               {\r
+                                                                       statuspath = str;\r
+                                                                       status = SVNStatus::GetMoreImportant(stat.status->text_status, stat.status->prop_status);\r
+                                                                       fetchedstatus = status;\r
+                                                                       if ((stat.status->entry)&&(stat.status->entry->lock_token))\r
+                                                                               itemStates |= (stat.status->entry->lock_token[0] != 0) ? ITEMIS_LOCKED : 0;\r
+                                                                       if ((stat.status->entry)&&(stat.status->entry->kind == svn_node_dir))\r
+                                                                       {\r
+                                                                               itemStates |= ITEMIS_FOLDER;\r
+                                                                               if ((status != svn_wc_status_unversioned)&&(status != svn_wc_status_ignored)&&(status != svn_wc_status_none))\r
+                                                                                       itemStates |= ITEMIS_FOLDERINSVN;\r
+                                                                       }\r
+                                                                       if ((stat.status->entry)&&(stat.status->entry->present_props))\r
+                                                                       {\r
+                                                                               if (strstr(stat.status->entry->present_props, "svn:needs-lock"))\r
+                                                                                       itemStates |= ITEMIS_NEEDSLOCK;\r
+                                                                       }\r
+                                                                       if ((stat.status->entry)&&(stat.status->entry->uuid))\r
+                                                                               uuidSource = CUnicodeUtils::StdGetUnicode(stat.status->entry->uuid);\r
+                                                               }\r
+                                                               else\r
+                                                               {\r
+                                                                       // sometimes, svn_client_status() returns with an error.\r
+                                                                       // in that case, we have to check if the working copy is versioned\r
+                                                                       // anyway to show the 'correct' context menu\r
+                                                                       if (askedpath.HasAdminDir())\r
+                                                                               status = svn_wc_status_normal;\r
+                                                               }\r
+                                                       }\r
+                                                       catch ( ... )\r
+                                                       {\r
+                                                               ATLTRACE2(_T("Exception in SVNStatus::GetAllStatus()\n"));\r
+                                                       }\r
+                                                       if ((status != svn_wc_status_unversioned)&&(status != svn_wc_status_ignored)&&(status != svn_wc_status_none))\r
+                                                               itemStates |= ITEMIS_INSVN;\r
+                                                       if (status == svn_wc_status_ignored)\r
+                                                               itemStates |= ITEMIS_IGNORED;\r
+                                                       if (status == svn_wc_status_normal)\r
+                                                               itemStates |= ITEMIS_NORMAL;\r
+                                                       if (status == svn_wc_status_conflicted)\r
+                                                               itemStates |= ITEMIS_CONFLICTED;\r
+                                                       if (status == svn_wc_status_added)\r
+                                                               itemStates |= ITEMIS_ADDED;\r
+                                                       if (status == svn_wc_status_deleted)\r
+                                                               itemStates |= ITEMIS_DELETED;\r
+                                               }\r
+                                       }\r
+                               } // for (int i = 0; i < count; i++)\r
+                               GlobalUnlock ( drop );\r
+                               ReleaseStgMedium ( &stg );\r
+                       } // if (m_State == FileStateDropHandler) \r
+                       else\r
+                       {\r
+                               //Enumerate PIDLs which the user has selected\r
+                               CIDA* cida = (CIDA*)GlobalLock(medium.hGlobal);\r
+                               ItemIDList parent( GetPIDLFolder (cida));\r
+\r
+                               int count = cida->cidl;\r
+                               BOOL statfetched = FALSE;\r
+                               for (int i = 0; i < count; ++i)\r
+                               {\r
+                                       ItemIDList child (GetPIDLItem (cida, i), &parent);\r
+                                       stdstring str = child.toString();\r
+                                       if ((str.empty() == false)&&(g_ShellCache.IsContextPathAllowed(str.c_str())))\r
+                                       {\r
+                                               //check if our menu is requested for a subversion admin directory\r
+                                               if (g_SVNAdminDir.IsAdminDirPath(str.c_str()))\r
+                                                       continue;\r
+\r
+                                               files_.push_back(str);\r
+                                               CTSVNPath strpath;\r
+                                               strpath.SetFromWin(str.c_str());\r
+                                               itemStates |= (strpath.GetFileExtension().CompareNoCase(_T(".diff"))==0) ? ITEMIS_PATCHFILE : 0;\r
+                                               itemStates |= (strpath.GetFileExtension().CompareNoCase(_T(".patch"))==0) ? ITEMIS_PATCHFILE : 0;\r
+                                               if (!statfetched)\r
+                                               {\r
+                                                       //get the Subversion status of the item\r
+                                                       svn_wc_status_kind status = svn_wc_status_none;\r
+                                                       if ((g_ShellCache.IsSimpleContext())&&(strpath.IsDirectory()))\r
+                                                       {\r
+                                                               if (strpath.HasAdminDir())\r
+                                                                       status = svn_wc_status_normal;\r
+                                                       }\r
+                                                       else\r
+                                                       {\r
+                                                               try\r
+                                                               {\r
+                                                                       SVNStatus stat;\r
+                                                                       if (strpath.HasAdminDir())\r
+                                                                               stat.GetStatus(strpath, false, true, true);\r
+                                                                       statuspath = str;\r
+                                                                       if (stat.status)\r
+                                                                       {\r
+                                                                               status = SVNStatus::GetMoreImportant(stat.status->text_status, stat.status->prop_status);\r
+                                                                               fetchedstatus = status;\r
+                                                                               if ((stat.status->entry)&&(stat.status->entry->lock_token))\r
+                                                                                       itemStates |= (stat.status->entry->lock_token[0] != 0) ? ITEMIS_LOCKED : 0;\r
+                                                                               if ((stat.status->entry)&&(stat.status->entry->kind == svn_node_dir))\r
+                                                                               {\r
+                                                                                       itemStates |= ITEMIS_FOLDER;\r
+                                                                                       if ((status != svn_wc_status_unversioned)&&(status != svn_wc_status_ignored)&&(status != svn_wc_status_none))\r
+                                                                                               itemStates |= ITEMIS_FOLDERINSVN;\r
+                                                                               }\r
+                                                                               if ((stat.status->entry)&&(stat.status->entry->conflict_wrk))\r
+                                                                                       itemStates |= ITEMIS_CONFLICTED;\r
+                                                                               if ((stat.status->entry)&&(stat.status->entry->present_props))\r
+                                                                               {\r
+                                                                                       if (strstr(stat.status->entry->present_props, "svn:needs-lock"))\r
+                                                                                               itemStates |= ITEMIS_NEEDSLOCK;\r
+                                                                               }\r
+                                                                               if ((stat.status->entry)&&(stat.status->entry->uuid))\r
+                                                                                       uuidSource = CUnicodeUtils::StdGetUnicode(stat.status->entry->uuid);\r
+                                                                       }       \r
+                                                                       else\r
+                                                                       {\r
+                                                                               // sometimes, svn_client_status() returns with an error.\r
+                                                                               // in that case, we have to check if the working copy is versioned\r
+                                                                               // anyway to show the 'correct' context menu\r
+                                                                               if (strpath.HasAdminDir())\r
+                                                                               {\r
+                                                                                       status = svn_wc_status_normal;\r
+                                                                                       fetchedstatus = status;\r
+                                                                               }\r
+                                                                       }\r
+                                                                       statfetched = TRUE;\r
+                                                               }\r
+                                                               catch ( ... )\r
+                                                               {\r
+                                                                       ATLTRACE2(_T("Exception in SVNStatus::GetAllStatus()\n"));\r
+                                                               }\r
+                                                       }\r
+                                                       if ((status != svn_wc_status_unversioned)&&(status != svn_wc_status_ignored)&&(status != svn_wc_status_none))\r
+                                                               itemStates |= ITEMIS_INSVN;\r
+                                                       if (status == svn_wc_status_ignored)\r
+                                                       {\r
+                                                               itemStates |= ITEMIS_IGNORED;\r
+                                                               // the item is ignored. Get the svn:ignored properties so we can (maybe) later\r
+                                                               // offer a 'remove from ignored list' entry\r
+                                                               SVNProperties props(strpath.GetContainingDirectory(), false);\r
+                                                               ignoredprops.empty();\r
+                                                               for (int p=0; p<props.GetCount(); ++p)\r
+                                                               {\r
+                                                                       if (props.GetItemName(p).compare(stdstring(_T("svn:ignore")))==0)\r
+                                                                       {\r
+                                                                               std::string st = props.GetItemValue(p);\r
+                                                                               ignoredprops = MultibyteToWide(st.c_str());\r
+                                                                               // remove all escape chars ('\\')\r
+                                                                               std::remove(ignoredprops.begin(), ignoredprops.end(), '\\');\r
+                                                                               break;\r
+                                                                       }\r
+                                                               }\r
+                                                       }\r
+                                                       if (status == svn_wc_status_normal)\r
+                                                               itemStates |= ITEMIS_NORMAL;\r
+                                                       if (status == svn_wc_status_conflicted)\r
+                                                               itemStates |= ITEMIS_CONFLICTED;\r
+                                                       if (status == svn_wc_status_added)\r
+                                                               itemStates |= ITEMIS_ADDED;\r
+                                                       if (status == svn_wc_status_deleted)\r
+                                                               itemStates |= ITEMIS_DELETED;\r
+                                               }\r
+                                       }\r
+                               } // for (int i = 0; i < count; ++i)\r
+                               ItemIDList child (GetPIDLItem (cida, 0), &parent);\r
+                               if (g_ShellCache.HasSVNAdminDir(child.toString().c_str(), FALSE))\r
+                                       itemStates |= ITEMIS_INVERSIONEDFOLDER;\r
+                               GlobalUnlock(medium.hGlobal);\r
+\r
+                               // if the item is a versioned folder, check if there's a patch file\r
+                               // in the clipboard to be used in "Apply Patch"\r
+                               UINT cFormatDiff = RegisterClipboardFormat(_T("TSVN_UNIFIEDDIFF"));\r
+                               if (cFormatDiff)\r
+                               {\r
+                                       if (IsClipboardFormatAvailable(cFormatDiff)) \r
+                                               itemStates |= ITEMIS_PATCHINCLIPBOARD;\r
+                               }\r
+                               if (IsClipboardFormatAvailable(CF_HDROP)) \r
+                                       itemStates |= ITEMIS_PATHINCLIPBOARD;\r
+                       }\r
+\r
+                       ReleaseStgMedium ( &medium );\r
+                       if (medium.pUnkForRelease)\r
+                       {\r
+                               IUnknown* relInterface = (IUnknown*)medium.pUnkForRelease;\r
+                               relInterface->Release();\r
+                       }\r
+               }\r
+       }\r
+\r
+       // get folder background\r
+       if (pIDFolder)\r
+       {\r
+               ItemIDList list(pIDFolder);\r
+               folder_ = list.toString();\r
+               svn_wc_status_kind status = svn_wc_status_none;\r
+               if (IsClipboardFormatAvailable(CF_HDROP)) \r
+                       itemStatesFolder |= ITEMIS_PATHINCLIPBOARD;\r
+               if ((folder_.compare(statuspath)!=0)&&(g_ShellCache.IsContextPathAllowed(folder_.c_str())))\r
+               {\r
+                       CTSVNPath askedpath;\r
+                       askedpath.SetFromWin(folder_.c_str());\r
+                       try\r
+                       {\r
+                               SVNStatus stat;\r
+                               stat.GetStatus(CTSVNPath(folder_.c_str()), false, true, true);\r
+                               if (stat.status)\r
+                               {\r
+                                       status = SVNStatus::GetMoreImportant(stat.status->text_status, stat.status->prop_status);\r
+                                       if ((stat.status->entry)&&(stat.status->entry->lock_token))\r
+                                               itemStatesFolder |= (stat.status->entry->lock_token[0] != 0) ? ITEMIS_LOCKED : 0;\r
+                                       if ((stat.status->entry)&&(stat.status->entry->present_props))\r
+                                       {\r
+                                               if (strstr(stat.status->entry->present_props, "svn:needs-lock"))\r
+                                                       itemStatesFolder |= ITEMIS_NEEDSLOCK;\r
+                                       }\r
+                                       if ((stat.status->entry)&&(stat.status->entry->uuid))\r
+                                               uuidTarget = CUnicodeUtils::StdGetUnicode(stat.status->entry->uuid);\r
+                                       if ((status != svn_wc_status_unversioned)&&(status != svn_wc_status_ignored)&&(status != svn_wc_status_none))\r
+                                               itemStatesFolder |= ITEMIS_INSVN;\r
+                                       if (status == svn_wc_status_normal)\r
+                                               itemStatesFolder |= ITEMIS_NORMAL;\r
+                                       if (status == svn_wc_status_conflicted)\r
+                                               itemStatesFolder |= ITEMIS_CONFLICTED;\r
+                                       if (status == svn_wc_status_added)\r
+                                               itemStatesFolder |= ITEMIS_ADDED;\r
+                                       if (status == svn_wc_status_deleted)\r
+                                               itemStatesFolder |= ITEMIS_DELETED;\r
+                               }\r
+                               else\r
+                               {\r
+                                       // sometimes, svn_client_status() returns with an error.\r
+                                       // in that case, we have to check if the working copy is versioned\r
+                                       // anyway to show the 'correct' context menu\r
+                                       if (askedpath.HasAdminDir())\r
+                                               status = svn_wc_status_normal;\r
+                               }\r
+                       }\r
+                       catch ( ... )\r
+                       {\r
+                               ATLTRACE2(_T("Exception in SVNStatus::GetAllStatus()\n"));\r
+                       }\r
+               }\r
+               else\r
+               {\r
+                       status = fetchedstatus;\r
+               }\r
+               if ((status != svn_wc_status_unversioned)&&(status != svn_wc_status_ignored)&&(status != svn_wc_status_none))\r
+               {\r
+                       itemStatesFolder |= ITEMIS_FOLDERINSVN;\r
+               }\r
+               if (status == svn_wc_status_ignored)\r
+                       itemStatesFolder |= ITEMIS_IGNORED;\r
+               itemStatesFolder |= ITEMIS_FOLDER;\r
+               if (files_.size() == 0)\r
+                       itemStates |= ITEMIS_ONLYONE;\r
+               if (m_State != FileStateDropHandler)\r
+                       itemStates |= itemStatesFolder;\r
+       }\r
+       if (files_.size() == 2)\r
+               itemStates |= ITEMIS_TWO;\r
+       if ((files_.size() == 1)&&(g_ShellCache.IsContextPathAllowed(files_.front().c_str())))\r
+       {\r
+               itemStates |= ITEMIS_ONLYONE;\r
+               if (m_State != FileStateDropHandler)\r
+               {\r
+                       if (PathIsDirectory(files_.front().c_str()))\r
+                       {\r
+                               folder_ = files_.front();\r
+                               svn_wc_status_kind status = svn_wc_status_none;\r
+                               if (folder_.compare(statuspath)!=0)\r
+                               {\r
+                                       CTSVNPath askedpath;\r
+                                       askedpath.SetFromWin(folder_.c_str());\r
+                                       try\r
+                                       {\r
+                                               SVNStatus stat;\r
+                                               stat.GetStatus(CTSVNPath(folder_.c_str()), false, true, true);\r
+                                               if (stat.status)\r
+                                               {\r
+                                                       status = SVNStatus::GetMoreImportant(stat.status->text_status, stat.status->prop_status);\r
+                                                       if ((stat.status->entry)&&(stat.status->entry->lock_token))\r
+                                                               itemStates |= (stat.status->entry->lock_token[0] != 0) ? ITEMIS_LOCKED : 0;\r
+                                                       if ((stat.status->entry)&&(stat.status->entry->present_props))\r
+                                                       {\r
+                                                               if (strstr(stat.status->entry->present_props, "svn:needs-lock"))\r
+                                                                       itemStates |= ITEMIS_NEEDSLOCK;\r
+                                                       }\r
+                                                       if ((stat.status->entry)&&(stat.status->entry->uuid))\r
+                                                               uuidTarget = CUnicodeUtils::StdGetUnicode(stat.status->entry->uuid);\r
+                                               }\r
+                                       }\r
+                                       catch ( ... )\r
+                                       {\r
+                                               ATLTRACE2(_T("Exception in SVNStatus::GetAllStatus()\n"));\r
+                                       }\r
+                               }\r
+                               else\r
+                               {\r
+                                       status = fetchedstatus;\r
+                               }\r
+                               if ((status != svn_wc_status_unversioned)&&(status != svn_wc_status_ignored)&&(status != svn_wc_status_none))\r
+                                       itemStates |= ITEMIS_FOLDERINSVN;\r
+                               if (status == svn_wc_status_ignored)\r
+                                       itemStates |= ITEMIS_IGNORED;\r
+                               itemStates |= ITEMIS_FOLDER;\r
+                               if (status == svn_wc_status_added)\r
+                                       itemStates |= ITEMIS_ADDED;\r
+                               if (status == svn_wc_status_deleted)\r
+                                       itemStates |= ITEMIS_DELETED;\r
+                       }\r
+               }\r
+       }\r
+               \r
+       return NOERROR;\r
+}\r
+\r
+void CShellExt::InsertSVNMenu(BOOL istop, HMENU menu, UINT pos, UINT_PTR id, UINT stringid, UINT icon, UINT idCmdFirst, SVNCommands com, UINT uFlags)\r
+{\r
+       TCHAR menutextbuffer[255] = {0};\r
+       TCHAR verbsbuffer[255] = {0};\r
+       MAKESTRING(stringid);\r
+\r
+       if (istop)\r
+       {\r
+               //menu entry for the top context menu, so append an "SVN " before\r
+               //the menu text to indicate where the entry comes from\r
+               _tcscpy_s(menutextbuffer, 255, _T("SVN "));\r
+       }\r
+       _tcscat_s(menutextbuffer, 255, stringtablebuffer);\r
+       if ((fullver < 0x500)||(fullver == 0x500 && !uFlags))\r
+       {\r
+               InsertMenu(menu, pos, MF_BYPOSITION | MF_STRING , id, menutextbuffer);\r
+               if (fullver >= 0x500)\r
+               {\r
+                       // on win2k, the context menu does not work properly if we use\r
+                       // icon bitmaps. At least the menu text is empty in the context menu\r
+                       // for folder backgrounds (seems like a win2k bug).\r
+                       HBITMAP bmp = IconToBitmap(icon); \r
+                       SetMenuItemBitmaps(menu, pos, MF_BYPOSITION, bmp, bmp);\r
+               }\r
+       }\r
+       else\r
+       {\r
+               MENUITEMINFO menuiteminfo = {0};\r
+               menuiteminfo.cbSize = sizeof(menuiteminfo);\r
+               menuiteminfo.fMask = MIIM_FTYPE | MIIM_ID | MIIM_BITMAP | MIIM_STRING;\r
+               menuiteminfo.fType = MFT_STRING;\r
+               menuiteminfo.dwTypeData = menutextbuffer;\r
+               if (icon)\r
+                       menuiteminfo.hbmpItem = (fullver >= 0x600) ? IconToBitmapPARGB32(icon) : HBMMENU_CALLBACK;\r
+               menuiteminfo.wID = id;\r
+               InsertMenuItem(menu, pos, TRUE, &menuiteminfo);\r
+       }\r
+\r
+       if (istop)\r
+       {\r
+               //menu entry for the top context menu, so append an "SVN " before\r
+               //the menu text to indicate where the entry comes from\r
+               _tcscpy_s(menutextbuffer, 255, _T("SVN "));\r
+       }\r
+       LoadString(g_hResInst, stringid, verbsbuffer, sizeof(verbsbuffer));\r
+       _tcscat_s(menutextbuffer, 255, verbsbuffer);\r
+       stdstring verb = stdstring(menutextbuffer);\r
+       if (verb.find('&') != -1)\r
+       {\r
+               verb.erase(verb.find('&'),1);\r
+       }\r
+       myVerbsMap[verb] = id - idCmdFirst;\r
+       myVerbsMap[verb] = id;\r
+       myVerbsIDMap[id - idCmdFirst] = verb;\r
+       myVerbsIDMap[id] = verb;\r
+       // We store the relative and absolute diameter\r
+       // (drawitem callback uses absolute, others relative)\r
+       myIDMap[id - idCmdFirst] = com;\r
+       myIDMap[id] = com;\r
+       if (!istop)\r
+               mySubMenuMap[pos] = com;\r
+}\r
+\r
+HBITMAP CShellExt::IconToBitmap(UINT uIcon)\r
+{\r
+       std::map<UINT, HBITMAP>::iterator bitmap_it = bitmaps.lower_bound(uIcon);\r
+       if (bitmap_it != bitmaps.end() && bitmap_it->first == uIcon)\r
+               return bitmap_it->second;\r
+\r
+       HICON hIcon = (HICON)LoadImage(g_hResInst, MAKEINTRESOURCE(uIcon), IMAGE_ICON, 12, 12, LR_DEFAULTCOLOR);\r
+       if (!hIcon)\r
+               return NULL;\r
+\r
+       RECT rect;\r
+\r
+       rect.right = ::GetSystemMetrics(SM_CXMENUCHECK);\r
+       rect.bottom = ::GetSystemMetrics(SM_CYMENUCHECK);\r
+\r
+       rect.left = rect.top = 0;\r
+\r
+       HWND desktop = ::GetDesktopWindow();\r
+       if (desktop == NULL)\r
+       {\r
+               DestroyIcon(hIcon);\r
+               return NULL;\r
+       }\r
+\r
+       HDC screen_dev = ::GetDC(desktop);\r
+       if (screen_dev == NULL)\r
+       {\r
+               DestroyIcon(hIcon);\r
+               return NULL;\r
+       }\r
+\r
+       // Create a compatible DC\r
+       HDC dst_hdc = ::CreateCompatibleDC(screen_dev);\r
+       if (dst_hdc == NULL)\r
+       {\r
+               DestroyIcon(hIcon);\r
+               ::ReleaseDC(desktop, screen_dev); \r
+               return NULL;\r
+       }\r
+\r
+       // Create a new bitmap of icon size\r
+       HBITMAP bmp = ::CreateCompatibleBitmap(screen_dev, rect.right, rect.bottom);\r
+       if (bmp == NULL)\r
+       {\r
+               DestroyIcon(hIcon);\r
+               ::DeleteDC(dst_hdc);\r
+               ::ReleaseDC(desktop, screen_dev); \r
+               return NULL;\r
+       }\r
+\r
+       // Select it into the compatible DC\r
+       HBITMAP old_dst_bmp = (HBITMAP)::SelectObject(dst_hdc, bmp);\r
+       if (old_dst_bmp == NULL)\r
+       {\r
+               DestroyIcon(hIcon);\r
+               return NULL;\r
+       }\r
+\r
+       // Fill the background of the compatible DC with the white color\r
+       // that is taken by menu routines as transparent\r
+       ::SetBkColor(dst_hdc, RGB(255, 255, 255));\r
+       ::ExtTextOut(dst_hdc, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL);\r
+\r
+       // Draw the icon into the compatible DC\r
+       ::DrawIconEx(dst_hdc, 0, 0, hIcon, rect.right, rect.bottom, 0, NULL, DI_NORMAL);\r
+\r
+       // Restore settings\r
+       ::SelectObject(dst_hdc, old_dst_bmp);\r
+       ::DeleteDC(dst_hdc);\r
+       ::ReleaseDC(desktop, screen_dev); \r
+       DestroyIcon(hIcon);\r
+       if (bmp)\r
+               bitmaps.insert(bitmap_it, std::make_pair(uIcon, bmp));\r
+       return bmp;\r
+}\r
+\r
+bool CShellExt::WriteClipboardPathsToTempFile(stdstring& tempfile)\r
+{\r
+       bool bRet = true;\r
+       tempfile = stdstring();\r
+       //write all selected files and paths to a temporary file\r
+       //for TortoiseProc.exe to read out again.\r
+       DWORD written = 0;\r
+       DWORD pathlength = GetTempPath(0, NULL);\r
+       TCHAR * path = new TCHAR[pathlength+1];\r
+       TCHAR * tempFile = new TCHAR[pathlength + 100];\r
+       GetTempPath (pathlength+1, path);\r
+       GetTempFileName (path, _T("svn"), 0, tempFile);\r
+       tempfile = stdstring(tempFile);\r
+\r
+       HANDLE file = ::CreateFile (tempFile,\r
+               GENERIC_WRITE, \r
+               FILE_SHARE_READ, \r
+               0, \r
+               CREATE_ALWAYS, \r
+               FILE_ATTRIBUTE_TEMPORARY,\r
+               0);\r
+\r
+       delete [] path;\r
+       delete [] tempFile;\r
+       if (file == INVALID_HANDLE_VALUE)\r
+               return false;\r
+\r
+       if (!IsClipboardFormatAvailable(CF_HDROP))\r
+               return false;\r
+       if (!OpenClipboard(NULL))\r
+               return false;\r
+\r
+       stdstring sClipboardText;\r
+       HGLOBAL hglb = GetClipboardData(CF_HDROP);\r
+       HDROP hDrop = (HDROP)GlobalLock(hglb);\r
+       if(hDrop != NULL)\r
+       {\r
+               TCHAR szFileName[MAX_PATH];\r
+               UINT cFiles = DragQueryFile(hDrop, 0xFFFFFFFF, NULL, 0); \r
+               for(UINT i = 0; i < cFiles; ++i)\r
+               {\r
+                       DragQueryFile(hDrop, i, szFileName, sizeof(szFileName));\r
+                       stdstring filename = szFileName;\r
+                       ::WriteFile (file, filename.c_str(), filename.size()*sizeof(TCHAR), &written, 0);\r
+                       ::WriteFile (file, _T("\n"), 2, &written, 0);\r
+               }\r
+               GlobalUnlock(hDrop);\r
+       }\r
+       else bRet = false;\r
+       GlobalUnlock(hglb);\r
+\r
+       CloseClipboard();\r
+       ::CloseHandle(file);\r
+\r
+       return bRet;\r
+}\r
+\r
+stdstring CShellExt::WriteFileListToTempFile()\r
+{\r
+       //write all selected files and paths to a temporary file\r
+       //for TortoiseProc.exe to read out again.\r
+       DWORD pathlength = GetTempPath(0, NULL);\r
+       TCHAR * path = new TCHAR[pathlength+1];\r
+       TCHAR * tempFile = new TCHAR[pathlength + 100];\r
+       GetTempPath (pathlength+1, path);\r
+       GetTempFileName (path, _T("svn"), 0, tempFile);\r
+       stdstring retFilePath = stdstring(tempFile);\r
+       \r
+       HANDLE file = ::CreateFile (tempFile,\r
+                                                               GENERIC_WRITE, \r
+                                                               FILE_SHARE_READ, \r
+                                                               0, \r
+                                                               CREATE_ALWAYS, \r
+                                                               FILE_ATTRIBUTE_TEMPORARY,\r
+                                                               0);\r
+\r
+       delete [] path;\r
+       delete [] tempFile;\r
+       if (file == INVALID_HANDLE_VALUE)\r
+               return stdstring();\r
+               \r
+       DWORD written = 0;\r
+       if (files_.empty())\r
+       {\r
+               ::WriteFile (file, folder_.c_str(), folder_.size()*sizeof(TCHAR), &written, 0);\r
+               ::WriteFile (file, _T("\n"), 2, &written, 0);\r
+       }\r
+\r
+       for (std::vector<stdstring>::iterator I = files_.begin(); I != files_.end(); ++I)\r
+       {\r
+               ::WriteFile (file, I->c_str(), I->size()*sizeof(TCHAR), &written, 0);\r
+               ::WriteFile (file, _T("\n"), 2, &written, 0);\r
+       }\r
+       ::CloseHandle(file);\r
+       return retFilePath;\r
+}\r
+\r
+STDMETHODIMP CShellExt::QueryDropContext(UINT uFlags, UINT idCmdFirst, HMENU hMenu, UINT &indexMenu)\r
+{\r
+       PreserveChdir preserveChdir;\r
+       LoadLangDll();\r
+\r
+       if ((uFlags & CMF_DEFAULTONLY)!=0)\r
+               return NOERROR;                                 //we don't change the default action\r
+\r
+       if ((files_.size() == 0)||(folder_.size() == 0))\r
+               return NOERROR;\r
+\r
+       if (((uFlags & 0x000f)!=CMF_NORMAL)&&(!(uFlags & CMF_EXPLORE))&&(!(uFlags & CMF_VERBSONLY)))\r
+               return NOERROR;\r
+\r
+       bool bSourceAndTargetFromSameRepository = (uuidSource.compare(uuidTarget) == 0) || uuidSource.empty() || uuidTarget.empty();\r
+\r
+       //the drop handler only has eight commands, but not all are visible at the same time:\r
+       //if the source file(s) are under version control then those files can be moved\r
+       //to the new location or they can be moved with a rename, \r
+       //if they are unversioned then they can be added to the working copy\r
+       //if they are versioned, they also can be exported to an unversioned location\r
+       UINT idCmd = idCmdFirst;\r
+\r
+       // SVN move here\r
+       // available if source is versioned but not added, target is versioned, source and target from same repository or target folder is added\r
+       if ((bSourceAndTargetFromSameRepository||(itemStatesFolder & ITEMIS_ADDED))&&(itemStatesFolder & ITEMIS_FOLDERINSVN)&&((itemStates & ITEMIS_INSVN)&&((~itemStates) & ITEMIS_ADDED)))\r
+               InsertSVNMenu(FALSE, hMenu, indexMenu++, idCmd++, IDS_DROPMOVEMENU, 0, idCmdFirst, ShellMenuDropMove, uFlags);\r
+\r
+       // SVN move and rename here\r
+       // available if source is a single, versioned but not added item, target is versioned, source and target from same repository or target folder is added\r
+       if ((bSourceAndTargetFromSameRepository||(itemStatesFolder & ITEMIS_ADDED))&&(itemStatesFolder & ITEMIS_FOLDERINSVN)&&(itemStates & ITEMIS_INSVN)&&(itemStates & ITEMIS_ONLYONE)&&((~itemStates) & ITEMIS_ADDED))\r
+               InsertSVNMenu(FALSE, hMenu, indexMenu++, idCmd++, IDS_DROPMOVERENAMEMENU, 0, idCmdFirst, ShellMenuDropMoveRename, uFlags);\r
+\r
+       // SVN copy here\r
+       // available if source is versioned but not added, target is versioned, source and target from same repository or target folder is added\r
+       if ((bSourceAndTargetFromSameRepository||(itemStatesFolder & ITEMIS_ADDED))&&(itemStatesFolder & ITEMIS_FOLDERINSVN)&&(itemStates & ITEMIS_INSVN)&&((~itemStates) & ITEMIS_ADDED))\r
+               InsertSVNMenu(FALSE, hMenu, indexMenu++, idCmd++, IDS_DROPCOPYMENU, 0, idCmdFirst, ShellMenuDropCopy, uFlags);\r
+\r
+       // SVN copy and rename here, source and target from same repository\r
+       // available if source is a single, versioned but not added item, target is versioned or target folder is added\r
+       if ((bSourceAndTargetFromSameRepository||(itemStatesFolder & ITEMIS_ADDED))&&(itemStatesFolder & ITEMIS_FOLDERINSVN)&&(itemStates & ITEMIS_INSVN)&&(itemStates & ITEMIS_ONLYONE)&&((~itemStates) & ITEMIS_ADDED))\r
+               InsertSVNMenu(FALSE, hMenu, indexMenu++, idCmd++, IDS_DROPCOPYRENAMEMENU, 0, idCmdFirst, ShellMenuDropCopyRename, uFlags);\r
+\r
+       // SVN add here\r
+       // available if target is versioned and source is either unversioned or from another repository\r
+       if ((itemStatesFolder & ITEMIS_FOLDERINSVN)&&(((~itemStates) & ITEMIS_INSVN)||!bSourceAndTargetFromSameRepository))\r
+               InsertSVNMenu(FALSE, hMenu, indexMenu++, idCmd++, IDS_DROPCOPYADDMENU, 0, idCmdFirst, ShellMenuDropCopyAdd, uFlags);\r
+\r
+       // SVN export here\r
+       // available if source is versioned and a folder\r
+       if ((itemStates & ITEMIS_INSVN)&&(itemStates & ITEMIS_FOLDER))\r
+               InsertSVNMenu(FALSE, hMenu, indexMenu++, idCmd++, IDS_DROPEXPORTMENU, 0, idCmdFirst, ShellMenuDropExport, uFlags);\r
+\r
+       // SVN export all here\r
+       // available if source is versioned and a folder\r
+       if ((itemStates & ITEMIS_INSVN)&&(itemStates & ITEMIS_FOLDER))\r
+               InsertSVNMenu(FALSE, hMenu, indexMenu++, idCmd++, IDS_DROPEXPORTEXTENDEDMENU, 0, idCmdFirst, ShellMenuDropExportExtended, uFlags);\r
+\r
+       // apply patch\r
+       // available if source is a patchfile\r
+       if (itemStates & ITEMIS_PATCHFILE)\r
+               InsertSVNMenu(FALSE, hMenu, indexMenu++, idCmd++, IDS_MENUAPPLYPATCH, 0, idCmdFirst, ShellMenuApplyPatch, uFlags);\r
+\r
+       // separator\r
+       if (idCmd != idCmdFirst)\r
+               InsertMenu(hMenu, indexMenu++, MF_SEPARATOR|MF_BYPOSITION, 0, NULL); \r
+\r
+       return ResultFromScode(MAKE_SCODE(SEVERITY_SUCCESS, 0, (USHORT)(idCmd - idCmdFirst)));\r
+}\r
+\r
+STDMETHODIMP CShellExt::QueryContextMenu(HMENU hMenu,\r
+                                         UINT indexMenu,\r
+                                         UINT idCmdFirst,\r
+                                         UINT /*idCmdLast*/,\r
+                                         UINT uFlags)\r
+{\r
+       ATLTRACE("Shell :: QueryContextMenu\n");\r
+       PreserveChdir preserveChdir;\r
+       \r
+       //first check if our drop handler is called\r
+       //and then (if true) provide the context menu for the\r
+       //drop handler\r
+       if (m_State == FileStateDropHandler)\r
+       {\r
+               return QueryDropContext(uFlags, idCmdFirst, hMenu, indexMenu);\r
+       }\r
+\r
+       if ((uFlags & CMF_DEFAULTONLY)!=0)\r
+               return NOERROR;                                 //we don't change the default action\r
+\r
+       if ((files_.size() == 0)&&(folder_.size() == 0))\r
+               return NOERROR;\r
+\r
+       if (((uFlags & 0x000f)!=CMF_NORMAL)&&(!(uFlags & CMF_EXPLORE))&&(!(uFlags & CMF_VERBSONLY)))\r
+               return NOERROR;\r
+\r
+       int csidlarray[] = \r
+       {\r
+               CSIDL_BITBUCKET,\r
+               CSIDL_CDBURN_AREA,\r
+               CSIDL_COMMON_FAVORITES,\r
+               CSIDL_COMMON_STARTMENU,\r
+               CSIDL_COMPUTERSNEARME,\r
+               CSIDL_CONNECTIONS,\r
+               CSIDL_CONTROLS,\r
+               CSIDL_COOKIES,\r
+               CSIDL_FAVORITES,\r
+               CSIDL_FONTS,\r
+               CSIDL_HISTORY,\r
+               CSIDL_INTERNET,\r
+               CSIDL_INTERNET_CACHE,\r
+               CSIDL_NETHOOD,\r
+               CSIDL_NETWORK,\r
+               CSIDL_PRINTERS,\r
+               CSIDL_PRINTHOOD,\r
+               CSIDL_RECENT,\r
+               CSIDL_SENDTO,\r
+               CSIDL_STARTMENU,\r
+               0\r
+       };\r
+       if (IsIllegalFolder(folder_, csidlarray))\r
+               return NOERROR;\r
+\r
+       if (folder_.empty())\r
+       {\r
+               // folder is empty, but maybe files are selected\r
+               if (files_.size() == 0)\r
+                       return NOERROR; // nothing selected - we don't have a menu to show\r
+               // check whether a selected entry is an UID - those are namespace extensions\r
+               // which we can't handle\r
+               for (std::vector<stdstring>::const_iterator it = files_.begin(); it != files_.end(); ++it)\r
+               {\r
+                       if (_tcsncmp(it->c_str(), _T("::{"), 3)==0)\r
+                               return NOERROR;\r
+               }\r
+       }\r
+\r
+       //check if our menu is requested for a subversion admin directory\r
+       if (g_SVNAdminDir.IsAdminDirPath(folder_.c_str()))\r
+               return NOERROR;\r
+\r
+       if (uFlags & CMF_EXTENDEDVERBS)\r
+               itemStates |= ITEMIS_EXTENDED;\r
+\r
+       const BOOL bShortcut = !!(uFlags & CMF_VERBSONLY);\r
+       if ( bShortcut && (files_.size()==1))\r
+       {\r
+               // Don't show the context menu for a link if the\r
+               // destination is not part of a working copy.\r
+               // It would only show the standard menu items\r
+               // which are already shown for the lnk-file.\r
+               CString path = files_.front().c_str();\r
+               if ( !g_SVNAdminDir.HasAdminDir(path) )\r
+               {\r
+                       return NOERROR;\r
+               }\r
+       }\r
+\r
+       //check if we already added our menu entry for a folder.\r
+       //we check that by iterating through all menu entries and check if \r
+       //the dwItemData member points to our global ID string. That string is set\r
+       //by our shell extension when the folder menu is inserted.\r
+       TCHAR menubuf[MAX_PATH];\r
+       int count = GetMenuItemCount(hMenu);\r
+       for (int i=0; i<count; ++i)\r
+       {\r
+               MENUITEMINFO miif;\r
+               SecureZeroMemory(&miif, sizeof(MENUITEMINFO));\r
+               miif.cbSize = sizeof(MENUITEMINFO);\r
+               miif.fMask = MIIM_DATA;\r
+               miif.dwTypeData = menubuf;\r
+               miif.cch = sizeof(menubuf)/sizeof(TCHAR);\r
+               GetMenuItemInfo(hMenu, i, TRUE, &miif);\r
+               if (miif.dwItemData == (ULONG_PTR)g_MenuIDString)\r
+                       return NOERROR;\r
+       }\r
+\r
+       LoadLangDll();\r
+       UINT idCmd = idCmdFirst;\r
+\r
+       //create the sub menu\r
+       HMENU subMenu = CreateMenu();\r
+       int indexSubMenu = 0;\r
+\r
+       unsigned __int64 topmenu = g_ShellCache.GetMenuLayout();\r
+       unsigned __int64 menumask = g_ShellCache.GetMenuMask();\r
+\r
+       int menuIndex = 0;\r
+       bool bAddSeparator = false;\r
+       bool bMenuEntryAdded = false;\r
+       // insert separator at start\r
+       InsertMenu(hMenu, indexMenu++, MF_SEPARATOR|MF_BYPOSITION, 0, NULL); idCmd++;\r
+       bool bShowIcons = !!DWORD(CRegStdWORD(_T("Software\\TortoiseSVN\\ShowContextMenuIcons"), TRUE));\r
+       if (fullver <= 0x0500)\r
+               bShowIcons = false;\r
+       while (menuInfo[menuIndex].command != ShellMenuLastEntry)\r
+       {\r
+               if (menuInfo[menuIndex].command == ShellSeparator)\r
+               {\r
+                       // we don't add a separator immediately. Because there might not be\r
+                       // another 'normal' menu entry after we insert a separator.\r
+                       // we simply set a flag here, indicating that before the next\r
+                       // 'normal' menu entry, a separator should be added.\r
+                       bAddSeparator = true;\r
+               }\r
+               else\r
+               {\r
+                       // check the conditions whether to show the menu entry or not\r
+                       bool bInsertMenu = false;\r
+\r
+                       if (menuInfo[menuIndex].firstyes && menuInfo[menuIndex].firstno)\r
+                       {\r
+                               if (((menuInfo[menuIndex].firstyes & itemStates) == menuInfo[menuIndex].firstyes)\r
+                                       &&\r
+                                       ((menuInfo[menuIndex].firstno & (~itemStates)) == menuInfo[menuIndex].firstno))\r
+                                       bInsertMenu = true;\r
+                       }\r
+                       else if ((menuInfo[menuIndex].firstyes)&&((menuInfo[menuIndex].firstyes & itemStates) == menuInfo[menuIndex].firstyes))\r
+                               bInsertMenu = true;\r
+                       else if ((menuInfo[menuIndex].firstno)&&((menuInfo[menuIndex].firstno & (~itemStates)) == menuInfo[menuIndex].firstno))\r
+                               bInsertMenu = true;\r
+\r
+                       if (menuInfo[menuIndex].secondyes && menuInfo[menuIndex].secondno)\r
+                       {\r
+                               if (((menuInfo[menuIndex].secondyes & itemStates) == menuInfo[menuIndex].secondyes)\r
+                                       &&\r
+                                       ((menuInfo[menuIndex].secondno & (~itemStates)) == menuInfo[menuIndex].secondno))\r
+                                       bInsertMenu = true;\r
+                       }\r
+                       else if ((menuInfo[menuIndex].secondyes)&&((menuInfo[menuIndex].secondyes & itemStates) == menuInfo[menuIndex].secondyes))\r
+                               bInsertMenu = true;\r
+                       else if ((menuInfo[menuIndex].secondno)&&((menuInfo[menuIndex].secondno & (~itemStates)) == menuInfo[menuIndex].secondno))\r
+                               bInsertMenu = true;\r
+\r
+                       if (menuInfo[menuIndex].thirdyes && menuInfo[menuIndex].thirdno)\r
+                       {\r
+                               if (((menuInfo[menuIndex].thirdyes & itemStates) == menuInfo[menuIndex].thirdyes)\r
+                                       &&\r
+                                       ((menuInfo[menuIndex].thirdno & (~itemStates)) == menuInfo[menuIndex].thirdno))\r
+                                       bInsertMenu = true;\r
+                       }\r
+                       else if ((menuInfo[menuIndex].thirdyes)&&((menuInfo[menuIndex].thirdyes & itemStates) == menuInfo[menuIndex].thirdyes))\r
+                               bInsertMenu = true;\r
+                       else if ((menuInfo[menuIndex].thirdno)&&((menuInfo[menuIndex].thirdno & (~itemStates)) == menuInfo[menuIndex].thirdno))\r
+                               bInsertMenu = true;\r
+\r
+                       if (menuInfo[menuIndex].fourthyes && menuInfo[menuIndex].fourthno)\r
+                       {\r
+                               if (((menuInfo[menuIndex].fourthyes & itemStates) == menuInfo[menuIndex].fourthyes)\r
+                                       &&\r
+                                       ((menuInfo[menuIndex].fourthno & (~itemStates)) == menuInfo[menuIndex].fourthno))\r
+                                       bInsertMenu = true;\r
+                       }\r
+                       else if ((menuInfo[menuIndex].fourthyes)&&((menuInfo[menuIndex].fourthyes & itemStates) == menuInfo[menuIndex].fourthyes))\r
+                               bInsertMenu = true;\r
+                       else if ((menuInfo[menuIndex].fourthno)&&((menuInfo[menuIndex].fourthno & (~itemStates)) == menuInfo[menuIndex].fourthno))\r
+                               bInsertMenu = true;\r
+\r
+                       if (menuInfo[menuIndex].menuID & (~menumask))\r
+                       {\r
+                               if (bInsertMenu)\r
+                               {\r
+                                       // insert a separator\r
+                                       if ((bMenuEntryAdded)&&(bAddSeparator))\r
+                                       {\r
+                                               bAddSeparator = false;\r
+                                               bMenuEntryAdded = false;\r
+                                               InsertMenu(subMenu, indexSubMenu++, MF_SEPARATOR|MF_BYPOSITION, 0, NULL); \r
+                                               idCmd++;\r
+                                       }\r
+                                       \r
+                                       // handle special cases (sub menus)\r
+                                       if ((menuInfo[menuIndex].command == ShellMenuIgnoreSub)||(menuInfo[menuIndex].command == ShellMenuUnIgnoreSub))\r
+                                       {\r
+                                               InsertIgnoreSubmenus(idCmd, idCmdFirst, hMenu, subMenu, indexMenu, indexSubMenu, topmenu, bShowIcons);\r
+                                               bMenuEntryAdded = true;\r
+                                       }\r
+                                       else\r
+                                       {\r
+                                               // the 'get lock' command is special\r
+                                               bool bIsTop = ((topmenu & menuInfo[menuIndex].menuID) != 0);\r
+                                               if (menuInfo[menuIndex].command == ShellMenuLock)\r
+                                               {\r
+                                                       if ((itemStates & ITEMIS_NEEDSLOCK) && g_ShellCache.IsGetLockTop())\r
+                                                               bIsTop = true;\r
+                                               }\r
+                                               // insert the menu entry\r
+                                               InsertSVNMenu(  bIsTop,\r
+                                                                               bIsTop ? hMenu : subMenu,\r
+                                                                               bIsTop ? indexMenu++ : indexSubMenu++,\r
+                                                                               idCmd++,\r
+                                                                               menuInfo[menuIndex].menuTextID,\r
+                                                                               bShowIcons ? menuInfo[menuIndex].iconID : 0,\r
+                                                                               idCmdFirst,\r
+                                                                               menuInfo[menuIndex].command,\r
+                                                                               uFlags);\r
+                                               if (!bIsTop)\r
+                                                       bMenuEntryAdded = true;\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+               menuIndex++;\r
+       }\r
+\r
+       //add sub menu to main context menu\r
+       //don't use InsertMenu because this will lead to multiple menu entries in the explorer file menu.\r
+       //see http://support.microsoft.com/default.aspx?scid=kb;en-us;214477 for details of that.\r
+       MAKESTRING(IDS_MENUSUBMENU);\r
+       MENUITEMINFO menuiteminfo;\r
+       SecureZeroMemory(&menuiteminfo, sizeof(menuiteminfo));\r
+       menuiteminfo.cbSize = sizeof(menuiteminfo);\r
+       menuiteminfo.fType = MFT_STRING;\r
+       menuiteminfo.dwTypeData = stringtablebuffer;\r
+\r
+       UINT uIcon = bShowIcons ? IDI_APP : 0;\r
+       if (folder_.size())\r
+       {\r
+               uIcon = bShowIcons ? IDI_MENUFOLDER : 0;\r
+               myIDMap[idCmd - idCmdFirst] = ShellSubMenuFolder;\r
+               myIDMap[idCmd] = ShellSubMenuFolder;\r
+               menuiteminfo.dwItemData = (ULONG_PTR)g_MenuIDString;\r
+       }\r
+       else if (!bShortcut && (files_.size()==1))\r
+       {\r
+               uIcon = bShowIcons ? IDI_MENUFILE : 0;\r
+               myIDMap[idCmd - idCmdFirst] = ShellSubMenuFile;\r
+               myIDMap[idCmd] = ShellSubMenuFile;\r
+       }\r
+       else if (bShortcut && (files_.size()==1))\r
+       {\r
+               uIcon = bShowIcons ? IDI_MENULINK : 0;\r
+               myIDMap[idCmd - idCmdFirst] = ShellSubMenuLink;\r
+               myIDMap[idCmd] = ShellSubMenuLink;\r
+       }\r
+       else if (files_.size() > 1)\r
+       {\r
+               uIcon = bShowIcons ? IDI_MENUMULTIPLE : 0;\r
+               myIDMap[idCmd - idCmdFirst] = ShellSubMenuMultiple;\r
+               myIDMap[idCmd] = ShellSubMenuMultiple;\r
+       }\r
+       else\r
+       {\r
+               myIDMap[idCmd - idCmdFirst] = ShellSubMenu;\r
+               myIDMap[idCmd] = ShellSubMenu;\r
+       }\r
+       HBITMAP bmp = NULL;\r
+       if ((fullver < 0x500)||(fullver == 0x500 && !uFlags))\r
+       {\r
+               bmp = IconToBitmap(uIcon);\r
+               menuiteminfo.fMask = MIIM_STRING | MIIM_ID | MIIM_SUBMENU | MIIM_CHECKMARKS | MIIM_DATA;\r
+       }\r
+       else\r
+       {\r
+               menuiteminfo.fMask = MIIM_FTYPE | MIIM_ID | MIIM_SUBMENU | MIIM_DATA | MIIM_BITMAP | MIIM_STRING;\r
+               if (bShowIcons)\r
+                       menuiteminfo.hbmpItem = (fullver >= 0x600) ? IconToBitmapPARGB32(uIcon) : HBMMENU_CALLBACK;\r
+       }\r
+       menuiteminfo.hbmpChecked = bmp;\r
+       menuiteminfo.hbmpUnchecked = bmp;\r
+       menuiteminfo.hSubMenu = subMenu;\r
+       menuiteminfo.wID = idCmd++;\r
+       InsertMenuItem(hMenu, indexMenu++, TRUE, &menuiteminfo);\r
+\r
+       //separator after\r
+       InsertMenu(hMenu, indexMenu++, MF_SEPARATOR|MF_BYPOSITION, 0, NULL); idCmd++;\r
+\r
+       //return number of menu items added\r
+       return ResultFromScode(MAKE_SCODE(SEVERITY_SUCCESS, 0, (USHORT)(idCmd - idCmdFirst)));\r
+}\r
+\r
+\r
+// This is called when you invoke a command on the menu:\r
+STDMETHODIMP CShellExt::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi)\r
+{\r
+       PreserveChdir preserveChdir;\r
+       HRESULT hr = E_INVALIDARG;\r
+       if (lpcmi == NULL)\r
+               return hr;\r
+\r
+       std::string command;\r
+       std::string parent;\r
+       std::string file;\r
+\r
+       if ((files_.size() > 0)||(folder_.size() > 0))\r
+       {\r
+               UINT idCmd = LOWORD(lpcmi->lpVerb);\r
+\r
+               if (HIWORD(lpcmi->lpVerb))\r
+               {\r
+                       stdstring verb = stdstring(MultibyteToWide(lpcmi->lpVerb));\r
+                       std::map<stdstring, UINT_PTR>::const_iterator verb_it = myVerbsMap.lower_bound(verb);\r
+                       if (verb_it != myVerbsMap.end() && verb_it->first == verb)\r
+                               idCmd = verb_it->second;\r
+                       else\r
+                               return hr;\r
+               }\r
+\r
+               // See if we have a handler interface for this id\r
+               std::map<UINT_PTR, UINT_PTR>::const_iterator id_it = myIDMap.lower_bound(idCmd);\r
+               if (id_it != myIDMap.end() && id_it->first == idCmd)\r
+               {\r
+                       STARTUPINFO startup;\r
+                       PROCESS_INFORMATION process;\r
+                       memset(&startup, 0, sizeof(startup));\r
+                       startup.cb = sizeof(startup);\r
+                       memset(&process, 0, sizeof(process));\r
+                       CRegStdString tortoiseProcPath(_T("Software\\TortoiseSVN\\ProcPath"), _T("TortoiseProc.exe"), false, HKEY_LOCAL_MACHINE);\r
+                       CRegStdString tortoiseMergePath(_T("Software\\TortoiseSVN\\TMergePath"), _T("TortoiseMerge.exe"), false, HKEY_LOCAL_MACHINE);\r
+\r
+                       //TortoiseProc expects a command line of the form:\r
+                       //"/command:<commandname> /pathfile:<path> /startrev:<startrevision> /endrev:<endrevision> /deletepathfile\r
+                       // or\r
+                       //"/command:<commandname> /path:<path> /startrev:<startrevision> /endrev:<endrevision>\r
+                       //\r
+                       //* path is a path to a single file/directory for commands which only act on single items (log, checkout, ...)\r
+                       //* pathfile is a path to a temporary file which contains a list of file paths\r
+                       stdstring svnCmd = _T(" /command:");\r
+                       stdstring tempfile;\r
+                       switch (id_it->second)\r
+                       {\r
+                               //#region case\r
+                       case ShellMenuCheckout:\r
+                               svnCmd += _T("checkout /path:\"");\r
+                               svnCmd += folder_;\r
+                               svnCmd += _T("\"");\r
+                               break;\r
+                       case ShellMenuUpdate:\r
+                               tempfile = WriteFileListToTempFile();\r
+                               svnCmd += _T("update /pathfile:\"");\r
+                               svnCmd += tempfile;\r
+                               svnCmd += _T("\"");\r
+                               svnCmd += _T(" /deletepathfile");\r
+                               break;\r
+                       case ShellMenuUpdateExt:\r
+                               tempfile = WriteFileListToTempFile();\r
+                               svnCmd += _T("update /pathfile:\"");\r
+                               svnCmd += tempfile;\r
+                               svnCmd += _T("\"");\r
+                               svnCmd += _T(" /deletepathfile");\r
+                               svnCmd += _T(" /rev");\r
+                               break;\r
+                       case ShellMenuCommit:\r
+                               tempfile = WriteFileListToTempFile();\r
+                               svnCmd += _T("commit /pathfile:\"");\r
+                               svnCmd += tempfile;\r
+                               svnCmd += _T("\"");\r
+                               svnCmd += _T(" /deletepathfile");\r
+                               break;\r
+                       case ShellMenuAdd:\r
+                       case ShellMenuAddAsReplacement:\r
+                               tempfile = WriteFileListToTempFile();\r
+                               svnCmd += _T("add /pathfile:\"");\r
+                               svnCmd += tempfile;\r
+                               svnCmd += _T("\"");\r
+                               svnCmd += _T(" /deletepathfile");\r
+                               break;\r
+                       case ShellMenuIgnore:\r
+                               tempfile = WriteFileListToTempFile();\r
+                               svnCmd += _T("ignore /pathfile:\"");\r
+                               svnCmd += tempfile;\r
+                               svnCmd += _T("\"");\r
+                               svnCmd += _T(" /deletepathfile");\r
+                               break;\r
+                       case ShellMenuIgnoreCaseSensitive:\r
+                               tempfile = WriteFileListToTempFile();\r
+                               svnCmd += _T("ignore /pathfile:\"");\r
+                               svnCmd += tempfile;\r
+                               svnCmd += _T("\"");\r
+                               svnCmd += _T(" /deletepathfile");\r
+                               svnCmd += _T(" /onlymask");\r
+                               break;\r
+                       case ShellMenuUnIgnore:\r
+                               tempfile = WriteFileListToTempFile();\r
+                               svnCmd += _T("unignore /pathfile:\"");\r
+                               svnCmd += tempfile;\r
+                               svnCmd += _T("\"");\r
+                               svnCmd += _T(" /deletepathfile");\r
+                               break;\r
+                       case ShellMenuUnIgnoreCaseSensitive:\r
+                               tempfile = WriteFileListToTempFile();\r
+                               svnCmd += _T("unignore /pathfile:\"");\r
+                               svnCmd += tempfile;\r
+                               svnCmd += _T("\"");\r
+                               svnCmd += _T(" /deletepathfile");\r
+                               svnCmd += _T(" /onlymask");\r
+                               break;\r
+                       case ShellMenuRevert:\r
+                               tempfile = WriteFileListToTempFile();\r
+                               svnCmd += _T("revert /pathfile:\"");\r
+                               svnCmd += tempfile;\r
+                               svnCmd += _T("\"");\r
+                               svnCmd += _T(" /deletepathfile");\r
+                               break;\r
+                       case ShellMenuDelUnversioned:\r
+                               svnCmd += _T("delunversioned /path:\"");\r
+                               svnCmd += folder_;\r
+                               svnCmd += _T("\"");\r
+                               break;\r
+                       case ShellMenuCleanup:\r
+                               tempfile = WriteFileListToTempFile();\r
+                               svnCmd += _T("cleanup /pathfile:\"");\r
+                               svnCmd += tempfile;\r
+                               svnCmd += _T("\"");\r
+                               svnCmd += _T(" /deletepathfile");\r
+                               break;\r
+                       case ShellMenuResolve:\r
+                               tempfile = WriteFileListToTempFile();\r
+                               svnCmd += _T("resolve /pathfile:\"");\r
+                               svnCmd += tempfile;\r
+                               svnCmd += _T("\"");\r
+                               svnCmd += _T(" /deletepathfile");\r
+                               break;\r
+                       case ShellMenuSwitch:\r
+                               svnCmd += _T("switch /path:\"");\r
+                               if (files_.size() > 0)\r
+                                       svnCmd += files_.front();\r
+                               else\r
+                                       svnCmd += folder_;\r
+                               svnCmd += _T("\"");\r
+                               break;\r
+                       case ShellMenuImport:\r
+                               svnCmd += _T("import /path:\"");\r
+                               svnCmd += folder_;\r
+                               svnCmd += _T("\"");\r
+                               break;\r
+                       case ShellMenuExport:\r
+                               svnCmd += _T("export /path:\"");\r
+                               svnCmd += folder_;\r
+                               svnCmd += _T("\"");\r
+                               break;\r
+                       case ShellMenuAbout:\r
+                               svnCmd += _T("about");\r
+                               break;\r
+                       case ShellMenuCreateRepos:\r
+                               svnCmd += _T("repocreate /path:\"");\r
+                               svnCmd += folder_;\r
+                               svnCmd += _T("\"");\r
+                               break;\r
+                       case ShellMenuMerge:\r
+                               svnCmd += _T("merge /path:\"");\r
+                               if (files_.size() > 0)\r
+                                       svnCmd += files_.front();\r
+                               else\r
+                                       svnCmd += folder_;\r
+                               svnCmd += _T("\"");\r
+                               break;\r
+                       case ShellMenuMergeAll:\r
+                               svnCmd += _T("mergeall /path:\"");\r
+                               if (files_.size() > 0)\r
+                                       svnCmd += files_.front();\r
+                               else\r
+                                       svnCmd += folder_;\r
+                               svnCmd += _T("\"");\r
+                               break;\r
+                       case ShellMenuCopy:\r
+                               svnCmd += _T("copy /path:\"");\r
+                               if (files_.size() > 0)\r
+                                       svnCmd += files_.front();\r
+                               else\r
+                                       svnCmd += folder_;\r
+                               svnCmd += _T("\"");\r
+                               break;\r
+                       case ShellMenuSettings:\r
+                               svnCmd += _T("settings");\r
+                               break;\r
+                       case ShellMenuHelp:\r
+                               svnCmd += _T("help");\r
+                               break;\r
+                       case ShellMenuRename:\r
+                               svnCmd += _T("rename /path:\"");\r
+                               if (files_.size() > 0)\r
+                                       svnCmd += files_.front();\r
+                               else\r
+                                       svnCmd += folder_;\r
+                               svnCmd += _T("\"");\r
+                               break;\r
+                       case ShellMenuRemove:\r
+                               tempfile = WriteFileListToTempFile();\r
+                               svnCmd += _T("remove /pathfile:\"");\r
+                               svnCmd += tempfile;\r
+                               svnCmd += _T("\"");\r
+                               svnCmd += _T(" /deletepathfile");\r
+                               break;\r
+                       case ShellMenuRemoveKeep:\r
+                               tempfile = WriteFileListToTempFile();\r
+                               svnCmd += _T("remove /pathfile:\"");\r
+                               svnCmd += tempfile;\r
+                               svnCmd += _T("\"");\r
+                               svnCmd += _T(" /deletepathfile");\r
+                               svnCmd += _T(" /keep");\r
+                               break;\r
+                       case ShellMenuDiff:\r
+                               svnCmd += _T("diff /path:\"");\r
+                               if (files_.size() == 1)\r
+                                       svnCmd += files_.front();\r
+                               else if (files_.size() == 2)\r
+                               {\r
+                                       std::vector<stdstring>::iterator I = files_.begin();\r
+                                       svnCmd += *I;\r
+                                       I++;\r
+                                       svnCmd += _T("\" /path2:\"");\r
+                                       svnCmd += *I;\r
+                               }\r
+                               else\r
+                                       svnCmd += folder_;\r
+                               svnCmd += _T("\"");\r
+                               if (GetAsyncKeyState(VK_SHIFT) & 0x8000)\r
+                                       svnCmd += _T(" /alternative");\r
+                               break;\r
+                       case ShellMenuPrevDiff:\r
+                               svnCmd += _T("prevdiff /path:\"");\r
+                               if (files_.size() == 1)\r
+                                       svnCmd += files_.front();\r
+                               else\r
+                                       svnCmd += folder_;\r
+                               svnCmd += _T("\"");\r
+                               if (GetAsyncKeyState(VK_SHIFT) & 0x8000)\r
+                                       svnCmd += _T(" /alternative");\r
+                               break;\r
+                       case ShellMenuUrlDiff:\r
+                               svnCmd += _T("urldiff /path:\"");\r
+                               if (files_.size() == 1)\r
+                                       svnCmd += files_.front();\r
+                               else\r
+                                       svnCmd += folder_;\r
+                               svnCmd += _T("\"");\r
+                               break;\r
+                       case ShellMenuDropCopyAdd:\r
+                               tempfile = WriteFileListToTempFile();\r
+                               svnCmd += _T("dropcopyadd /pathfile:\"");\r
+                               svnCmd += tempfile;\r
+                               svnCmd += _T("\"");\r
+                               svnCmd += _T(" /deletepathfile");\r
+                               svnCmd += _T(" /droptarget:\"");\r
+                               svnCmd += folder_;\r
+                               svnCmd += _T("\"";)\r
+                                       break;\r
+                       case ShellMenuDropCopy:\r
+                               tempfile = WriteFileListToTempFile();\r
+                               svnCmd += _T("dropcopy /pathfile:\"");\r
+                               svnCmd += tempfile;\r
+                               svnCmd += _T("\"");\r
+                               svnCmd += _T(" /deletepathfile");\r
+                               svnCmd += _T(" /droptarget:\"");\r
+                               svnCmd += folder_;\r
+                               svnCmd += _T("\"";)\r
+                                       break;\r
+                       case ShellMenuDropCopyRename:\r
+                               tempfile = WriteFileListToTempFile();\r
+                               svnCmd += _T("dropcopy /pathfile:\"");\r
+                               svnCmd += tempfile;\r
+                               svnCmd += _T("\"");\r
+                               svnCmd += _T(" /deletepathfile");\r
+                               svnCmd += _T(" /droptarget:\"");\r
+                               svnCmd += folder_;\r
+                               svnCmd += _T("\" /rename";)\r
+                                       break;\r
+                       case ShellMenuDropMove:\r
+                               tempfile = WriteFileListToTempFile();\r
+                               svnCmd += _T("dropmove /pathfile:\"");\r
+                               svnCmd += tempfile;\r
+                               svnCmd += _T("\"");\r
+                               svnCmd += _T(" /deletepathfile");\r
+                               svnCmd += _T(" /droptarget:\"");\r
+                               svnCmd += folder_;\r
+                               svnCmd += _T("\"");\r
+                               break;\r
+                       case ShellMenuDropMoveRename:\r
+                               tempfile = WriteFileListToTempFile();\r
+                               svnCmd += _T("dropmove /pathfile:\"");\r
+                               svnCmd += tempfile;\r
+                               svnCmd += _T("\"");\r
+                               svnCmd += _T(" /deletepathfile");\r
+                               svnCmd += _T(" /droptarget:\"");\r
+                               svnCmd += folder_;\r
+                               svnCmd += _T("\" /rename";)\r
+                               break;\r
+                       case ShellMenuDropExport:\r
+                               tempfile = WriteFileListToTempFile();\r
+                               svnCmd += _T("dropexport /pathfile:\"");\r
+                               svnCmd += tempfile;\r
+                               svnCmd += _T("\"");\r
+                               svnCmd += _T(" /deletepathfile");\r
+                               svnCmd += _T(" /droptarget:\"");\r
+                               svnCmd += folder_;\r
+                               svnCmd += _T("\"");\r
+                               break;\r
+                       case ShellMenuDropExportExtended:\r
+                               tempfile = WriteFileListToTempFile();\r
+                               svnCmd += _T("dropexport /pathfile:\"");\r
+                               svnCmd += tempfile;\r
+                               svnCmd += _T("\"");\r
+                               svnCmd += _T(" /deletepathfile");\r
+                               svnCmd += _T(" /droptarget:\"");\r
+                               svnCmd += folder_;\r
+                               svnCmd += _T("\"");\r
+                               svnCmd += _T(" /extended");\r
+                               break;\r
+                       case ShellMenuLog:\r
+                               svnCmd += _T("log /path:\"");\r
+                               if (files_.size() > 0)\r
+                                       svnCmd += files_.front();\r
+                               else\r
+                                       svnCmd += folder_;\r
+                               svnCmd += _T("\"");\r
+                               break;\r
+                       case ShellMenuConflictEditor:\r
+                               svnCmd += _T("conflicteditor /path:\"");\r
+                               if (files_.size() > 0)\r
+                                       svnCmd += files_.front();\r
+                               else\r
+                                       svnCmd += folder_;\r
+                               svnCmd += _T("\"");\r
+                               break;\r
+                       case ShellMenuRelocate:\r
+                               svnCmd += _T("relocate /path:\"");\r
+                               if (files_.size() > 0)\r
+                                       svnCmd += files_.front();\r
+                               else\r
+                                       svnCmd += folder_;\r
+                               svnCmd += _T("\"");\r
+                               break;\r
+                       case ShellMenuShowChanged:\r
+                               if (files_.size() > 1)\r
+                {\r
+                                   tempfile = WriteFileListToTempFile();\r
+                                   svnCmd += _T("repostatus /pathfile:\"");\r
+                                   svnCmd += tempfile;\r
+                               svnCmd += _T("\"");\r
+                               svnCmd += _T(" /deletepathfile");\r
+                }\r
+                else\r
+                {\r
+                    svnCmd += _T("repostatus /path:\"");\r
+                                   if (files_.size() > 0)\r
+                                           svnCmd += files_.front();\r
+                                   else\r
+                                           svnCmd += folder_;\r
+                               svnCmd += _T("\"");\r
+                }\r
+                               break;\r
+                       case ShellMenuRepoBrowse:\r
+                               svnCmd += _T("repobrowser /path:\"");\r
+                               if (files_.size() > 0)\r
+                                       svnCmd += files_.front();\r
+                               else\r
+                                       svnCmd += folder_;\r
+                               svnCmd += _T("\"");\r
+                               break;\r
+                       case ShellMenuBlame:\r
+                               svnCmd += _T("blame /path:\"");\r
+                               if (files_.size() > 0)\r
+                                       svnCmd += files_.front();\r
+                               else\r
+                                       svnCmd += folder_;\r
+                               svnCmd += _T("\"");\r
+                               break;\r
+                       case ShellMenuCreatePatch:\r
+                               tempfile = WriteFileListToTempFile();\r
+                               svnCmd += _T("createpatch /pathfile:\"");\r
+                               svnCmd += tempfile;\r
+                               svnCmd += _T("\"");\r
+                               svnCmd += _T(" /deletepathfile");\r
+                               break;\r
+                       case ShellMenuApplyPatch:\r
+                               if ((itemStates & ITEMIS_PATCHINCLIPBOARD) && ((~itemStates) & ITEMIS_PATCHFILE))\r
+                               {\r
+                                       // if there's a patch file in the clipboard, we save it\r
+                                       // to a temporary file and tell TortoiseMerge to use that one\r
+                                       UINT cFormat = RegisterClipboardFormat(_T("TSVN_UNIFIEDDIFF"));\r
+                                       if ((cFormat)&&(OpenClipboard(NULL)))\r
+                                       { \r
+                                               HGLOBAL hglb = GetClipboardData(cFormat); \r
+                                               LPCSTR lpstr = (LPCSTR)GlobalLock(hglb); \r
+\r
+                                               DWORD len = GetTempPath(0, NULL);\r
+                                               TCHAR * path = new TCHAR[len+1];\r
+                                               TCHAR * tempF = new TCHAR[len+100];\r
+                                               GetTempPath (len+1, path);\r
+                                               GetTempFileName (path, TEXT("svn"), 0, tempF);\r
+                                               std::wstring sTempFile = std::wstring(tempF);\r
+                                               delete [] path;\r
+                                               delete [] tempF;\r
+\r
+                                               FILE * outFile;\r
+                                               size_t patchlen = strlen(lpstr);\r
+                                               _tfopen_s(&outFile, sTempFile.c_str(), _T("wb"));\r
+                                               if(outFile)\r
+                                               {\r
+                                                       size_t size = fwrite(lpstr, sizeof(char), patchlen, outFile);\r
+                                                       if (size == patchlen)\r
+                                                       {\r
+                                                               itemStates |= ITEMIS_PATCHFILE;\r
+                                                               files_.clear();\r
+                                                               files_.push_back(sTempFile);\r
+                                                       }\r
+                                                       fclose(outFile);\r
+                                               }\r
+                                               GlobalUnlock(hglb); \r
+                                               CloseClipboard(); \r
+                                       } \r
+                               }\r
+                               if (itemStates & ITEMIS_PATCHFILE)\r
+                               {\r
+                                       svnCmd = _T(" /diff:\"");\r
+                                       if (files_.size() > 0)\r
+                                       {\r
+                                               svnCmd += files_.front();\r
+                                               if (itemStatesFolder & ITEMIS_FOLDERINSVN)\r
+                                               {\r
+                                                       svnCmd += _T("\" /patchpath:\"");\r
+                                                       svnCmd += folder_;\r
+                                               }\r
+                                       }\r
+                                       else\r
+                                               svnCmd += folder_;\r
+                                       if (itemStates & ITEMIS_INVERSIONEDFOLDER)\r
+                                               svnCmd += _T("\" /wc");\r
+                                       else\r
+                                               svnCmd += _T("\"");\r
+                               }\r
+                               else\r
+                               {\r
+                                       svnCmd = _T(" /patchpath:\"");\r
+                                       if (files_.size() > 0)\r
+                                               svnCmd += files_.front();\r
+                                       else\r
+                                               svnCmd += folder_;\r
+                                       svnCmd += _T("\"");\r
+                               }\r
+                               myIDMap.clear();\r
+                               myVerbsIDMap.clear();\r
+                               myVerbsMap.clear();\r
+                               if (CreateProcess(((stdstring)tortoiseMergePath).c_str(), const_cast<TCHAR*>(svnCmd.c_str()), NULL, NULL, FALSE, 0, 0, 0, &startup, &process)==0)\r
+                               {\r
+                                       LPVOID lpMsgBuf;\r
+                                       FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | \r
+                                               FORMAT_MESSAGE_FROM_SYSTEM | \r
+                                               FORMAT_MESSAGE_IGNORE_INSERTS,\r
+                                               NULL,\r
+                                               GetLastError(),\r
+                                               MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language\r
+                                               (LPTSTR) &lpMsgBuf,\r
+                                               0,\r
+                                               NULL \r
+                                               );\r
+                                       MessageBox( NULL, (LPCTSTR)lpMsgBuf, _T("TortoiseMerge launch failed"), MB_OK | MB_ICONINFORMATION );\r
+                                       LocalFree( lpMsgBuf );\r
+                               }\r
+                               CloseHandle(process.hThread);\r
+                               CloseHandle(process.hProcess);\r
+                               return NOERROR;\r
+                               break;\r
+                       case ShellMenuRevisionGraph:\r
+                               svnCmd += _T("revisiongraph /path:\"");\r
+                               if (files_.size() > 0)\r
+                                       svnCmd += files_.front();\r
+                               else\r
+                                       svnCmd += folder_;\r
+                               svnCmd += _T("\"");\r
+                               break;\r
+                       case ShellMenuLock:\r
+                               tempfile = WriteFileListToTempFile();\r
+                               svnCmd += _T("lock /pathfile:\"");\r
+                               svnCmd += tempfile;\r
+                               svnCmd += _T("\"");\r
+                               svnCmd += _T(" /deletepathfile");\r
+                               break;\r
+                       case ShellMenuUnlock:\r
+                               tempfile = WriteFileListToTempFile();\r
+                               svnCmd += _T("unlock /pathfile:\"");\r
+                               svnCmd += tempfile;\r
+                               svnCmd += _T("\"");\r
+                               svnCmd += _T(" /deletepathfile");\r
+                               break;\r
+                       case ShellMenuUnlockForce:\r
+                               tempfile = WriteFileListToTempFile();\r
+                               svnCmd += _T("unlock /pathfile:\"");\r
+                               svnCmd += tempfile;\r
+                               svnCmd += _T("\"");\r
+                               svnCmd += _T(" /deletepathfile");\r
+                               svnCmd += _T(" /force");\r
+                               break;\r
+                       case ShellMenuProperties:\r
+                               tempfile = WriteFileListToTempFile();\r
+                               svnCmd += _T("properties /pathfile:\"");\r
+                               svnCmd += tempfile;\r
+                               svnCmd += _T("\"");\r
+                               svnCmd += _T(" /deletepathfile");\r
+                               break;\r
+                       case ShellMenuClipPaste:\r
+                               if (WriteClipboardPathsToTempFile(tempfile))\r
+                               {\r
+                                       bool bCopy = true;\r
+                                       UINT cPrefDropFormat = RegisterClipboardFormat(_T("Preferred DropEffect"));\r
+                                       if (cPrefDropFormat)\r
+                                       {\r
+                                               if (OpenClipboard(lpcmi->hwnd))\r
+                                               {\r
+                                                       HGLOBAL hglb = GetClipboardData(cPrefDropFormat);\r
+                                                       if (hglb)\r
+                                                       {\r
+                                                               DWORD* effect = (DWORD*) GlobalLock(hglb);\r
+                                                               if (*effect == DROPEFFECT_MOVE)\r
+                                                                       bCopy = false;\r
+                                                               GlobalUnlock(hglb);\r
+                                                       }\r
+                                                       CloseClipboard();\r
+                                               }\r
+                                       }\r
+\r
+                                       if (bCopy)\r
+                                               svnCmd += _T("pastecopy /pathfile:\"");\r
+                                       else\r
+                                               svnCmd += _T("pastemove /pathfile:\"");\r
+                                       svnCmd += tempfile;\r
+                                       svnCmd += _T("\"");\r
+                                       svnCmd += _T(" /deletepathfile");\r
+                                       svnCmd += _T(" /droptarget:\"");\r
+                                       svnCmd += folder_;\r
+                                       svnCmd += _T("\"");\r
+                               }\r
+                               else return NOERROR;\r
+                               break;\r
+                       default:\r
+                               break;\r
+                               //#endregion\r
+                       } // switch (id_it->second) \r
+                       svnCmd += _T(" /hwnd:");\r
+                       TCHAR buf[30];\r
+                       _stprintf_s(buf, 30, _T("%d"), lpcmi->hwnd);\r
+                       svnCmd += buf;\r
+                       myIDMap.clear();\r
+                       myVerbsIDMap.clear();\r
+                       myVerbsMap.clear();\r
+                       if (CreateProcess(((stdstring)tortoiseProcPath).c_str(), const_cast<TCHAR*>(svnCmd.c_str()), NULL, NULL, FALSE, 0, 0, 0, &startup, &process)==0)\r
+                       {\r
+                               LPVOID lpMsgBuf;\r
+                               FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | \r
+                                       FORMAT_MESSAGE_FROM_SYSTEM | \r
+                                       FORMAT_MESSAGE_IGNORE_INSERTS,\r
+                                       NULL,\r
+                                       GetLastError(),\r
+                                       MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language\r
+                                       (LPTSTR) &lpMsgBuf,\r
+                                       0,\r
+                                       NULL \r
+                                       );\r
+                               MessageBox( NULL, (LPCTSTR)lpMsgBuf, _T("TortoiseProc Launch failed"), MB_OK | MB_ICONINFORMATION );\r
+                               LocalFree( lpMsgBuf );\r
+                       }\r
+                       CloseHandle(process.hThread);\r
+                       CloseHandle(process.hProcess);\r
+                       hr = NOERROR;\r
+               } // if (id_it != myIDMap.end() && id_it->first == idCmd) \r
+       } // if ((files_.size() > 0)||(folder_.size() > 0)) \r
+       return hr;\r
+\r
+}\r
+\r
+// This is for the status bar and things like that:\r
+STDMETHODIMP CShellExt::GetCommandString(UINT_PTR idCmd,\r
+                                         UINT uFlags,\r
+                                         UINT FAR * /*reserved*/,\r
+                                         LPSTR pszName,\r
+                                         UINT cchMax)\r
+{\r
+       PreserveChdir preserveChdir;\r
+       //do we know the id?\r
+       std::map<UINT_PTR, UINT_PTR>::const_iterator id_it = myIDMap.lower_bound(idCmd);\r
+       if (id_it == myIDMap.end() || id_it->first != idCmd)\r
+       {\r
+               return E_INVALIDARG;            //no, we don't\r
+       }\r
+\r
+       LoadLangDll();\r
+       HRESULT hr = E_INVALIDARG;\r
+\r
+       MAKESTRING(IDS_MENUDESCDEFAULT);\r
+       int menuIndex = 0;\r
+       while (menuInfo[menuIndex].command != ShellMenuLastEntry)\r
+       {\r
+               if (menuInfo[menuIndex].command == (SVNCommands)id_it->second)\r
+               {\r
+                       MAKESTRING(menuInfo[menuIndex].menuDescID);\r
+                       break;\r
+               }\r
+               menuIndex++;\r
+       }\r
+\r
+       const TCHAR * desc = stringtablebuffer;\r
+       switch(uFlags)\r
+       {\r
+       case GCS_HELPTEXTA:\r
+               {\r
+                       std::string help = WideToMultibyte(desc);\r
+                       lstrcpynA(pszName, help.c_str(), cchMax);\r
+                       hr = S_OK;\r
+                       break; \r
+               }\r
+       case GCS_HELPTEXTW: \r
+               {\r
+                       wide_string help = desc;\r
+                       lstrcpynW((LPWSTR)pszName, help.c_str(), cchMax); \r
+                       hr = S_OK;\r
+                       break; \r
+               }\r
+       case GCS_VERBA:\r
+               {\r
+                       std::map<UINT_PTR, stdstring>::const_iterator verb_id_it = myVerbsIDMap.lower_bound(idCmd);\r
+                       if (verb_id_it != myVerbsIDMap.end() && verb_id_it->first == idCmd)\r
+                       {\r
+                               std::string help = WideToMultibyte(verb_id_it->second);\r
+                               lstrcpynA(pszName, help.c_str(), cchMax);\r
+                               hr = S_OK;\r
+                       }\r
+               }\r
+               break;\r
+       case GCS_VERBW:\r
+               {\r
+                       std::map<UINT_PTR, stdstring>::const_iterator verb_id_it = myVerbsIDMap.lower_bound(idCmd);\r
+                       if (verb_id_it != myVerbsIDMap.end() && verb_id_it->first == idCmd)\r
+                       {\r
+                               wide_string help = verb_id_it->second;\r
+                               ATLTRACE("verb : %ws\n", help.c_str());\r
+                               lstrcpynW((LPWSTR)pszName, help.c_str(), cchMax); \r
+                               hr = S_OK;\r
+                       }\r
+               }\r
+               break;\r
+       }\r
+       return hr;\r
+}\r
+\r
+STDMETHODIMP CShellExt::HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam)\r
+{\r
+       LRESULT res;\r
+       return HandleMenuMsg2(uMsg, wParam, lParam, &res);\r
+}\r
+\r
+STDMETHODIMP CShellExt::HandleMenuMsg2(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *pResult)\r
+{\r
+       PreserveChdir preserveChdir;\r
+\r
+       LRESULT res;\r
+       if (pResult == NULL)\r
+               pResult = &res;\r
+       *pResult = FALSE;\r
+\r
+       LoadLangDll();\r
+       switch (uMsg)\r
+       {\r
+       case WM_MEASUREITEM:\r
+               {\r
+                       MEASUREITEMSTRUCT* lpmis = (MEASUREITEMSTRUCT*)lParam;\r
+                       if (lpmis==NULL)\r
+                               break;\r
+                       lpmis->itemWidth += 2;\r
+                       if (lpmis->itemHeight < 16)\r
+                               lpmis->itemHeight = 16;\r
+                       *pResult = TRUE;\r
+               }\r
+               break;\r
+       case WM_DRAWITEM:\r
+               {\r
+                       LPCTSTR resource;\r
+                       DRAWITEMSTRUCT* lpdis = (DRAWITEMSTRUCT*)lParam;\r
+                       if ((lpdis==NULL)||(lpdis->CtlType != ODT_MENU))\r
+                               return S_OK;            //not for a menu\r
+                       resource = GetMenuTextFromResource(myIDMap[lpdis->itemID]);\r
+                       if (resource == NULL)\r
+                               return S_OK;\r
+                       HICON hIcon = (HICON)LoadImage(g_hResInst, resource, IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);\r
+                       if (hIcon == NULL)\r
+                               return S_OK;\r
+                       DrawIconEx(lpdis->hDC,\r
+                               lpdis->rcItem.left - 16,\r
+                               lpdis->rcItem.top + (lpdis->rcItem.bottom - lpdis->rcItem.top - 16) / 2,\r
+                               hIcon, 16, 16,\r
+                               0, NULL, DI_NORMAL);\r
+                       DestroyIcon(hIcon);\r
+                       *pResult = TRUE;\r
+               }\r
+               break;\r
+       case WM_MENUCHAR:\r
+               {\r
+                       LPCTSTR resource;\r
+                       TCHAR *szItem;\r
+                       if (HIWORD(wParam) != MF_POPUP)\r
+                               return NOERROR;\r
+                       int nChar = LOWORD(wParam);\r
+                       if (_istascii((wint_t)nChar) && _istupper((wint_t)nChar))\r
+                               nChar = tolower(nChar);\r
+                       // we have the char the user pressed, now search that char in all our\r
+                       // menu items\r
+                       std::vector<int> accmenus;\r
+                       for (std::map<UINT_PTR, UINT_PTR>::iterator It = mySubMenuMap.begin(); It != mySubMenuMap.end(); ++It)\r
+                       {\r
+                               resource = GetMenuTextFromResource(mySubMenuMap[It->first]);\r
+                               if (resource == NULL)\r
+                                       continue;\r
+                               szItem = stringtablebuffer;\r
+                               TCHAR * amp = _tcschr(szItem, '&');\r
+                               if (amp == NULL)\r
+                                       continue;\r
+                               amp++;\r
+                               int ampChar = LOWORD(*amp);\r
+                               if (_istascii((wint_t)ampChar) && _istupper((wint_t)ampChar))\r
+                                       ampChar = tolower(ampChar);\r
+                               if (ampChar == nChar)\r
+                               {\r
+                                       // yep, we found a menu which has the pressed key\r
+                                       // as an accelerator. Add that menu to the list to\r
+                                       // process later.\r
+                                       accmenus.push_back(It->first);\r
+                               }\r
+                       }\r
+                       if (accmenus.size() == 0)\r
+                       {\r
+                               // no menu with that accelerator key.\r
+                               *pResult = MAKELONG(0, MNC_IGNORE);\r
+                               return NOERROR;\r
+                       }\r
+                       if (accmenus.size() == 1)\r
+                       {\r
+                               // Only one menu with that accelerator key. We're lucky!\r
+                               // So just execute that menu entry.\r
+                               *pResult = MAKELONG(accmenus[0], MNC_EXECUTE);\r
+                               return NOERROR;\r
+                       }\r
+                       if (accmenus.size() > 1)\r
+                       {\r
+                               // we have more than one menu item with this accelerator key!\r
+                               MENUITEMINFO mif;\r
+                               mif.cbSize = sizeof(MENUITEMINFO);\r
+                               mif.fMask = MIIM_STATE;\r
+                               for (std::vector<int>::iterator it = accmenus.begin(); it != accmenus.end(); ++it)\r
+                               {\r
+                                       GetMenuItemInfo((HMENU)lParam, *it, TRUE, &mif);\r
+                                       if (mif.fState == MFS_HILITE)\r
+                                       {\r
+                                               // this is the selected item, so select the next one\r
+                                               ++it;\r
+                                               if (it == accmenus.end())\r
+                                                       *pResult = MAKELONG(accmenus[0], MNC_SELECT);\r
+                                               else\r
+                                                       *pResult = MAKELONG(*it, MNC_SELECT);\r
+                                               return NOERROR;\r
+                                       }\r
+                               }\r
+                               *pResult = MAKELONG(accmenus[0], MNC_SELECT);\r
+                       }\r
+               }\r
+               break;\r
+       default:\r
+               return NOERROR;\r
+       }\r
+\r
+       return NOERROR;\r
+}\r
+\r
+LPCTSTR CShellExt::GetMenuTextFromResource(int id)\r
+{\r
+       TCHAR textbuf[255];\r
+       LPCTSTR resource = NULL;\r
+       unsigned __int64 layout = g_ShellCache.GetMenuLayout();\r
+       space = 6;\r
+\r
+       int menuIndex = 0;\r
+       while (menuInfo[menuIndex].command != ShellMenuLastEntry)\r
+       {\r
+               if (menuInfo[menuIndex].command == id)\r
+               {\r
+                       MAKESTRING(menuInfo[menuIndex].menuTextID);\r
+                       resource = MAKEINTRESOURCE(menuInfo[menuIndex].iconID);\r
+                       switch (id)\r
+                       {\r
+                       case ShellMenuLock:\r
+                               // menu lock is special because it can be set to the top\r
+                               // with a separate option in the registry\r
+                               space = ((layout & MENULOCK) || ((itemStates & ITEMIS_NEEDSLOCK) && g_ShellCache.IsGetLockTop())) ? 0 : 6;\r
+                               if ((layout & MENULOCK) || ((itemStates & ITEMIS_NEEDSLOCK) && g_ShellCache.IsGetLockTop()))\r
+                               {\r
+                                       _tcscpy_s(textbuf, 255, _T("SVN "));\r
+                                       _tcscat_s(textbuf, 255, stringtablebuffer);\r
+                                       _tcscpy_s(stringtablebuffer, 255, textbuf);\r
+                               }\r
+                               break;\r
+                               // the sub menu entries are special because they're *always* on the top level menu\r
+                       case ShellSubMenuMultiple:\r
+                       case ShellSubMenuLink:\r
+                       case ShellSubMenuFolder:\r
+                       case ShellSubMenuFile:\r
+                       case ShellSubMenu:\r
+                               space = 0;\r
+                               break;\r
+                       default:\r
+                               space = layout & menuInfo[menuIndex].menuID ? 0 : 6;\r
+                               if (layout & (menuInfo[menuIndex].menuID)) \r
+                               {\r
+                                       _tcscpy_s(textbuf, 255, _T("SVN "));\r
+                                       _tcscat_s(textbuf, 255, stringtablebuffer);\r
+                                       _tcscpy_s(stringtablebuffer, 255, textbuf);\r
+                               }\r
+                               break;\r
+                       }\r
+                       return resource;\r
+               }\r
+               menuIndex++;\r
+       }\r
+       return NULL;\r
+}\r
+\r
+bool CShellExt::IsIllegalFolder(std::wstring folder, int * cslidarray)\r
+{\r
+       int i=0;\r
+       TCHAR buf[MAX_PATH];    //MAX_PATH ok, since SHGetSpecialFolderPath doesn't return the required buffer length!\r
+       LPITEMIDLIST pidl = NULL;\r
+       while (cslidarray[i])\r
+       {\r
+               ++i;\r
+               pidl = NULL;\r
+               if (SHGetFolderLocation(NULL, cslidarray[i-1], NULL, 0, &pidl)!=S_OK)\r
+                       continue;\r
+               if (!SHGetPathFromIDList(pidl, buf))\r
+               {\r
+                       // not a file system path, definitely illegal for our use\r
+                       CoTaskMemFree(pidl);\r
+                       continue;\r
+               }\r
+               CoTaskMemFree(pidl);\r
+               if (_tcslen(buf)==0)\r
+                       continue;\r
+               if (_tcscmp(buf, folder.c_str())==0)\r
+                       return true;\r
+       }\r
+       return false;\r
+}\r
+\r
+void CShellExt::InsertIgnoreSubmenus(UINT &idCmd, UINT idCmdFirst, HMENU hMenu, HMENU subMenu, UINT &indexMenu, int &indexSubMenu, unsigned __int64 topmenu, bool bShowIcons)\r
+{\r
+       HMENU ignoresubmenu = NULL;\r
+       int indexignoresub = 0;\r
+       bool bShowIgnoreMenu = false;\r
+       TCHAR maskbuf[MAX_PATH];                // MAX_PATH is ok, since this only holds a filename\r
+       TCHAR ignorepath[MAX_PATH];             // MAX_PATH is ok, since this only holds a filename\r
+       if (files_.size() == 0)\r
+               return;\r
+       UINT icon = bShowIcons ? IDI_IGNORE : 0;\r
+\r
+       std::vector<stdstring>::iterator I = files_.begin();\r
+       if (_tcsrchr(I->c_str(), '\\'))\r
+               _tcscpy_s(ignorepath, MAX_PATH, _tcsrchr(I->c_str(), '\\')+1);\r
+       else\r
+               _tcscpy_s(ignorepath, MAX_PATH, I->c_str());\r
+       if ((itemStates & ITEMIS_IGNORED)&&(ignoredprops.size() > 0))\r
+       {\r
+               // check if the item name is ignored or the mask\r
+               size_t p = 0;\r
+               while ( (p=ignoredprops.find( ignorepath,p )) != -1 )\r
+               {\r
+                       if ( (p==0 || ignoredprops[p-1]==TCHAR('\n'))\r
+                               && (p+_tcslen(ignorepath)==ignoredprops.length() || ignoredprops[p+_tcslen(ignorepath)+1]==TCHAR('\n')) )\r
+                       {\r
+                               break;\r
+                       }\r
+                       p++;\r
+               }\r
+               if (p!=-1)\r
+               {\r
+                       ignoresubmenu = CreateMenu();\r
+                       InsertMenu(ignoresubmenu, indexignoresub++, MF_BYPOSITION | MF_STRING , idCmd, ignorepath);\r
+                       stdstring verb = stdstring(ignorepath);\r
+                       myVerbsMap[verb] = idCmd - idCmdFirst;\r
+                       myVerbsMap[verb] = idCmd;\r
+                       myVerbsIDMap[idCmd - idCmdFirst] = verb;\r
+                       myVerbsIDMap[idCmd] = verb;\r
+                       myIDMap[idCmd - idCmdFirst] = ShellMenuUnIgnore;\r
+                       myIDMap[idCmd++] = ShellMenuUnIgnore;\r
+                       bShowIgnoreMenu = true;\r
+               }\r
+               _tcscpy_s(maskbuf, MAX_PATH, _T("*"));\r
+               if (_tcsrchr(ignorepath, '.'))\r
+               {\r
+                       _tcscat_s(maskbuf, MAX_PATH, _tcsrchr(ignorepath, '.'));\r
+                       p = ignoredprops.find(maskbuf);\r
+                       if ((p!=-1) &&\r
+                               ((ignoredprops.compare(maskbuf)==0) || (ignoredprops.find('\n', p)==p+_tcslen(maskbuf)+1) || (ignoredprops.rfind('\n', p)==p-1)))\r
+                       {\r
+                               if (ignoresubmenu==NULL)\r
+                                       ignoresubmenu = CreateMenu();\r
+\r
+                               InsertMenu(ignoresubmenu, indexignoresub++, MF_BYPOSITION | MF_STRING , idCmd, maskbuf);\r
+                               stdstring verb = stdstring(maskbuf);\r
+                               myVerbsMap[verb] = idCmd - idCmdFirst;\r
+                               myVerbsMap[verb] = idCmd;\r
+                               myVerbsIDMap[idCmd - idCmdFirst] = verb;\r
+                               myVerbsIDMap[idCmd] = verb;\r
+                               myIDMap[idCmd - idCmdFirst] = ShellMenuUnIgnoreCaseSensitive;\r
+                               myIDMap[idCmd++] = ShellMenuUnIgnoreCaseSensitive;\r
+                               bShowIgnoreMenu = true;\r
+                       }\r
+               }\r
+       }\r
+       else if ((itemStates & ITEMIS_IGNORED) == 0)\r
+       {\r
+               bShowIgnoreMenu = true;\r
+               ignoresubmenu = CreateMenu();\r
+               if (itemStates & ITEMIS_ONLYONE)\r
+               {\r
+                       InsertMenu(ignoresubmenu, indexignoresub++, MF_BYPOSITION | MF_STRING , idCmd, ignorepath);\r
+                       myIDMap[idCmd - idCmdFirst] = ShellMenuIgnore;\r
+                       myIDMap[idCmd++] = ShellMenuIgnore;\r
+\r
+                       _tcscpy_s(maskbuf, MAX_PATH, _T("*"));\r
+                       if (_tcsrchr(ignorepath, '.'))\r
+                       {\r
+                               _tcscat_s(maskbuf, MAX_PATH, _tcsrchr(ignorepath, '.'));\r
+                               InsertMenu(ignoresubmenu, indexignoresub++, MF_BYPOSITION | MF_STRING , idCmd, maskbuf);\r
+                               stdstring verb = stdstring(maskbuf);\r
+                               myVerbsMap[verb] = idCmd - idCmdFirst;\r
+                               myVerbsMap[verb] = idCmd;\r
+                               myVerbsIDMap[idCmd - idCmdFirst] = verb;\r
+                               myVerbsIDMap[idCmd] = verb;\r
+                               myIDMap[idCmd - idCmdFirst] = ShellMenuIgnoreCaseSensitive;\r
+                               myIDMap[idCmd++] = ShellMenuIgnoreCaseSensitive;\r
+                       }\r
+               }\r
+               else\r
+               {\r
+                       MAKESTRING(IDS_MENUIGNOREMULTIPLE);\r
+                       _stprintf_s(ignorepath, MAX_PATH, stringtablebuffer, files_.size());\r
+                       InsertMenu(ignoresubmenu, indexignoresub++, MF_BYPOSITION | MF_STRING , idCmd, ignorepath);\r
+                       stdstring verb = stdstring(ignorepath);\r
+                       myVerbsMap[verb] = idCmd - idCmdFirst;\r
+                       myVerbsMap[verb] = idCmd;\r
+                       myVerbsIDMap[idCmd - idCmdFirst] = verb;\r
+                       myVerbsIDMap[idCmd] = verb;\r
+                       myIDMap[idCmd - idCmdFirst] = ShellMenuIgnore;\r
+                       myIDMap[idCmd++] = ShellMenuIgnore;\r
+\r
+                       MAKESTRING(IDS_MENUIGNOREMULTIPLEMASK);\r
+                       _stprintf_s(ignorepath, MAX_PATH, stringtablebuffer, files_.size());\r
+                       InsertMenu(ignoresubmenu, indexignoresub++, MF_BYPOSITION | MF_STRING , idCmd, ignorepath);\r
+                       verb = stdstring(ignorepath);\r
+                       myVerbsMap[verb] = idCmd - idCmdFirst;\r
+                       myVerbsMap[verb] = idCmd;\r
+                       myVerbsIDMap[idCmd - idCmdFirst] = verb;\r
+                       myVerbsIDMap[idCmd] = verb;\r
+                       myIDMap[idCmd - idCmdFirst] = ShellMenuIgnoreCaseSensitive;\r
+                       myIDMap[idCmd++] = ShellMenuIgnoreCaseSensitive;\r
+               }\r
+       }\r
+\r
+       if (bShowIgnoreMenu)\r
+       {\r
+               MENUITEMINFO menuiteminfo;\r
+               SecureZeroMemory(&menuiteminfo, sizeof(menuiteminfo));\r
+               menuiteminfo.cbSize = sizeof(menuiteminfo);\r
+               menuiteminfo.fMask = MIIM_FTYPE | MIIM_ID | MIIM_SUBMENU | MIIM_DATA | MIIM_BITMAP | MIIM_STRING;\r
+               menuiteminfo.fType = MFT_STRING;\r
+               HBITMAP bmp = (fullver >= 0x600) ? IconToBitmapPARGB32(icon) : IconToBitmap(icon);\r
+               if (icon)\r
+                       menuiteminfo.hbmpItem = (fullver >= 0x600) ? bmp : HBMMENU_CALLBACK;\r
+               menuiteminfo.hbmpChecked = bmp;\r
+               menuiteminfo.hbmpUnchecked = bmp;\r
+               menuiteminfo.hSubMenu = ignoresubmenu;\r
+               menuiteminfo.wID = idCmd;\r
+               SecureZeroMemory(stringtablebuffer, sizeof(stringtablebuffer));\r
+               if (itemStates & ITEMIS_IGNORED)\r
+                       GetMenuTextFromResource(ShellMenuUnIgnoreSub);\r
+               else\r
+                       GetMenuTextFromResource(ShellMenuIgnoreSub);\r
+               menuiteminfo.dwTypeData = stringtablebuffer;\r
+               menuiteminfo.cch = (UINT)min(_tcslen(menuiteminfo.dwTypeData), UINT_MAX);\r
+\r
+               InsertMenuItem((topmenu & MENUIGNORE) ? hMenu : subMenu, (topmenu & MENUIGNORE) ? indexMenu++ : indexSubMenu++, TRUE, &menuiteminfo);\r
+               if (itemStates & ITEMIS_IGNORED)\r
+               {\r
+                       myIDMap[idCmd - idCmdFirst] = ShellMenuUnIgnoreSub;\r
+                       myIDMap[idCmd++] = ShellMenuUnIgnoreSub;\r
+               }\r
+               else\r
+               {\r
+                       myIDMap[idCmd - idCmdFirst] = ShellMenuIgnoreSub;\r
+                       myIDMap[idCmd++] = ShellMenuIgnoreSub;\r
+               }\r
+       }\r
+}\r
+\r
+HBITMAP CShellExt::IconToBitmapPARGB32(UINT uIcon)\r
+{\r
+       std::map<UINT, HBITMAP>::iterator bitmap_it = bitmaps.lower_bound(uIcon);\r
+       if (bitmap_it != bitmaps.end() && bitmap_it->first == uIcon)\r
+               return bitmap_it->second;\r
+\r
+       HICON hIcon = (HICON)LoadImage(g_hResInst, MAKEINTRESOURCE(uIcon), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);\r
+       if (!hIcon)\r
+               return NULL;\r
+\r
+       if (pfnBeginBufferedPaint == NULL || pfnEndBufferedPaint == NULL || pfnGetBufferedPaintBits == NULL)\r
+               return NULL;\r
+\r
+       SIZE sizIcon;\r
+       sizIcon.cx = GetSystemMetrics(SM_CXSMICON);\r
+       sizIcon.cy = GetSystemMetrics(SM_CYSMICON);\r
+\r
+       RECT rcIcon;\r
+       SetRect(&rcIcon, 0, 0, sizIcon.cx, sizIcon.cy);\r
+       HBITMAP hBmp = NULL;\r
+\r
+       HDC hdcDest = CreateCompatibleDC(NULL);\r
+       if (hdcDest)\r
+       {\r
+               if (SUCCEEDED(Create32BitHBITMAP(hdcDest, &sizIcon, NULL, &hBmp)))\r
+               {\r
+                       HBITMAP hbmpOld = (HBITMAP)SelectObject(hdcDest, hBmp);\r
+                       if (hbmpOld)\r
+                       {\r
+                               BLENDFUNCTION bfAlpha = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };\r
+                               BP_PAINTPARAMS paintParams = {0};\r
+                               paintParams.cbSize = sizeof(paintParams);\r
+                               paintParams.dwFlags = BPPF_ERASE;\r
+                               paintParams.pBlendFunction = &bfAlpha;\r
+\r
+                               HDC hdcBuffer;\r
+                               HPAINTBUFFER hPaintBuffer = pfnBeginBufferedPaint(hdcDest, &rcIcon, BPBF_DIB, &paintParams, &hdcBuffer);\r
+                               if (hPaintBuffer)\r
+                               {\r
+                                       if (DrawIconEx(hdcBuffer, 0, 0, hIcon, sizIcon.cx, sizIcon.cy, 0, NULL, DI_NORMAL))\r
+                                       {\r
+                                               // If icon did not have an alpha channel we need to convert buffer to PARGB\r
+                                               ConvertBufferToPARGB32(hPaintBuffer, hdcDest, hIcon, sizIcon);\r
+                                       }\r
+\r
+                                       // This will write the buffer contents to the destination bitmap\r
+                                       pfnEndBufferedPaint(hPaintBuffer, TRUE);\r
+                               }\r
+\r
+                               SelectObject(hdcDest, hbmpOld);\r
+                       }\r
+               }\r
+\r
+               DeleteDC(hdcDest);\r
+       }\r
+\r
+       DestroyIcon(hIcon);\r
+\r
+       if(hBmp)\r
+               bitmaps.insert(bitmap_it, std::make_pair(uIcon, hBmp));\r
+       return hBmp;\r
+}\r
+\r
+HRESULT CShellExt::Create32BitHBITMAP(HDC hdc, const SIZE *psize, __deref_opt_out void **ppvBits, __out HBITMAP* phBmp)\r
+{\r
+       *phBmp = NULL;\r
+\r
+       BITMAPINFO bmi;\r
+       ZeroMemory(&bmi, sizeof(bmi));\r
+       bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);\r
+       bmi.bmiHeader.biPlanes = 1;\r
+       bmi.bmiHeader.biCompression = BI_RGB;\r
+\r
+       bmi.bmiHeader.biWidth = psize->cx;\r
+       bmi.bmiHeader.biHeight = psize->cy;\r
+       bmi.bmiHeader.biBitCount = 32;\r
+\r
+       HDC hdcUsed = hdc ? hdc : GetDC(NULL);\r
+       if (hdcUsed)\r
+       {\r
+               *phBmp = CreateDIBSection(hdcUsed, &bmi, DIB_RGB_COLORS, ppvBits, NULL, 0);\r
+               if (hdc != hdcUsed)\r
+               {\r
+                       ReleaseDC(NULL, hdcUsed);\r
+               }\r
+       }\r
+       return (NULL == *phBmp) ? E_OUTOFMEMORY : S_OK;\r
+}\r
+\r
+HRESULT CShellExt::ConvertBufferToPARGB32(HPAINTBUFFER hPaintBuffer, HDC hdc, HICON hicon, SIZE& sizIcon)\r
+{\r
+       RGBQUAD *prgbQuad;\r
+       int cxRow;\r
+       HRESULT hr = pfnGetBufferedPaintBits(hPaintBuffer, &prgbQuad, &cxRow);\r
+       if (SUCCEEDED(hr))\r
+       {\r
+               ARGB *pargb = reinterpret_cast<ARGB *>(prgbQuad);\r
+               if (!HasAlpha(pargb, sizIcon, cxRow))\r
+               {\r
+                       ICONINFO info;\r
+                       if (GetIconInfo(hicon, &info))\r
+                       {\r
+                               if (info.hbmMask)\r
+                               {\r
+                                       hr = ConvertToPARGB32(hdc, pargb, info.hbmMask, sizIcon, cxRow);\r
+                               }\r
+\r
+                               DeleteObject(info.hbmColor);\r
+                               DeleteObject(info.hbmMask);\r
+                       }\r
+               }\r
+       }\r
+\r
+       return hr;\r
+}\r
+\r
+bool CShellExt::HasAlpha(__in ARGB *pargb, SIZE& sizImage, int cxRow)\r
+{\r
+       ULONG cxDelta = cxRow - sizImage.cx;\r
+       for (ULONG y = sizImage.cy; y; --y)\r
+       {\r
+               for (ULONG x = sizImage.cx; x; --x)\r
+               {\r
+                       if (*pargb++ & 0xFF000000)\r
+                       {\r
+                               return true;\r
+                       }\r
+               }\r
+\r
+               pargb += cxDelta;\r
+       }\r
+\r
+       return false;\r
+}\r
+\r
+HRESULT CShellExt::ConvertToPARGB32(HDC hdc, __inout ARGB *pargb, HBITMAP hbmp, SIZE& sizImage, int cxRow)\r
+{\r
+       BITMAPINFO bmi;\r
+       ZeroMemory(&bmi, sizeof(bmi));\r
+       bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);\r
+       bmi.bmiHeader.biPlanes = 1;\r
+       bmi.bmiHeader.biCompression = BI_RGB;\r
+\r
+       bmi.bmiHeader.biWidth = sizImage.cx;\r
+       bmi.bmiHeader.biHeight = sizImage.cy;\r
+       bmi.bmiHeader.biBitCount = 32;\r
+\r
+       HRESULT hr = E_OUTOFMEMORY;\r
+       HANDLE hHeap = GetProcessHeap();\r
+       void *pvBits = HeapAlloc(hHeap, 0, bmi.bmiHeader.biWidth * 4 * bmi.bmiHeader.biHeight);\r
+       if (pvBits)\r
+       {\r
+               hr = E_UNEXPECTED;\r
+               if (GetDIBits(hdc, hbmp, 0, bmi.bmiHeader.biHeight, pvBits, &bmi, DIB_RGB_COLORS) == bmi.bmiHeader.biHeight)\r
+               {\r
+                       ULONG cxDelta = cxRow - bmi.bmiHeader.biWidth;\r
+                       ARGB *pargbMask = static_cast<ARGB *>(pvBits);\r
+\r
+                       for (ULONG y = bmi.bmiHeader.biHeight; y; --y)\r
+                       {\r
+                               for (ULONG x = bmi.bmiHeader.biWidth; x; --x)\r
+                               {\r
+                                       if (*pargbMask++)\r
+                                       {\r
+                                               // transparent pixel\r
+                                               *pargb++ = 0;\r
+                                       }\r
+                                       else\r
+                                       {\r
+                                               // opaque pixel\r
+                                               *pargb++ |= 0xFF000000;\r
+                                       }\r
+                               }\r
+\r
+                               pargb += cxDelta;\r
+                       }\r
+\r
+                       hr = S_OK;\r
+               }\r
+\r
+               HeapFree(hHeap, 0, pvBits);\r
+       }\r
+\r
+       return hr;\r
+}\r
+\r
diff --git a/TortoiseShell/Documentation.h b/TortoiseShell/Documentation.h
new file mode 100644 (file)
index 0000000..012385d
--- /dev/null
@@ -0,0 +1,416 @@
+/**\r
+ * \mainpage TortoiseSVN\r
+ * \section introduction Introduction\r
+ * TortoiseSVN is a windows shell integration for the Subversion\r
+ * source versioning system. Subversion is the successor\r
+ * of the famous CVS. See http://subversion.tigris.org for\r
+ * more information on Subversion.\r
+ *\r
+ * TortoiseSVN was inspired by the similar program TortoiseCVS.\r
+ * \r
+ * With TortoiseSVN you can use Subversion directly from the\r
+ * Windows Explorer. The Subversion status of your project\r
+ * files are clearly visible and all commands Subversion\r
+ * provides are accessible directly from the Explorer Context-Menu\r
+ * (right-click).\r
+ *\r
+ * \section Requirements Requirements\r
+ * TortoiseSVN runs under Win2k and later.\r
+ * You also need at least Internet Explorer Version 5 or higher.\r
+ *\r
+ * \section Installation Installation\r
+ * TortoiseSVN comes with an installer. Just run the installer and follow\r
+ * the simple steps of the wizard. You may need to restart your computer\r
+ * to get TortoiseSVN to work, depending on your registry settings. If you're\r
+ * not sure, fire up the explorer, right click on a folder and check if\r
+ * the TortoiseSVN menus are there.\r
+ *\r
+ * \section Sourcecode Source code\r
+ * The full source code of TortoiseSVN is available at http://tortoisesvn.net.\r
+ * \r
+ * For information on how to build TortoiseSVN from the source code see \ref build\r
+ *\r
+ * \section Internals Internals\r
+ * TortoiseSVN is a shell extension. All commands and dialogs are started by\r
+ * using the shell context menu on files and folders.\r
+ *\r
+ * The shell extension part is separate from the main application to reduce\r
+ * the memory use in the shell. The shell extension only does what's absolutely\r
+ * necessary and then creates a new process of the main application to do the\r
+ * real work. The main application is TortoiseProc. The shell passes all the\r
+ * information to TortoiseProc via command line switches and temporary files.\r
+ *\r
+ * To avoid loading the whole TortoiseShell dll if it isn't really wanted (e.g.,\r
+ * in applications other than the explorer itself), there's a stub dll which\r
+ * is registered in the shell as the extension. That stub dll then loads the\r
+ * TortoiseShell dll if required.\r
+ */\r
+\r
+/**\r
+ * \page licensesvn Subversion license\r
+ * <H1>Subversion License</H1>\r
+ * ================================================================\r
+ * Copyright (c) 2004 CollabNet.  All rights reserved.\r
+ * \r
+ * Redistribution and use in source and binary forms, with or without\r
+ * modification, are permitted provided that the following conditions are\r
+ * met:\r
+ * \r
+ * 1. Redistributions of source code must retain the above copyright\r
+ * notice, this list of conditions and the following disclaimer.\r
+ * \r
+ * 2. Redistributions in binary form must reproduce the above copyright\r
+ * notice, this list of conditions and the following disclaimer in the\r
+ * documentation and/or other materials provided with the distribution.\r
+ * \r
+ * 3. The end-user documentation included with the redistribution, if\r
+ * any, must include the following acknowledgment: "This product includes\r
+ * software developed by CollabNet (http://www.Collab.Net/)."\r
+ * Alternately, this acknowledgment may appear in the software itself, if\r
+ * and wherever such third-party acknowledgments normally appear.\r
+ * \r
+ * 4. The hosted project names must not be used to endorse or promote\r
+ * products derived from this software without prior written\r
+ * permission. For written permission, please contact info@collab.net.\r
+ * \r
+ * 5. Products derived from this software may not use the "Tigris" name\r
+ * nor may "Tigris" appear in their names without prior written\r
+ * permission of CollabNet.\r
+ * \r
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED\r
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\r
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\r
+ * IN NO EVENT SHALL COLLABNET OR ITS CONTRIBUTORS BE LIABLE FOR ANY\r
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\r
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE\r
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\r
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER\r
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR\r
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\r
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+ *\r
+ * ====================================================================\r
+ * \r
+ * This software consists of voluntary contributions made by many\r
+ * individuals on behalf of CollabNet.\r
+ */\r
+\r
+\r
+/**\r
+ * \page license TortoiseSVN license\r
+ * <H1>GNU General Public License</H1>\r
+ * also available at http://www.gnu.org/licenses/gpl.html\r
+ * \n\r
+ * Version 2, June 1991\r
+ * \n\r
+ * Copyright (C) 1989, 1991 Free Software Foundation, Inc.  \r
+ * \n\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ * \n\r
+ * Everyone is permitted to copy and distribute verbatim copies\r
+ * of this license document, but changing it is not allowed.\r
+ * \n\r
+ * <H2>Preamble</H2>\r
+ * The licenses for most software are designed to take away your \r
+ * freedom to share and change it. By contrast, the GNU General \r
+ * Public License is intended to guarantee your freedom to share and \r
+ * change free software--to make sure the software is free for all its \r
+ * users. This General Public License applies to most of the Free \r
+ * Software Foundation's software and to any other program whose authors \r
+ * commit to using it. (Some other Free Software Foundation software is \r
+ * covered by the GNU Library General Public License instead.) You can \r
+ * apply it to your programs, too. \r
+ * \n\r
+ * When we speak of free software, we are referring to freedom, not price. \r
+ * Our General Public Licenses are designed to make sure that you have the \r
+ * freedom to distribute copies of free software (and charge for this service \r
+ * if you wish), that you receive source code or can get it if you want it, \r
+ * that you can change the software or use pieces of it in new free programs; \r
+ * and that you know you can do these things. \r
+ * \n\r
+ * To protect your rights, we need to make restrictions that forbid anyone to \r
+ * deny you these rights or to ask you to surrender the rights. These restrictions \r
+ * translate to certain responsibilities for you if you distribute copies \r
+ * of the software, or if you modify it. \r
+ * \n\r
+ * For example, if you distribute copies of such a program, whether gratis or for \r
+ * a fee, you must give the recipients all the rights that you have. You must make \r
+ * sure that they, too, receive or can get the source code. And you must show them \r
+ * these terms so they know their rights. \r
+ * \n\r
+ * We protect your rights with two steps: (1) copyright the software, and (2) offer \r
+ * you this license which gives you legal permission to copy, distribute and/or \r
+ * modify the software. \r
+ * \n\r
+ * Also, for each author's protection and ours, we want to make certain that \r
+ * everyone understands that there is no warranty for this free software. If \r
+ * the software is modified by someone else and passed on, we want its recipients \r
+ * to know that what they have is not the original, so that any problems introduced \r
+ * by others will not reflect on the original authors' reputations. \r
+ * \n\r
+ * Finally, any free program is threatened constantly by software patents. We wish \r
+ * to avoid the danger that redistributors of a free program will individually \r
+ * obtain patent licenses, in effect making the program proprietary. To prevent \r
+ * this, we have made it clear that any patent must be licensed for everyone's \r
+ * free use or not licensed at all. \r
+ * \n\r
+ * The precise terms and conditions for copying, distribution and modification follow. \r
+ * \r
+ * <H1>TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION</H1>\r
+ * -# This License applies to any program or other work which contains a notice \r
+ * placed by the copyright holder saying it may be distributed under the terms of \r
+ * this General Public License. The "Program", below, refers to any such program \r
+ * or work, and a "work based on the Program" means either the Program or any \r
+ * derivative work under copyright law: that is to say, a work containing the \r
+ * Program or a portion of it, either verbatim or with modifications and/or \r
+ * translated into another language. (Hereinafter, translation is included without\r
+ * limitation in the term "modification".) Each licensee is addressed as "you". \r
+ * \n\r
+ * Activities other than copying, distribution and modification are not covered \r
+ * by this License; they are outside its scope. The act of running the Program \r
+ * is not restricted, and the output from the Program is covered only if its \r
+ * contents constitute a work based on the Program (independent of having been \r
+ * made by running the Program). Whether that is true depends on what the Program does. \r
+ * -# You may copy and distribute verbatim copies of the Program's source code \r
+ * as you receive it, in any medium, provided that you conspicuously and \r
+ * appropriately publish on each copy an appropriate copyright notice and \r
+ * disclaimer of warranty; keep intact all the notices that refer to this \r
+ * License and to the absence of any warranty; and give any other recipients \r
+ * of the Program a copy of this License along with the Program. \r
+ * \n\r
+ * You may charge a fee for the physical act of transferring a copy, and you \r
+ * may at your option offer warranty protection in exchange for a fee. \r
+ * -# You may modify your copy or copies of the Program or any portion of it, \r
+ * thus forming a work based on the Program, and copy and distribute such \r
+ * modifications or work under the terms of Section 1 above, provided that you \r
+ * also meet all of these conditions: \r
+ *   - You must cause the modified files to carry prominent notices stating \r
+ *       that you changed the files and the date of any change. \r
+ *   - You must cause any work that you distribute or publish, that in whole \r
+ *   or in part contains or is derived from the Program or any part thereof, \r
+ *   to be licensed as a whole at no charge to all third parties under the terms \r
+ *   of this License. \r
+ *   - If the modified program normally reads commands interactively when run,\r
+ *   you must cause it, when started running for such interactive use in the most \r
+ *   ordinary way, to print or display an announcement including an appropriate \r
+ *   copyright notice and a notice that there is no warranty (or else, saying that \r
+ *   you provide a warranty) and that users may redistribute the program under these \r
+ *   conditions, and telling the user how to view a copy of this License. (Exception: \r
+ *   if the Program itself is interactive but does not normally print such an \r
+ *   announcement, your work based on the Program is not required to print an \r
+ *   announcement.) \r
+ *   .\r
+ *   These requirements apply to the modified work as a whole. If identifiable \r
+ *   sections of that work are not derived from the Program, and can be reasonably \r
+ *   considered independent and separate works in themselves, then this License, \r
+ *   and its terms, do not apply to those sections when you distribute them as \r
+ *   separate works. But when you distribute the same sections as part of a whole \r
+ *   which is a work based on the Program, the distribution of the whole must be \r
+ *   on the terms of this License, whose permissions for other licensees extend \r
+ *   to the entire whole, and thus to each and every part regardless of who wrote it. \r
+ *   \n\r
+ *   Thus, it is not the intent of this section to claim rights or contest your \r
+ *   rights to work written entirely by you; rather, the intent is to exercise \r
+ *   the right to control the distribution of derivative or collective works \r
+ *   based on the Program. \r
+ *   \n\r
+ *   In addition, mere aggregation of another work not based on the Program with \r
+ *   the Program (or with a work based on the Program) on a volume of a storage \r
+ *   or distribution medium does not bring the other work under the scope of \r
+ *   this License. \r
+ * -# You may copy and distribute the Program (or a work based on it, under Section 2)\r
+ * in object code or executable form under the terms of Sections 1 and 2 above \r
+ * provided that you also do one of the following: \r
+ *   - Accompany it with the complete corresponding machine-readable source code, \r
+ *   which must be distributed under the terms of Sections 1 and 2 above on a \r
+ *   medium customarily used for software interchange; or, \r
+ *   - Accompany it with a written offer, valid for at least three years, \r
+ *   to give any third party, for a charge no more than your cost of physically \r
+ *   performing source distribution, a complete machine-readable copy of the \r
+ *   corresponding source code, to be distributed under the terms of Sections 1 \r
+ *   and 2 above on a medium customarily used for software interchange; or, \r
+ *   - Accompany it with the information you received as to the offer to distribute \r
+ *   corresponding source code. (This alternative is allowed only for noncommercial \r
+ *   distribution and only if you received the program in object code or executable \r
+ *   form with such an offer, in accord with Subsection b above.) \r
+ *   .\r
+ *   The source code for a work means the preferred form of the work for making \r
+ *   modifications to it. For an executable work, complete source code means all \r
+ *   the source code for all modules it contains, plus any associated interface \r
+ *   definition files, plus the scripts used to control compilation and installation \r
+ *   of the executable. However, as a special exception, the source code distributed \r
+ *   need not include anything that is normally distributed (in either source or \r
+ *   binary form) with the major components (compiler, kernel, and so on) of the \r
+ *   operating system on which the executable runs, unless that component itself \r
+ *   accompanies the executable. \r
+ *   \n\r
+ *   If distribution of executable or object code is made by offering access to \r
+ *   copy from a designated place, then offering equivalent access to copy the \r
+ *   source code from the same place counts as distribution of the source code, \r
+ *   even though third parties are not compelled to copy the source along with \r
+ *   the object code. \r
+ * -# You may not copy, modify, sublicense, or distribute the Program except \r
+ * as expressly provided under this License. Any attempt otherwise to copy,\r
+ * modify, sublicense or distribute the Program is void, and will automatically \r
+ * terminate your rights under this License. However, parties who have received \r
+ * copies, or rights, from you under this License will not have their licenses \r
+ * terminated so long as such parties remain in full compliance. \r
+ * -# You are not required to accept this License, since you have not signed it. \r
+ * However, nothing else grants you permission to modify or distribute the Program \r
+ * or its derivative works. These actions are prohibited by law if you do not \r
+ * accept this License. Therefore, by modifying or distributing the Program \r
+ * (or any work based on the Program), you indicate your acceptance of this \r
+ * License to do so, and all its terms and conditions for copying, distributing \r
+ * or modifying the Program or works based on it. \r
+ * -# Each time you redistribute the Program (or any work based on the Program), \r
+ * the recipient automatically receives a license from the original licensor to copy, \r
+ * distribute or modify the Program subject to these terms and conditions. You may \r
+ * not impose any further restrictions on the recipients' exercise of the rights \r
+ * granted herein. You are not responsible for enforcing compliance by third \r
+ * parties to this License. \r
+ * -# If, as a consequence of a court judgment or allegation of patent infringement \r
+ * or for any other reason (not limited to patent issues), conditions are imposed \r
+ * on you (whether by court order, agreement or otherwise) that contradict the \r
+ * conditions of this License, they do not excuse you from the conditions of this \r
+ * License. If you cannot distribute so as to satisfy simultaneously your obligations \r
+ * under this License and any other pertinent obligations, then as a consequence you \r
+ * may not distribute the Program at all. For example, if a patent license would not \r
+ * permit royalty-free redistribution of the Program by all those who receive copies \r
+ * directly or indirectly through you, then the only way you could satisfy both it and \r
+ * this License would be to refrain entirely from distribution of the Program. \r
+ * \n\r
+ * If any portion of this section is held invalid or unenforceable under any particular \r
+ * circumstance, the balance of the section is intended to apply and the section as a \r
+ * whole is intended to apply in other circumstances. \r
+ * \n\r
+ * It is not the purpose of this section to induce you to infringe any patents or other \r
+ * property right claims or to contest validity of any such claims; this section has \r
+ * the sole purpose of protecting the integrity of the free software distribution system, \r
+ * which is implemented by public license practices. Many people have made generous \r
+ * contributions to the wide range of software distributed through that system in reliance \r
+ * on consistent application of that system; it is up to the author/donor to decide if he \r
+ * or she is willing to distribute software through any other system and a licensee cannot \r
+ * impose that choice. \r
+ * \n\r
+ * This section is intended to make thoroughly clear what is believed to be a consequence \r
+ * of the rest of this License. \r
+ * -# If the distribution and/or use of the Program is restricted in certain countries \r
+ * either by patents or by copyrighted interfaces, the original copyright holder who \r
+ * places the Program under this License may add an explicit geographical distribution \r
+ * limitation excluding those countries, so that distribution is permitted only in or \r
+ * among countries not thus excluded. In such case, this License incorporates the \r
+ * limitation as if written in the body of this License. \r
+ * -# The Free Software Foundation may publish revised and/or new versions of the \r
+ * General Public License from time to time. Such new versions will be similar in \r
+ * spirit to the present version, but may differ in detail to address new problems \r
+ * or concerns. \r
+ * \n\r
+ * Each version is given a distinguishing version number. If the Program specifies \r
+ * a version number of this License which applies to it and "any later version", \r
+ * you have the option of following the terms and conditions either of that version \r
+ * or of any later version published by the Free Software Foundation. If the Program \r
+ * does not specify a version number of this License, you may choose any version \r
+ * ever published by the Free Software Foundation. \r
+ * -# If you wish to incorporate parts of the Program into other free programs whose \r
+ * distribution conditions are different, write to the author to ask for permission. \r
+ * For software which is copyrighted by the Free Software Foundation, write to the \r
+ * Free Software Foundation; we sometimes make exceptions for this. Our decision \r
+ * will be guided by the two goals of preserving the free status of all derivatives \r
+ * of our free software and of promoting the sharing and reuse of software generally. \r
+ * <H2>NO WARRANTY</H2>\r
+ * -# BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE \r
+ * PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED \r
+ * IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" \r
+ * WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED \r
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. \r
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD \r
+ * THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR \r
+ * OR CORRECTION. \r
+ * -# IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY\r
+ * COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM \r
+ * AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, \r
+ * INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE \r
+ * PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE \r
+ * OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE \r
+ * WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF \r
+ * THE POSSIBILITY OF SUCH DAMAGES.\r
+ *\r
+ */\r
+\r
+/**\r
+ * \defgroup TortoiseShell TortoiseShell\r
+ * Windows-Shell integration of TortoiseSVN.\r
+ * This project links to a COM object implementing all required\r
+ * interfaces a shell extension needs.\r
+ * The shell extension draws the overlay icons, provides the right-click menu,\r
+ * the property sheet and the SVN column (in Win2k and later).\r
+ */\r
+\r
+/**\r
+ * \defgroup TortoiseProc TortoiseProc\r
+ * Main program of TortoiseSVN. TortoiseShell calls this program\r
+ * to execute the required commands, the commands are specified in\r
+ * the command line.\r
+ */\r
+\r
+/**\r
+ * \defgroup TortoiseMerge TortoiseMerge\r
+ * A separate Diff/Merge program, shipped with TortoiseSVN.\r
+ */\r
+\r
+/**\r
+ * \defgroup TortoiseBlame TortoiseBlame\r
+ * A viewer for Subversion blame information.\r
+ */\r
+\r
+/**\r
+ * \defgroup TortoiseIDiff TortoiseIDiff\r
+ * An image diff viewer.\r
+ */\r
+\r
+/**\r
+ * \defgroup TortoiseUDiff TortoiseUDiff\r
+ * An unified diff viewer.\r
+ */\r
+\r
+/**\r
+ * \defgroup ResText ResText\r
+ * An utility to extract resource strings from executable files and apply\r
+ * translated strings back to them. Used for translating.\r
+ */\r
+\r
+/**\r
+ * \defgroup SubWCRev SubWCRev\r
+ * A tool to get information about a Subversion working copy.\r
+ */\r
+\r
+/**\r
+ * \defgroup Utils Utils\r
+ * Utility classes and functions not specific to TortoiseSVN, i.e. they\r
+ * could be used in other programs too.\r
+ */\r
+\r
+/**\r
+ * \defgroup SVN SVN\r
+ * Wrappers for the Subversion API.\r
+ */\r
+\r
+/**\r
+ * \defgroup TSVNCache TSVNCache\r
+ * Cache for Subversion status of files and folders.\r
+ */\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+ */\r
diff --git a/TortoiseShell/Globals.h b/TortoiseShell/Globals.h
new file mode 100644 (file)
index 0000000..6612edb
--- /dev/null
@@ -0,0 +1,102 @@
+// TortoiseSVN - a Windows shell extension for easy version control\r
+\r
+// Copyright (C) 2003-2008 - TortoiseSVN\r
+\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software Foundation,\r
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+//\r
+#pragma once\r
+\r
+#define MENUCHECKOUT           0x0000000000000001\r
+#define MENUUPDATE                     0x0000000000000002\r
+#define MENUCOMMIT                     0x0000000000000004\r
+#define MENUADD                                0x0000000000000008\r
+#define MENUREVERT                     0x0000000000000010\r
+#define MENUCLEANUP                    0x0000000000000020\r
+#define MENURESOLVE                    0x0000000000000040\r
+#define MENUSWITCH                     0x0000000000000080\r
+#define MENUIMPORT                     0x0000000000000100\r
+#define MENUEXPORT                     0x0000000000000200\r
+#define MENUCREATEREPOS                0x0000000000000400\r
+#define MENUCOPY                       0x0000000000000800\r
+#define MENUMERGE                      0x0000000000001000\r
+#define MENUREMOVE                     0x0000000000002000\r
+#define MENURENAME                     0x0000000000004000\r
+#define MENUUPDATEEXT          0x0000000000008000\r
+#define MENUDIFF                       0x0000000000010000\r
+#define MENULOG                                0x0000000000020000\r
+#define MENUCONFLICTEDITOR     0x0000000000040000\r
+#define MENURELOCATE           0x0000000000080000\r
+#define MENUSHOWCHANGED                0x0000000000100000\r
+#define MENUIGNORE                     0x0000000000200000\r
+#define MENUREPOBROWSE         0x0000000000400000\r
+#define MENUBLAME                      0x0000000000800000\r
+#define MENUCREATEPATCH                0x0000000001000000\r
+#define MENUAPPLYPATCH         0x0000000002000000\r
+#define MENUREVISIONGRAPH      0x0000000004000000\r
+#define MENULOCK                       0x0000000008000000\r
+#define MENUUNLOCK                     0x0000000010000000\r
+#define MENUPROPERTIES         0x0000000020000000\r
+#define MENUURLDIFF                    0x0000000040000000\r
+#define MENUDELUNVERSIONED     0x0000000080000000\r
+#define MENUMERGEALL           0x0000000100000000\r
+#define MENUPREVDIFF           0x0000000200000000\r
+#define MENUCLIPPASTE          0x0000000400000000\r
+\r
+#define MENUSETTINGS           0x2000000000000000\r
+#define MENUHELP                       0x4000000000000000\r
+#define MENUABOUT                      0x8000000000000000\r
+\r
+/**\r
+ * \ingroup TortoiseShell\r
+ * Since we need an own COM-object for every different\r
+ * Icon-Overlay implemented this enum defines which class\r
+ * is used.\r
+ */\r
+enum FileState\r
+{\r
+    FileStateUncontrolled,\r
+    FileStateVersioned,\r
+    FileStateModified,\r
+    FileStateConflict,\r
+       FileStateDeleted,\r
+       FileStateReadOnly,\r
+       FileStateLockedOverlay,\r
+       FileStateAddedOverlay,\r
+       FileStateIgnoredOverlay,\r
+       FileStateUnversionedOverlay,\r
+       FileStateDropHandler,\r
+       FileStateInvalid\r
+};\r
+\r
+\r
+#define ITEMIS_ONLYONE                         0x00000001\r
+#define ITEMIS_EXTENDED                                0x00000002\r
+#define ITEMIS_INSVN                           0x00000004\r
+#define ITEMIS_CONFLICTED                      0x00000008\r
+#define ITEMIS_FOLDER                          0x00000010\r
+#define ITEMIS_FOLDERINSVN                     0x00000020\r
+#define ITEMIS_NORMAL                          0x00000040\r
+#define ITEMIS_IGNORED                         0x00000080\r
+#define ITEMIS_INVERSIONEDFOLDER       0x00000100\r
+#define ITEMIS_ADDED                           0x00000200\r
+#define ITEMIS_DELETED                         0x00000400\r
+#define ITEMIS_LOCKED                          0x00000800\r
+#define ITEMIS_PATCHFILE                       0x00001000\r
+// #define ITEMIS_SHORTCUT                     0x00002000 //unused\r
+#define ITEMIS_NEEDSLOCK                       0x00004000\r
+#define ITEMIS_PATCHINCLIPBOARD                0x00008000\r
+#define ITEMIS_PATHINCLIPBOARD      0x00010000\r
+#define ITEMIS_TWO                                     0x00020000\r
+\r
diff --git a/TortoiseShell/Guids.h b/TortoiseShell/Guids.h
new file mode 100644 (file)
index 0000000..50d1356
--- /dev/null
@@ -0,0 +1,54 @@
+// TortoiseSVN - a Windows shell extension for easy version control\r
+\r
+// Copyright (C) 2003-2008 - Stefan Kueng\r
+\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software Foundation,\r
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+//\r
+\r
+\r
+// The class IDs of these Shell extension class.\r
+//\r
+// class ids: \r
+//\r
+// 30351346-7B7D-4FCC-81B4-1E394CA267EB\r
+// 30351347-7B7D-4FCC-81B4-1E394CA267EB\r
+// 30351348-7B7D-4FCC-81B4-1E394CA267EB\r
+// 30351349-7B7D-4FCC-81B4-1E394CA267EB\r
+// 3035134A-7B7D-4FCC-81B4-1E394CA267EB\r
+// 3035134B-7B7D-4FCC-81B4-1E394CA267EB\r
+// 3035134C-7B7D-4FCC-81B4-1E394CA267EB\r
+// 3035134D-7B7D-4FCC-81B4-1E394CA267EB\r
+// 3035134E-7B7D-4FCC-81B4-1E394CA267EB\r
+// 3035134F-7B7D-4FCC-81B4-1E394CA267EB\r
+//\r
+//\r
+// NOTE!!!  If you use this shell extension as a starting point, \r
+//          you MUST change the GUID below.  Simply run UUIDGEN.EXE\r
+//          to generate a new GUID.\r
+//\r
+\r
+DEFINE_GUID(CLSID_TortoiseSVN_UPTODATE,0x30351346, 0x7b7d, 0x4fcc, 0x81, 0xb4, 0x1e, 0x39, 0x4c, 0xa2, 0x67, 0xeb);\r
+DEFINE_GUID(CLSID_TortoiseSVN_MODIFIED,0x30351347, 0x7b7d, 0x4fcc, 0x81, 0xb4, 0x1e, 0x39, 0x4c, 0xa2, 0x67, 0xeb);\r
+DEFINE_GUID(CLSID_TortoiseSVN_CONFLICTING,0x30351348, 0x7b7d, 0x4fcc, 0x81, 0xb4, 0x1e, 0x39, 0x4c, 0xa2, 0x67, 0xeb);\r
+DEFINE_GUID(CLSID_TortoiseSVN_UNCONTROLLED,0x30351349, 0x7b7d, 0x4fcc, 0x81, 0xb4, 0x1e, 0x39, 0x4c, 0xa2, 0x67, 0xeb);\r
+DEFINE_GUID(CLSID_TortoiseSVN_DROPHANDLER,0x3035134a, 0x7b7d, 0x4fcc, 0x81, 0xb4, 0x1e, 0x39, 0x4c, 0xa2, 0x67, 0xeb);\r
+DEFINE_GUID(CLSID_TortoiseSVN_READONLY,0x3035134b, 0x7b7d, 0x4fcc, 0x81, 0xb4, 0x1e, 0x39, 0x4c, 0xa2, 0x67, 0xeb);\r
+DEFINE_GUID(CLSID_TortoiseSVN_DELETED,0x3035134c, 0x7b7d, 0x4fcc, 0x81, 0xb4, 0x1e, 0x39, 0x4c, 0xa2, 0x67, 0xeb);\r
+DEFINE_GUID(CLSID_TortoiseSVN_LOCKED,0x3035134d, 0x7b7d, 0x4fcc, 0x81, 0xb4, 0x1e, 0x39, 0x4c, 0xa2, 0x67, 0xeb);\r
+DEFINE_GUID(CLSID_TortoiseSVN_ADDED,0x3035134e, 0x7b7d, 0x4fcc, 0x81, 0xb4, 0x1e, 0x39, 0x4c, 0xa2, 0x67, 0xeb);\r
+DEFINE_GUID(CLSID_TortoiseSVN_IGNORED,0x3035134f, 0x7b7d, 0x4fcc, 0x81, 0xb4, 0x1e, 0x39, 0x4c, 0xa2, 0x67, 0xeb);\r
+DEFINE_GUID(CLSID_TortoiseSVN_UNVERSIONED,0x30351350, 0x7b7d, 0x4fcc, 0x81, 0xb4, 0x1e, 0x39, 0x4c, 0xa2, 0x67, 0xeb);\r
+\r
+\r
diff --git a/TortoiseShell/IconOverlay.cpp b/TortoiseShell/IconOverlay.cpp
new file mode 100644 (file)
index 0000000..5a2535f
--- /dev/null
@@ -0,0 +1,372 @@
+// TortoiseSVN - a Windows shell extension for easy version control\r
+\r
+// Copyright (C) 2003-2008 - TortoiseSVN\r
+\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software Foundation,\r
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+//\r
+#include "stdafx.h"\r
+#include "ShellExt.h"\r
+#include "Guids.h"\r
+#include "PreserveChdir.h"\r
+#include "UnicodeUtils.h"\r
+#include "SVNStatus.h"\r
+#include "..\TSVNCache\CacheInterface.h"\r
+\r
+// "The Shell calls IShellIconOverlayIdentifier::GetOverlayInfo to request the\r
+//  location of the handler's icon overlay. The icon overlay handler returns\r
+//  the name of the file containing the overlay image, and its index within\r
+//  that file. The Shell then adds the icon overlay to the system image list."\r
+\r
+STDMETHODIMP CShellExt::GetOverlayInfo(LPWSTR /*pwszIconFile*/, int /*cchMax*/, int * /*pIndex*/, DWORD * /*pdwFlags*/)\r
+{\r
+       PreserveChdir preserveChdir;\r
+\r
+       // Now here's where we can find out if due to lack of enough overlay\r
+       // slots some of our overlays won't be shown.\r
+       // To do that we have to mark every overlay handler that's successfully\r
+       // loaded, so we can later check if some are missing\r
+       switch (m_State)\r
+       {\r
+               case FileStateVersioned                         : g_normalovlloaded = true; break;\r
+               case FileStateModified                          : g_modifiedovlloaded = true; break;\r
+               case FileStateConflict                          : g_conflictedovlloaded = true; break;\r
+               case FileStateDeleted                           : g_deletedovlloaded = true; break;\r
+               case FileStateReadOnly                          : g_readonlyovlloaded = true; break;\r
+               case FileStateLockedOverlay                     : g_lockedovlloaded = true; break;\r
+               case FileStateAddedOverlay                      : g_addedovlloaded = true; break;\r
+               case FileStateIgnoredOverlay            : g_ignoredovlloaded = true; break;\r
+               case FileStateUnversionedOverlay        : g_unversionedovlloaded = true; break;\r
+       }\r
+\r
+       // we don't have to set the icon file and/or the index here:\r
+       // the icons are handled by the TortoiseOverlays dll.\r
+    return S_OK;\r
+};\r
+\r
+STDMETHODIMP CShellExt::GetPriority(int *pPriority)\r
+{\r
+       switch (m_State)\r
+       {\r
+               case FileStateConflict:\r
+                       *pPriority = 0;\r
+                       break;\r
+               case FileStateModified:\r
+                       *pPriority = 1;\r
+                       break;\r
+               case FileStateDeleted:\r
+                       *pPriority = 2;\r
+                       break;\r
+               case FileStateReadOnly:\r
+                       *pPriority = 3;\r
+                       break;\r
+               case FileStateLockedOverlay:\r
+                       *pPriority = 4;\r
+                       break;\r
+               case FileStateAddedOverlay:\r
+                       *pPriority = 5;\r
+                       break;\r
+               case FileStateVersioned:\r
+                       *pPriority = 6;\r
+                       break;\r
+               default:\r
+                       *pPriority = 100;\r
+                       return S_FALSE;\r
+       }\r
+    return S_OK;\r
+}\r
+\r
+// "Before painting an object's icon, the Shell passes the object's name to\r
+//  each icon overlay handler's IShellIconOverlayIdentifier::IsMemberOf\r
+//  method. If a handler wants to have its icon overlay displayed,\r
+//  it returns S_OK. The Shell then calls the handler's\r
+//  IShellIconOverlayIdentifier::GetOverlayInfo method to determine which icon\r
+//  to display."\r
+\r
+STDMETHODIMP CShellExt::IsMemberOf(LPCWSTR pwszPath, DWORD /*dwAttrib*/)\r
+{\r
+       PreserveChdir preserveChdir;\r
+       svn_wc_status_kind status = svn_wc_status_none;\r
+       bool readonlyoverlay = false;\r
+       bool lockedoverlay = false;\r
+       if (pwszPath == NULL)\r
+               return S_FALSE;\r
+       const TCHAR* pPath = pwszPath;\r
+\r
+       // the shell sometimes asks overlays for invalid paths, e.g. for network\r
+       // printers (in that case the path is "0", at least for me here).\r
+       if (_tcslen(pPath)<2)\r
+               return S_FALSE;\r
+       // since the shell calls each and every overlay handler with the same filepath\r
+       // we use a small 'fast' cache of just one path here.\r
+       // To make sure that cache expires, clear it as soon as one handler is used.\r
+\r
+       AutoLocker lock(g_csGlobalCOMGuard);\r
+       if (_tcscmp(pPath, g_filepath.c_str())==0)\r
+       {\r
+               status = g_filestatus;\r
+               readonlyoverlay = g_readonlyoverlay;\r
+               lockedoverlay = g_lockedoverlay;\r
+       }\r
+       else\r
+       {\r
+               if (!g_ShellCache.IsPathAllowed(pPath))\r
+               {\r
+                       int drivenumber = -1;\r
+                       if ((m_State == FileStateVersioned) && g_ShellCache.ShowExcludedAsNormal() && \r
+                               ((drivenumber=PathGetDriveNumber(pPath))!=0)&&(drivenumber!=1) &&\r
+                               PathIsDirectory(pPath) && g_ShellCache.HasSVNAdminDir(pPath, true))\r
+                       {\r
+                               return S_OK;\r
+                       }\r
+                       return S_FALSE;\r
+               }\r
+\r
+               switch (g_ShellCache.GetCacheType())\r
+               {\r
+               case ShellCache::exe:\r
+                       {\r
+                               TSVNCacheResponse itemStatus;\r
+                               SecureZeroMemory(&itemStatus, sizeof(itemStatus));\r
+                               if (m_remoteCacheLink.GetStatusFromRemoteCache(CTSVNPath(pPath), &itemStatus, true))\r
+                               {\r
+                                       status = SVNStatus::GetMoreImportant(itemStatus.m_status.text_status, itemStatus.m_status.prop_status);\r
+                                       if ((itemStatus.m_kind == svn_node_file)&&(status == svn_wc_status_normal)&&((itemStatus.m_needslock && itemStatus.m_owner[0]==0)||(itemStatus.m_readonly)))\r
+                                               readonlyoverlay = true;\r
+                                       if (itemStatus.m_owner[0]!=0)\r
+                                               lockedoverlay = true;\r
+                               }\r
+                       }\r
+                       break;\r
+               case ShellCache::dll:\r
+                       {\r
+                               // Look in our caches for this item \r
+                               const FileStatusCacheEntry * s = m_CachedStatus.GetCachedItem(CTSVNPath(pPath));\r
+                               if (s)\r
+                               {\r
+                                       status = s->status;\r
+                               }\r
+                               else\r
+                               {\r
+                                       // No cached status available \r
+\r
+                                       // since the dwAttrib param of the IsMemberOf() function does not\r
+                                       // have the SFGAO_FOLDER flag set at all (it's 0 for files and folders!)\r
+                                       // we have to check if the path is a folder ourselves :(\r
+                                       if (PathIsDirectory(pPath))\r
+                                       {\r
+                                               if (g_ShellCache.HasSVNAdminDir(pPath, TRUE))\r
+                                               {\r
+                                                       if ((!g_ShellCache.IsRecursive()) && (!g_ShellCache.IsFolderOverlay()))\r
+                                                       {\r
+                                                               status = svn_wc_status_normal;\r
+                                                       }\r
+                                                       else\r
+                                                       {\r
+                                                               const FileStatusCacheEntry * s = m_CachedStatus.GetFullStatus(CTSVNPath(pPath), TRUE);\r
+                                                               status = s->status;\r
+                                                               status = SVNStatus::GetMoreImportant(svn_wc_status_normal, status);\r
+                                                       }\r
+                                               }\r
+                                               else\r
+                                               {\r
+                                                       status = svn_wc_status_none;\r
+                                               }\r
+                                       }\r
+                                       else\r
+                                       {\r
+                                               const FileStatusCacheEntry * s = m_CachedStatus.GetFullStatus(CTSVNPath(pPath), FALSE);\r
+                                               status = s->status;\r
+                                       }\r
+                               }\r
+                               if ((s)&&(status == svn_wc_status_normal)&&(s->needslock)&&(s->owner[0]==0))\r
+                                       readonlyoverlay = true;\r
+                               if ((s)&&(s->owner[0]!=0))\r
+                                       lockedoverlay = true;\r
+\r
+                       }\r
+                       break;\r
+               default:\r
+               case ShellCache::none:\r
+                       {\r
+                               // no cache means we only show a 'versioned' overlay on folders\r
+                               // with an admin directory\r
+                               if (PathIsDirectory(pPath))\r
+                               {\r
+                                       if (g_ShellCache.HasSVNAdminDir(pPath, TRUE))\r
+                                       {\r
+                                               status = svn_wc_status_normal;\r
+                                       }\r
+                                       else\r
+                                       {\r
+                                               status = svn_wc_status_none;\r
+                                       }\r
+                               }\r
+                               else\r
+                               {\r
+                                       status = svn_wc_status_none;\r
+                               }\r
+                       }\r
+                       break;\r
+               }\r
+               ATLTRACE(_T("Status %d for file %s\n"), status, pwszPath);\r
+       }\r
+       g_filepath.clear();\r
+       g_filepath = pPath;\r
+       g_filestatus = status;\r
+       g_readonlyoverlay = readonlyoverlay;\r
+       g_lockedoverlay = lockedoverlay;\r
+       \r
+       //the priority system of the shell doesn't seem to work as expected (or as I expected):\r
+       //as it seems that if one handler returns S_OK then that handler is used, no matter\r
+       //if other handlers would return S_OK too (they're never called on my machine!)\r
+       //So we return S_OK for ONLY ONE handler!\r
+       switch (status)\r
+       {\r
+               // note: we can show other overlays if due to lack of enough free overlay\r
+               // slots some of our overlays aren't loaded. But we assume that\r
+               // at least the 'normal' and 'modified' overlay are available.\r
+               case svn_wc_status_none:\r
+                       return S_FALSE;\r
+               case svn_wc_status_unversioned:\r
+                       if (g_ShellCache.ShowUnversionedOverlay() && g_unversionedovlloaded && (m_State == FileStateUnversionedOverlay))\r
+                       {\r
+                               g_filepath.clear();\r
+                               return S_OK;\r
+                       }\r
+                       return S_FALSE;\r
+               case svn_wc_status_ignored:\r
+                       if (g_ShellCache.ShowIgnoredOverlay() && g_ignoredovlloaded && (m_State == FileStateIgnoredOverlay))\r
+                       {\r
+                               g_filepath.clear();\r
+                               return S_OK;\r
+                       }\r
+                       return S_FALSE;\r
+               case svn_wc_status_normal:\r
+               case svn_wc_status_external:\r
+               case svn_wc_status_incomplete:\r
+                       if ((readonlyoverlay)&&(g_readonlyovlloaded))\r
+                       {\r
+                               if (m_State == FileStateReadOnly)\r
+                               {\r
+                                       g_filepath.clear();\r
+                                       return S_OK;\r
+                               }\r
+                               else\r
+                                       return S_FALSE;\r
+                       }\r
+                       else if ((lockedoverlay)&&(g_lockedovlloaded))\r
+                       {\r
+                               if (m_State == FileStateLockedOverlay)\r
+                               {\r
+                                       g_filepath.clear();\r
+                                       return S_OK;\r
+                               }\r
+                               else\r
+                                       return S_FALSE;\r
+                       }\r
+                       else if (m_State == FileStateVersioned)\r
+                       {\r
+                               g_filepath.clear();\r
+                               return S_OK;\r
+                       }\r
+                       else\r
+                               return S_FALSE;\r
+               case svn_wc_status_missing:\r
+               case svn_wc_status_deleted:\r
+                       if (g_deletedovlloaded)\r
+                       {\r
+                               if (m_State == FileStateDeleted)\r
+                               {\r
+                                       g_filepath.clear();\r
+                                       return S_OK;\r
+                               }\r
+                               else\r
+                                       return S_FALSE;\r
+                       }\r
+                       else\r
+                       {\r
+                               // the 'deleted' overlay isn't available (due to lack of enough\r
+                               // overlay slots). So just show the 'modified' overlay instead.\r
+                               if (m_State == FileStateModified)\r
+                               {\r
+                                       g_filepath.clear();\r
+                                       return S_OK;\r
+                               }\r
+                               else\r
+                                       return S_FALSE;\r
+                       }\r
+               case svn_wc_status_replaced:\r
+               case svn_wc_status_modified:\r
+               case svn_wc_status_merged:\r
+                       if (m_State == FileStateModified)\r
+                       {\r
+                               g_filepath.clear();\r
+                               return S_OK;\r
+                       }\r
+                       else\r
+                               return S_FALSE;\r
+               case svn_wc_status_added:\r
+                       if (g_addedovlloaded)\r
+                       {\r
+                               if (m_State== FileStateAddedOverlay)\r
+                               {\r
+                                       g_filepath.clear();\r
+                                       return S_OK;\r
+                               }\r
+                               else\r
+                                       return S_FALSE;\r
+                       }\r
+                       else\r
+                       {\r
+                               // the 'added' overlay isn't available (due to lack of enough\r
+                               // overlay slots). So just show the 'modified' overlay instead.\r
+                               if (m_State == FileStateModified)\r
+                               {\r
+                                       g_filepath.clear();\r
+                                       return S_OK;\r
+                               }\r
+                               else\r
+                                       return S_FALSE;\r
+                       }\r
+               case svn_wc_status_conflicted:\r
+               case svn_wc_status_obstructed:\r
+                       if (g_conflictedovlloaded)\r
+                       {\r
+                               if (m_State == FileStateConflict)\r
+                               {\r
+                                       g_filepath.clear();\r
+                                       return S_OK;\r
+                               }\r
+                               else\r
+                                       return S_FALSE;\r
+                       }\r
+                       else\r
+                       {\r
+                               // the 'conflicted' overlay isn't available (due to lack of enough\r
+                               // overlay slots). So just show the 'modified' overlay instead.\r
+                               if (m_State == FileStateModified)\r
+                               {\r
+                                       g_filepath.clear();\r
+                                       return S_OK;\r
+                               }\r
+                               else\r
+                                       return S_FALSE;\r
+                       }\r
+               default:\r
+                       return S_FALSE;\r
+       } // switch (status)\r
+    //return S_FALSE;\r
+}\r
+\r
diff --git a/TortoiseShell/ItemIDList.cpp b/TortoiseShell/ItemIDList.cpp
new file mode 100644 (file)
index 0000000..73c353a
--- /dev/null
@@ -0,0 +1,126 @@
+// TortoiseSVN - a Windows shell extension for easy version control\r
+\r
+// Copyright (C) 2003-2006 - Stefan Kueng\r
+\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software Foundation,\r
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+//\r
+#include "StdAfx.h"\r
+#include "ShellExt.h"\r
+#include "ItemIDList.h"\r
+\r
+\r
+ItemIDList::ItemIDList(LPCITEMIDLIST item, LPCITEMIDLIST parent) :\r
+         item_ (item)\r
+       , parent_ (parent)\r
+       , count_ (-1)\r
+{\r
+}\r
+\r
+ItemIDList::~ItemIDList()\r
+{\r
+}\r
+\r
+int ItemIDList::size() const\r
+{\r
+       if (count_ == -1)\r
+       {\r
+               count_ = 0;\r
+               if (item_)\r
+               {\r
+                       LPCSHITEMID ptr = &item_->mkid;\r
+                       while (ptr != 0 && ptr->cb != 0)\r
+                       {\r
+                               ++count_;\r
+                               LPBYTE byte = (LPBYTE) ptr;\r
+                               byte += ptr->cb;\r
+                               ptr = (LPCSHITEMID) byte;\r
+                       }\r
+               }\r
+       }\r
+       return count_;\r
+}\r
+\r
+LPCSHITEMID ItemIDList::get(int index) const\r
+{\r
+       int count = 0;\r
+\r
+       if (item_ == NULL)\r
+               return NULL;\r
+       LPCSHITEMID ptr = &item_->mkid;\r
+       if (ptr == NULL)\r
+               return NULL;\r
+       while (ptr->cb != 0)\r
+       {\r
+               if (count == index)\r
+                       break;\r
+\r
+               ++count;\r
+               LPBYTE byte = (LPBYTE) ptr;\r
+               byte += ptr->cb;\r
+               ptr = (LPCSHITEMID) byte;\r
+       }\r
+       return ptr;\r
+\r
+}\r
+stdstring ItemIDList::toString()\r
+{\r
+       IShellFolder *shellFolder = NULL;\r
+       IShellFolder *parentFolder = NULL;\r
+       STRRET name;\r
+       TCHAR * szDisplayName = NULL;\r
+       stdstring ret;\r
+       HRESULT hr;\r
+\r
+       hr = ::SHGetDesktopFolder(&shellFolder);\r
+       if (!SUCCEEDED(hr))\r
+               return ret;\r
+       if (parent_)\r
+       {\r
+               hr = shellFolder->BindToObject(parent_, 0, IID_IShellFolder, (void**) &parentFolder);\r
+               if (!SUCCEEDED(hr))\r
+                       parentFolder = shellFolder;\r
+       } \r
+       else \r
+       {\r
+               parentFolder = shellFolder;\r
+       }\r
+\r
+       if ((parentFolder != 0)&&(item_ != 0))\r
+       {\r
+               hr = parentFolder->GetDisplayNameOf(item_, SHGDN_NORMAL | SHGDN_FORPARSING, &name);\r
+               if (!SUCCEEDED(hr))\r
+               {\r
+                       parentFolder->Release();\r
+                       return ret;\r
+               }\r
+               hr = StrRetToStr (&name, item_, &szDisplayName);\r
+               if (!SUCCEEDED(hr))\r
+                       return ret;\r
+       }\r
+       parentFolder->Release();\r
+       if (szDisplayName == NULL)\r
+       {\r
+               CoTaskMemFree(szDisplayName);\r
+               return ret;                     //to avoid a crash!\r
+       }\r
+       ret = szDisplayName;\r
+       CoTaskMemFree(szDisplayName);\r
+       return ret;\r
+}\r
+\r
+LPCITEMIDLIST ItemIDList::operator& ()\r
+{\r
+       return item_;\r
+}\r
diff --git a/TortoiseShell/ItemIDList.h b/TortoiseShell/ItemIDList.h
new file mode 100644 (file)
index 0000000..aa36d02
--- /dev/null
@@ -0,0 +1,42 @@
+// TortoiseSVN - a Windows shell extension for easy version control\r
+\r
+// Copyright (C) 2003-2006 - Stefan Kueng\r
+\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software Foundation,\r
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+//\r
+#pragma once\r
+\r
+/**\r
+ * \ingroup TortoiseShell\r
+ * Represents a list of directory elements like folders and files.\r
+ */\r
+class ItemIDList  \r
+{\r
+public:\r
+       ItemIDList(LPCITEMIDLIST item, LPCITEMIDLIST parent = 0);\r
+\r
+       int size() const;\r
+       LPCSHITEMID get(int index) const;\r
+       virtual ~ItemIDList();\r
+\r
+       stdstring toString();\r
+\r
+       LPCITEMIDLIST operator& ();\r
+private:\r
+       LPCITEMIDLIST item_;\r
+       LPCITEMIDLIST parent_;\r
+       mutable int count_;\r
+};\r
+\r
diff --git a/TortoiseShell/PIDL.cpp b/TortoiseShell/PIDL.cpp
new file mode 100644 (file)
index 0000000..c8a6b0c
--- /dev/null
@@ -0,0 +1,30 @@
+// TortoiseSVN - a Windows shell extension for easy version control\r
+\r
+// Copyright (C) 2003-2006 - Stefan Kueng\r
+\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software Foundation,\r
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+//\r
+#include "stdafx.h"\r
+#include "PIDL.h"\r
+\r
+PIDL::PIDL()\r
+{\r
+       ::SHGetMalloc(&malloc_);\r
+}\r
+\r
+PIDL::~PIDL()\r
+{\r
+       malloc_->Release();\r
+}\r
diff --git a/TortoiseShell/PIDL.h b/TortoiseShell/PIDL.h
new file mode 100644 (file)
index 0000000..76db4fa
--- /dev/null
@@ -0,0 +1,33 @@
+// TortoiseSVN - a Windows shell extension for easy version control\r
+\r
+// Copyright (C) 2003-2006 - Stefan Kueng\r
+\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software Foundation,\r
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+//\r
+#pragma once\r
+\r
+/**\r
+ * \ingroup TortoiseShell\r
+ * Represents one entry of the class ItemIDList.\r
+ */\r
+class PIDL  \r
+{\r
+public:\r
+       PIDL();\r
+       virtual ~PIDL();\r
+private:\r
+       LPMALLOC malloc_;\r
+};\r
+\r
diff --git a/TortoiseShell/PreserveChdir.cpp b/TortoiseShell/PreserveChdir.cpp
new file mode 100644 (file)
index 0000000..4073f5e
--- /dev/null
@@ -0,0 +1,55 @@
+// TortoiseSVN - a Windows shell extension for easy version control\r
+\r
+// Copyright (C) 2003-2007 - TortoiseSVN\r
+\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software Foundation,\r
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+//\r
+#include "stdafx.h"\r
+#include "PreserveChdir.h"\r
+\r
+PreserveChdir::PreserveChdir()\r
+{\r
+       DWORD len = GetCurrentDirectory(0, NULL);\r
+       if (len)\r
+       {\r
+               originalCurrentDirectory = new TCHAR[len];\r
+               if (GetCurrentDirectory(len, originalCurrentDirectory)==0)\r
+               {\r
+                       delete [] originalCurrentDirectory;\r
+                       originalCurrentDirectory = NULL;\r
+               }\r
+       }\r
+       else\r
+               originalCurrentDirectory = NULL;\r
+}\r
+\r
+PreserveChdir::~PreserveChdir()\r
+{\r
+       if (originalCurrentDirectory)\r
+       {\r
+               DWORD len = GetCurrentDirectory(0, NULL);\r
+               TCHAR * currentDirectory = new TCHAR[len];\r
+\r
+               // _tchdir is an expensive function - don't call it unless we really have to\r
+               GetCurrentDirectory(len, currentDirectory);\r
+               if(_tcscmp(currentDirectory, originalCurrentDirectory) != 0)\r
+               {\r
+                       SetCurrentDirectory(originalCurrentDirectory);\r
+               }\r
+               delete [] currentDirectory;\r
+               delete [] originalCurrentDirectory;\r
+               originalCurrentDirectory = NULL;\r
+       }\r
+}\r
diff --git a/TortoiseShell/PreserveChdir.h b/TortoiseShell/PreserveChdir.h
new file mode 100644 (file)
index 0000000..56f1a94
--- /dev/null
@@ -0,0 +1,40 @@
+// TortoiseSVN - a Windows shell extension for easy version control\r
+\r
+// Copyright (C) 2003-2006,2008 - Stefan Kueng\r
+\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software Foundation,\r
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+//\r
+#pragma once\r
+\r
+/**\r
+ * \ingroup TortoiseShell\r
+ * Helper class to keep the current directory.\r
+ * When created, the current directory is saved and restored\r
+ * as soon as the object is destroyed.\r
+ * Since some of the SVN functions change the current directory\r
+ * which when in the explorer process space causes problems / crashes,\r
+ * this class is used to reset the current directory after calling\r
+ * those functions.\r
+ */\r
+class PreserveChdir\r
+{\r
+public:\r
+       PreserveChdir();\r
+       ~PreserveChdir();\r
+\r
+private:\r
+       TCHAR  * originalCurrentDirectory;\r
+};\r
+\r
diff --git a/TortoiseShell/RemoteCacheLink.cpp b/TortoiseShell/RemoteCacheLink.cpp
new file mode 100644 (file)
index 0000000..60318f7
--- /dev/null
@@ -0,0 +1,349 @@
+// TortoiseSVN - a Windows shell extension for easy version control\r
+\r
+// Copyright (C) 2003-2008 - TortoiseSVN\r
+\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software Foundation,\r
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+//\r
+#include "StdAfx.h"\r
+#include "Remotecachelink.h"\r
+#include "ShellExt.h"\r
+#include "..\TSVNCache\CacheInterface.h"\r
+#include "TSVNPath.h"\r
+\r
+CRemoteCacheLink::CRemoteCacheLink(void) \r
+       : m_hPipe(INVALID_HANDLE_VALUE)\r
+       , m_hCommandPipe(INVALID_HANDLE_VALUE)\r
+{\r
+       SecureZeroMemory(&m_dummyStatus, sizeof(m_dummyStatus));\r
+       m_dummyStatus.text_status = svn_wc_status_none;\r
+       m_dummyStatus.prop_status = svn_wc_status_none;\r
+       m_dummyStatus.repos_text_status = svn_wc_status_none;\r
+       m_dummyStatus.repos_prop_status = svn_wc_status_none;\r
+       m_lastTimeout = 0;\r
+       m_critSec.Init();\r
+}\r
+\r
+CRemoteCacheLink::~CRemoteCacheLink(void)\r
+{\r
+       ClosePipe();\r
+       CloseCommandPipe();\r
+       m_critSec.Term();\r
+}\r
+\r
+bool CRemoteCacheLink::EnsurePipeOpen()\r
+{\r
+       AutoLocker lock(m_critSec);\r
+       if(m_hPipe != INVALID_HANDLE_VALUE)\r
+       {\r
+               return true;\r
+       }\r
+\r
+       m_hPipe = CreateFile(\r
+               GetCachePipeName(),                             // pipe name\r
+               GENERIC_READ |                                  // read and write access\r
+               GENERIC_WRITE,\r
+               0,                                                              // no sharing\r
+               NULL,                                                   // default security attributes\r
+               OPEN_EXISTING,                                  // opens existing pipe\r
+               FILE_FLAG_OVERLAPPED,                   // default attributes\r
+               NULL);                                                  // no template file\r
+\r
+       if (m_hPipe == INVALID_HANDLE_VALUE && GetLastError() == ERROR_PIPE_BUSY)\r
+       {\r
+               // TSVNCache is running but is busy connecting a different client.\r
+               // Do not give up immediately but wait for a few milliseconds until\r
+               // the server has created the next pipe instance\r
+               if (WaitNamedPipe(GetCachePipeName(), 50))\r
+               {\r
+                       m_hPipe = CreateFile(\r
+                               GetCachePipeName(),                             // pipe name\r
+                               GENERIC_READ |                                  // read and write access\r
+                               GENERIC_WRITE,\r
+                               0,                                                              // no sharing\r
+                               NULL,                                                   // default security attributes\r
+                               OPEN_EXISTING,                                  // opens existing pipe\r
+                               FILE_FLAG_OVERLAPPED,                   // default attributes\r
+                               NULL);                                                  // no template file\r
+               }\r
+       }\r
+\r
+\r
+       if (m_hPipe != INVALID_HANDLE_VALUE)\r
+       {\r
+               // The pipe connected; change to message-read mode.\r
+               DWORD dwMode;\r
+\r
+               dwMode = PIPE_READMODE_MESSAGE;\r
+               if(!SetNamedPipeHandleState(\r
+                       m_hPipe,    // pipe handle\r
+                       &dwMode,  // new pipe mode\r
+                       NULL,     // don't set maximum bytes\r
+                       NULL))    // don't set maximum time\r
+               {\r
+                       ATLTRACE("SetNamedPipeHandleState failed");\r
+                       CloseHandle(m_hPipe);\r
+                       m_hPipe = INVALID_HANDLE_VALUE;\r
+                       return false;\r
+               }\r
+               // create an unnamed (=local) manual reset event for use in the overlapped structure\r
+               m_hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
+               if (m_hEvent)\r
+                       return true;\r
+               ATLTRACE("CreateEvent failed");\r
+               ClosePipe();\r
+               return false;\r
+       }\r
+\r
+       return false;\r
+}\r
+\r
+bool CRemoteCacheLink::EnsureCommandPipeOpen()\r
+{\r
+       AutoLocker lock(m_critSec);\r
+       if(m_hCommandPipe != INVALID_HANDLE_VALUE)\r
+       {\r
+               return true;\r
+       }\r
+\r
+       m_hCommandPipe = CreateFile(\r
+               GetCacheCommandPipeName(),              // pipe name\r
+               GENERIC_READ |                                  // read and write access\r
+               GENERIC_WRITE,\r
+               0,                                                              // no sharing\r
+               NULL,                                                   // default security attributes\r
+               OPEN_EXISTING,                                  // opens existing pipe\r
+               FILE_FLAG_OVERLAPPED,                   // default attributes\r
+               NULL);                                                  // no template file\r
+\r
+       if (m_hCommandPipe == INVALID_HANDLE_VALUE && GetLastError() == ERROR_PIPE_BUSY)\r
+       {\r
+               // TSVNCache is running but is busy connecting a different client.\r
+               // Do not give up immediately but wait for a few milliseconds until\r
+               // the server has created the next pipe instance\r
+               if (WaitNamedPipe(GetCacheCommandPipeName(), 50))\r
+               {\r
+                       m_hCommandPipe = CreateFile(\r
+                               GetCacheCommandPipeName(),              // pipe name\r
+                               GENERIC_READ |                                  // read and write access\r
+                               GENERIC_WRITE,\r
+                               0,                                                              // no sharing\r
+                               NULL,                                                   // default security attributes\r
+                               OPEN_EXISTING,                                  // opens existing pipe\r
+                               FILE_FLAG_OVERLAPPED,                   // default attributes\r
+                               NULL);                                                  // no template file\r
+               }\r
+       }\r
+\r
+\r
+       if (m_hCommandPipe != INVALID_HANDLE_VALUE)\r
+       {\r
+               // The pipe connected; change to message-read mode.\r
+               DWORD dwMode;\r
+\r
+               dwMode = PIPE_READMODE_MESSAGE;\r
+               if(!SetNamedPipeHandleState(\r
+                       m_hCommandPipe,    // pipe handle\r
+                       &dwMode,  // new pipe mode\r
+                       NULL,     // don't set maximum bytes\r
+                       NULL))    // don't set maximum time\r
+               {\r
+                       ATLTRACE("SetNamedPipeHandleState failed");\r
+                       CloseHandle(m_hCommandPipe);\r
+                       m_hCommandPipe = INVALID_HANDLE_VALUE;\r
+                       return false;\r
+               }\r
+               return true;\r
+       }\r
+\r
+       return false;\r
+}\r
+\r
+void CRemoteCacheLink::ClosePipe()\r
+{\r
+       AutoLocker lock(m_critSec);\r
+\r
+       if(m_hPipe != INVALID_HANDLE_VALUE)\r
+       {\r
+               CloseHandle(m_hPipe);\r
+               CloseHandle(m_hEvent);\r
+               m_hPipe = INVALID_HANDLE_VALUE;\r
+               m_hEvent = INVALID_HANDLE_VALUE;\r
+       }\r
+}\r
+\r
+void CRemoteCacheLink::CloseCommandPipe()\r
+{\r
+       AutoLocker lock(m_critSec);\r
+\r
+       if(m_hCommandPipe != INVALID_HANDLE_VALUE)\r
+       {\r
+               // now tell the cache we don't need it's command thread anymore\r
+               DWORD cbWritten; \r
+               TSVNCacheCommand cmd;\r
+               SecureZeroMemory(&cmd, sizeof(TSVNCacheCommand));\r
+               cmd.command = TSVNCACHECOMMAND_END;\r
+               WriteFile( \r
+                       m_hCommandPipe,                 // handle to pipe \r
+                       &cmd,                   // buffer to write from \r
+                       sizeof(cmd),    // number of bytes to write \r
+                       &cbWritten,             // number of bytes written \r
+                       NULL);                  // not overlapped I/O \r
+               DisconnectNamedPipe(m_hCommandPipe); \r
+               CloseHandle(m_hCommandPipe); \r
+               m_hCommandPipe = INVALID_HANDLE_VALUE;\r
+       }\r
+}\r
+\r
+bool CRemoteCacheLink::GetStatusFromRemoteCache(const CTSVNPath& Path, TSVNCacheResponse* pReturnedStatus, bool bRecursive)\r
+{\r
+       if(!EnsurePipeOpen())\r
+       {\r
+               // We've failed to open the pipe - try and start the cache\r
+               // but only if the last try to start the cache was a certain time\r
+               // ago. If we just try over and over again without a small pause\r
+               // in between, the explorer is rendered unusable!\r
+               // Failing to start the cache can have different reasons: missing exe,\r
+               // missing registry key, corrupt exe, ...\r
+               if (((long)GetTickCount() - m_lastTimeout) < 0)\r
+                       return false;\r
+               STARTUPINFO startup;\r
+               PROCESS_INFORMATION process;\r
+               memset(&startup, 0, sizeof(startup));\r
+               startup.cb = sizeof(startup);\r
+               memset(&process, 0, sizeof(process));\r
+\r
+               CRegString cachePath(_T("Software\\TortoiseSVN\\CachePath"), _T("TSVNCache.exe"), false, HKEY_LOCAL_MACHINE);\r
+               CString sCachePath = cachePath;\r
+               if (CreateProcess(sCachePath.GetBuffer(sCachePath.GetLength()+1), _T(""), NULL, NULL, FALSE, 0, 0, 0, &startup, &process)==0)\r
+               {\r
+                       // It's not appropriate to do a message box here, because there may be hundreds of calls\r
+                       sCachePath.ReleaseBuffer();\r
+                       ATLTRACE("Failed to start cache\n");\r
+                       return false;\r
+               }\r
+               CloseHandle(process.hThread);\r
+               CloseHandle(process.hProcess);\r
+               sCachePath.ReleaseBuffer();\r
+\r
+               // Wait for the cache to open\r
+               long endTime = (long)GetTickCount()+1000;\r
+               while(!EnsurePipeOpen())\r
+               {\r
+                       if(((long)GetTickCount() - endTime) > 0)\r
+                       {\r
+                               m_lastTimeout = (long)GetTickCount()+10000;\r
+                               return false;\r
+                       }\r
+               }\r
+       }\r
+\r
+       AutoLocker lock(m_critSec);\r
+\r
+       DWORD nBytesRead;\r
+       TSVNCacheRequest request;\r
+       request.flags = TSVNCACHE_FLAGS_NONOTIFICATIONS;\r
+       if(bRecursive)\r
+       {\r
+               request.flags |= TSVNCACHE_FLAGS_RECUSIVE_STATUS;\r
+       }\r
+       wcsncpy_s(request.path, MAX_PATH+1, Path.GetWinPath(), MAX_PATH);\r
+       SecureZeroMemory(&m_Overlapped, sizeof(OVERLAPPED));\r
+       m_Overlapped.hEvent = m_hEvent;\r
+       // Do the transaction in overlapped mode.\r
+       // That way, if anything happens which might block this call\r
+       // we still can get out of it. We NEVER MUST BLOCK THE SHELL!\r
+       // A blocked shell is a very bad user impression, because users\r
+       // who don't know why it's blocked might find the only solution\r
+       // to such a problem is a reboot and therefore they might loose\r
+       // valuable data.\r
+       // One particular situation where the shell could hang is when\r
+       // the cache crashes and our crash report dialog comes up.\r
+       // Sure, it would be better to have no situations where the shell\r
+       // even can get blocked, but the timeout of 10 seconds is long enough\r
+       // so that users still recognize that something might be wrong and\r
+       // report back to us so we can investigate further.\r
+\r
+       BOOL fSuccess = TransactNamedPipe(m_hPipe,\r
+               &request, sizeof(request),\r
+               pReturnedStatus, sizeof(*pReturnedStatus),\r
+               &nBytesRead, &m_Overlapped);\r
+\r
+       if (!fSuccess)\r
+       {\r
+               if (GetLastError()!=ERROR_IO_PENDING)\r
+               {\r
+                       //OutputDebugStringA("TortoiseShell: TransactNamedPipe failed\n");\r
+                       ClosePipe();\r
+                       return false;\r
+               }\r
+\r
+               // TransactNamedPipe is working in an overlapped operation.\r
+               // Wait for it to finish\r
+               DWORD dwWait = WaitForSingleObject(m_hEvent, 10000);\r
+               if (dwWait == WAIT_OBJECT_0)\r
+               {\r
+                       fSuccess = GetOverlappedResult(m_hPipe, &m_Overlapped, &nBytesRead, FALSE);\r
+               }\r
+               else\r
+               {\r
+                       // the cache didn't respond!\r
+                       fSuccess = FALSE;\r
+               }\r
+       }\r
+\r
+       if (fSuccess)\r
+       {\r
+               if(nBytesRead == sizeof(TSVNCacheResponse))\r
+               {\r
+                       // This is a full response - we need to fix-up some pointers\r
+                       pReturnedStatus->m_status.entry = &pReturnedStatus->m_entry;\r
+                       pReturnedStatus->m_entry.url = pReturnedStatus->m_url;\r
+               }\r
+               else\r
+               {\r
+                       pReturnedStatus->m_status.entry = NULL;\r
+               }\r
+\r
+               return true;\r
+       }\r
+       ClosePipe();\r
+       return false;\r
+}\r
+\r
+bool CRemoteCacheLink::ReleaseLockForPath(const CTSVNPath& path)\r
+{\r
+       EnsureCommandPipeOpen();\r
+       if (m_hCommandPipe != INVALID_HANDLE_VALUE) \r
+       {\r
+               DWORD cbWritten; \r
+               TSVNCacheCommand cmd;\r
+               SecureZeroMemory(&cmd, sizeof(TSVNCacheCommand));\r
+               cmd.command = TSVNCACHECOMMAND_RELEASE;\r
+               wcsncpy_s(cmd.path, MAX_PATH+1, path.GetDirectory().GetWinPath(), MAX_PATH);\r
+               BOOL fSuccess = WriteFile( \r
+                       m_hCommandPipe, // handle to pipe \r
+                       &cmd,                   // buffer to write from \r
+                       sizeof(cmd),    // number of bytes to write \r
+                       &cbWritten,             // number of bytes written \r
+                       NULL);                  // not overlapped I/O \r
+               if (! fSuccess || sizeof(cmd) != cbWritten)\r
+               {\r
+                       CloseCommandPipe();\r
+                       return false;\r
+               }\r
+               return true;\r
+       }\r
+       return false;\r
+}\r
diff --git a/TortoiseShell/RemoteCacheLink.h b/TortoiseShell/RemoteCacheLink.h
new file mode 100644 (file)
index 0000000..674d209
--- /dev/null
@@ -0,0 +1,57 @@
+// TortoiseSVN - a Windows shell extension for easy version control\r
+\r
+// Copyright (C) 2003-2008 - TortoiseSVN\r
+\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software Foundation,\r
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+//\r
+#pragma once\r
+\r
+struct TSVNCacheResponse;\r
+class CTSVNPath;\r
+\r
+/**\r
+ * \ingroup TortoiseShell\r
+ * This class provides the link to the cache process.\r
+ */\r
+class CRemoteCacheLink\r
+{\r
+public:\r
+       CRemoteCacheLink(void);\r
+       ~CRemoteCacheLink(void);\r
+\r
+public:\r
+       bool GetStatusFromRemoteCache(const CTSVNPath& Path, TSVNCacheResponse* pReturnedStatus, bool bRecursive);\r
+       bool ReleaseLockForPath(const CTSVNPath& path);\r
+\r
+private:\r
+       bool EnsurePipeOpen();\r
+       void ClosePipe();\r
+\r
+       bool EnsureCommandPipeOpen();\r
+       void CloseCommandPipe();\r
+\r
+private:\r
+       HANDLE m_hPipe;\r
+       OVERLAPPED m_Overlapped;\r
+       HANDLE m_hEvent;\r
+\r
+       HANDLE m_hCommandPipe;\r
+\r
+\r
+       CComCriticalSection m_critSec;\r
+       svn_wc_status2_t m_dummyStatus;\r
+       long m_lastTimeout;\r
+\r
+};\r
diff --git a/TortoiseShell/SVNPropertyPage.cpp b/TortoiseShell/SVNPropertyPage.cpp
new file mode 100644 (file)
index 0000000..b29e9c0
--- /dev/null
@@ -0,0 +1,422 @@
+// TortoiseSVN - a Windows shell extension for easy version control\r
+\r
+// Copyright (C) 2003-2008 - TortoiseSVN\r
+\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software Foundation,\r
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 U