--- /dev/null
+// 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
--- /dev/null
+/**\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