1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2003-2012, 2014-2015 - TortoiseSVN
4 // Copyright (C) 2008-2015 - TortoiseGit
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software Foundation,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include "ItemIDList.h"
23 #include "PreserveChdir.h"
24 #include "UnicodeUtils.h"
25 #include "GitStatus.h"
27 #include "PathUtils.h"
28 #include "CreateProcessHelper.h"
29 #include "FormatMessageWrapper.h"
30 #include "..\TGitCache\CacheInterface.h"
33 #define GetPIDLFolder(pida) (LPCITEMIDLIST)(((LPBYTE)pida)+(pida)->aoffset[0])
34 #define GetPIDLItem(pida, i) (LPCITEMIDLIST)(((LPBYTE)pida)+(pida)->aoffset[i+1])
36 int g_shellidlist
=RegisterClipboardFormat(CFSTR_SHELLIDLIST
);
38 extern MenuInfo menuInfo
[];
39 static int g_syncSeq
= 0;
41 STDMETHODIMP
CShellExt::Initialize(LPCITEMIDLIST pIDFolder
,
42 LPDATAOBJECT pDataObj
,
47 return Initialize_Wrap(pIDFolder
, pDataObj
, hRegKey
);
49 __except(CCrashReport::Instance().SendReport(GetExceptionInformation()))
55 STDMETHODIMP
CShellExt::Initialize_Wrap(LPCITEMIDLIST pIDFolder
,
56 LPDATAOBJECT pDataObj
,
59 CTraceToOutputDebugString::Instance()(__FUNCTION__
": Shell :: Initialize\n");
60 PreserveChdir preserveChdir
;
68 git_wc_status_kind fetchedstatus
= git_wc_status_none
;
69 // get selected files/folders
73 FORMATETC fmte
= {(CLIPFORMAT
)g_shellidlist
,
74 (DVTARGETDEVICE FAR
*)NULL
,
78 HRESULT hres
= pDataObj
->GetData(&fmte
, &medium
);
80 if (SUCCEEDED(hres
) && medium
.hGlobal
)
82 if (m_State
== FileStateDropHandler
)
85 FORMATETC etc
= { CF_HDROP
, NULL
, DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
};
86 STGMEDIUM stg
= { TYMED_HGLOBAL
};
87 if ( FAILED( pDataObj
->GetData ( &etc
, &stg
)))
89 ReleaseStgMedium ( &medium
);
94 HDROP drop
= (HDROP
)GlobalLock(stg
.hGlobal
);
97 ReleaseStgMedium ( &stg
);
98 ReleaseStgMedium ( &medium
);
102 int count
= DragQueryFile(drop
, (UINT
)-1, NULL
, 0);
104 itemStates
|= ITEMIS_ONLYONE
;
105 for (int i
= 0; i
< count
; i
++)
107 // find the path length in chars
108 UINT len
= DragQueryFile(drop
, i
, NULL
, 0);
111 std::unique_ptr
<TCHAR
[]> szFileName(new TCHAR
[len
+ 1]);
112 if (0 == DragQueryFile(drop
, i
, szFileName
.get(), len
+ 1))
114 stdstring str
= stdstring(szFileName
.get());
115 if ((!str
.empty()) && (g_ShellCache
.IsContextPathAllowed(szFileName
.get())))
117 if (itemStates
& ITEMIS_ONLYONE
)
120 strpath
.SetFromWin(str
.c_str());
121 itemStates
|= (strpath
.GetFileExtension().CompareNoCase(_T(".diff"))==0) ? ITEMIS_PATCHFILE
: 0;
122 itemStates
|= (strpath
.GetFileExtension().CompareNoCase(_T(".patch"))==0) ? ITEMIS_PATCHFILE
: 0;
124 files_
.push_back(str
);
127 //get the git status of the item
128 git_wc_status_kind status
= git_wc_status_none
;
130 askedpath
.SetFromWin(str
.c_str());
131 CString workTreePath
;
132 askedpath
.HasAdminDir(&workTreePath
);
133 uuidSource
= workTreePath
;
136 if (g_ShellCache
.GetCacheType() == ShellCache::exe
&& g_ShellCache
.IsPathAllowed(str
.c_str()))
138 CTGitPath
tpath(str
.c_str());
139 if (!tpath
.HasAdminDir())
141 status
= git_wc_status_none
;
144 if (tpath
.IsAdminDir())
146 status
= git_wc_status_none
;
149 TGITCacheResponse itemStatus
;
150 SecureZeroMemory(&itemStatus
, sizeof(itemStatus
));
151 if (m_remoteCacheLink
.GetStatusFromRemoteCache(tpath
, &itemStatus
, true))
153 fetchedstatus
= status
= GitStatus::GetMoreImportant(itemStatus
.m_status
.text_status
, itemStatus
.m_status
.prop_status
);
154 if (askedpath
.IsDirectory())//if ((stat.status->entry)&&(stat.status->entry->kind == git_node_dir))
156 itemStates
|= ITEMIS_FOLDER
;
157 if ((status
!= git_wc_status_unversioned
) && (status
!= git_wc_status_ignored
) && (status
!= git_wc_status_none
))
158 itemStates
|= ITEMIS_FOLDERINGIT
;
165 stat
.GetStatus(CTGitPath(str
.c_str()), false, false, true);
169 status
= GitStatus::GetMoreImportant(stat
.status
->text_status
, stat
.status
->prop_status
);
170 fetchedstatus
= status
;
171 if ( askedpath
.IsDirectory() )//if ((stat.status->entry)&&(stat.status->entry->kind == git_node_dir))
173 itemStates
|= ITEMIS_FOLDER
;
174 if ((status
!= git_wc_status_unversioned
)&&(status
!= git_wc_status_ignored
)&&(status
!= git_wc_status_none
))
175 itemStates
|= ITEMIS_FOLDERINGIT
;
177 //if ((stat.status->entry)&&(stat.status->entry->uuid))
178 // uuidSource = CUnicodeUtils::StdGetUnicode(stat.status->entry->uuid);
182 // sometimes, git_client_status() returns with an error.
183 // in that case, we have to check if the working copy is versioned
184 // anyway to show the 'correct' context menu
185 if (askedpath
.HasAdminDir())
186 status
= git_wc_status_normal
;
192 CTraceToOutputDebugString::Instance()(__FUNCTION__
": Exception in GitStatus::GetStatus()\n");
195 // TODO: should we really assume any sub-directory to be versioned
196 // or only if it contains versioned files
197 itemStates
|= askedpath
.GetAdminDirMask();
199 if ((status
== git_wc_status_unversioned
) || (status
== git_wc_status_ignored
) || (status
== git_wc_status_none
))
200 itemStates
&= ~ITEMIS_INGIT
;
202 if (status
== git_wc_status_ignored
)
203 itemStates
|= ITEMIS_IGNORED
;
204 if (status
== git_wc_status_normal
)
205 itemStates
|= ITEMIS_NORMAL
;
206 if (status
== git_wc_status_conflicted
)
207 itemStates
|= ITEMIS_CONFLICTED
;
208 if (status
== git_wc_status_added
)
209 itemStates
|= ITEMIS_ADDED
;
210 if (status
== git_wc_status_deleted
)
211 itemStates
|= ITEMIS_DELETED
;
214 } // for (int i = 0; i < count; i++)
215 GlobalUnlock ( drop
);
216 ReleaseStgMedium ( &stg
);
218 } // if (m_State == FileStateDropHandler)
222 //Enumerate PIDLs which the user has selected
223 CIDA
* cida
= (CIDA
*)GlobalLock(medium
.hGlobal
);
224 ItemIDList
parent( GetPIDLFolder (cida
));
226 int count
= cida
->cidl
;
227 BOOL statfetched
= FALSE
;
228 for (int i
= 0; i
< count
; ++i
)
230 ItemIDList
child (GetPIDLItem (cida
, i
), &parent
);
231 stdstring str
= child
.toString();
232 if ((str
.empty() == false)&&(g_ShellCache
.IsContextPathAllowed(str
.c_str())))
234 //check if our menu is requested for a git admin directory
235 if (GitAdminDir::IsAdminDirPath(str
.c_str()))
238 files_
.push_back(str
);
240 strpath
.SetFromWin(str
.c_str());
241 itemStates
|= (strpath
.GetFileExtension().CompareNoCase(_T(".diff"))==0) ? ITEMIS_PATCHFILE
: 0;
242 itemStates
|= (strpath
.GetFileExtension().CompareNoCase(_T(".patch"))==0) ? ITEMIS_PATCHFILE
: 0;
245 //get the git status of the item
246 git_wc_status_kind status
= git_wc_status_none
;
247 if ((g_ShellCache
.IsSimpleContext())&&(strpath
.IsDirectory()))
249 if (strpath
.HasAdminDir())
250 status
= git_wc_status_normal
;
256 if (g_ShellCache
.GetCacheType() == ShellCache::exe
&& g_ShellCache
.IsPathAllowed(str
.c_str()))
258 CTGitPath
tpath(str
.c_str());
259 if(!tpath
.HasAdminDir())
261 status
= git_wc_status_none
;
264 if(tpath
.IsAdminDir())
266 status
= git_wc_status_none
;
269 TGITCacheResponse itemStatus
;
270 SecureZeroMemory(&itemStatus
, sizeof(itemStatus
));
271 if (m_remoteCacheLink
.GetStatusFromRemoteCache(tpath
, &itemStatus
, true))
273 fetchedstatus
= status
= GitStatus::GetMoreImportant(itemStatus
.m_status
.text_status
, itemStatus
.m_status
.prop_status
);
274 if (strpath
.IsDirectory())//if ((stat.status->entry)&&(stat.status->entry->kind == git_node_dir))
276 itemStates
|= ITEMIS_FOLDER
;
277 if ((status
!= git_wc_status_unversioned
) && (status
!= git_wc_status_ignored
) && (status
!= git_wc_status_none
))
278 itemStates
|= ITEMIS_FOLDERINGIT
;
280 if (status
== git_wc_status_conflicted
)//if ((stat.status->entry)&&(stat.status->entry->conflict_wrk))
281 itemStates
|= ITEMIS_CONFLICTED
;
287 if (strpath
.HasAdminDir())
288 stat
.GetStatus(strpath
, false, false, true);
292 status
= GitStatus::GetMoreImportant(stat
.status
->text_status
, stat
.status
->prop_status
);
293 fetchedstatus
= status
;
294 if ( strpath
.IsDirectory() )//if ((stat.status->entry)&&(stat.status->entry->kind == git_node_dir))
296 itemStates
|= ITEMIS_FOLDER
;
297 if ((status
!= git_wc_status_unversioned
)&&(status
!= git_wc_status_ignored
)&&(status
!= git_wc_status_none
))
298 itemStates
|= ITEMIS_FOLDERINGIT
;
300 // TODO: do we need to check that it's not a dir? does conflict options makes sense for dir in git?
301 if (status
== git_wc_status_conflicted
)//if ((stat.status->entry)&&(stat.status->entry->conflict_wrk))
302 itemStates
|= ITEMIS_CONFLICTED
;
303 //if ((stat.status->entry)&&(stat.status->entry->uuid))
304 // uuidSource = CUnicodeUtils::StdGetUnicode(stat.status->entry->uuid);
308 // sometimes, git_client_status() returns with an error.
309 // in that case, we have to check if the working copy is versioned
310 // anyway to show the 'correct' context menu
311 if (strpath
.HasAdminDir())
313 status
= git_wc_status_normal
;
314 fetchedstatus
= status
;
322 CTraceToOutputDebugString::Instance()(__FUNCTION__
": Exception in GitStatus::GetStatus()\n");
326 itemStates
|= strpath
.GetAdminDirMask();
328 if ((status
== git_wc_status_unversioned
)||(status
== git_wc_status_ignored
)||(status
== git_wc_status_none
))
329 itemStates
&= ~ITEMIS_INGIT
;
330 if (status
== git_wc_status_ignored
)
332 itemStates
|= ITEMIS_IGNORED
;
335 if (status
== git_wc_status_normal
)
336 itemStates
|= ITEMIS_NORMAL
;
337 if (status
== git_wc_status_conflicted
)
338 itemStates
|= ITEMIS_CONFLICTED
;
339 if (status
== git_wc_status_added
)
340 itemStates
|= ITEMIS_ADDED
;
341 if (status
== git_wc_status_deleted
)
342 itemStates
|= ITEMIS_DELETED
;
345 } // for (int i = 0; i < count; ++i)
346 ItemIDList
child (GetPIDLItem (cida
, 0), &parent
);
347 if (g_ShellCache
.HasGITAdminDir(child
.toString().c_str(), FALSE
))
348 itemStates
|= ITEMIS_INVERSIONEDFOLDER
;
350 if (GitAdminDir::IsBareRepo(child
.toString().c_str()))
351 itemStates
= ITEMIS_BAREREPO
;
353 GlobalUnlock(medium
.hGlobal
);
355 // if the item is a versioned folder, check if there's a patch file
356 // in the clipboard to be used in "Apply Patch"
357 UINT cFormatDiff
= RegisterClipboardFormat(_T("TGIT_UNIFIEDDIFF"));
360 if (IsClipboardFormatAvailable(cFormatDiff
))
361 itemStates
|= ITEMIS_PATCHINCLIPBOARD
;
363 if (IsClipboardFormatAvailable(CF_HDROP
))
364 itemStates
|= ITEMIS_PATHINCLIPBOARD
;
368 ReleaseStgMedium ( &medium
);
369 if (medium
.pUnkForRelease
)
371 IUnknown
* relInterface
= (IUnknown
*)medium
.pUnkForRelease
;
372 relInterface
->Release();
377 // get folder background
381 ItemIDList
list(pIDFolder
);
382 folder_
= list
.toString();
383 git_wc_status_kind status
= git_wc_status_none
;
384 if (IsClipboardFormatAvailable(CF_HDROP
))
385 itemStatesFolder
|= ITEMIS_PATHINCLIPBOARD
;
388 askedpath
.SetFromWin(folder_
.c_str());
390 if (g_ShellCache
.IsContextPathAllowed(folder_
.c_str()))
392 if (folder_
.compare(statuspath
)!=0)
394 CString worktreePath
;
395 askedpath
.HasAdminDir(&worktreePath
);
396 uuidTarget
= worktreePath
;
399 if (g_ShellCache
.GetCacheType() == ShellCache::exe
&& g_ShellCache
.IsPathAllowed(folder_
.c_str()))
401 CTGitPath
tpath(folder_
.c_str());
402 if(!tpath
.HasAdminDir())
403 status
= git_wc_status_none
;
404 else if(tpath
.IsAdminDir())
405 status
= git_wc_status_none
;
408 TGITCacheResponse itemStatus
;
409 SecureZeroMemory(&itemStatus
, sizeof(itemStatus
));
410 if (m_remoteCacheLink
.GetStatusFromRemoteCache(tpath
, &itemStatus
, true))
411 status
= GitStatus::GetMoreImportant(itemStatus
.m_status
.text_status
, itemStatus
.m_status
.prop_status
);
417 stat
.GetStatus(CTGitPath(folder_
.c_str()), false, false, true);
420 status
= GitStatus::GetMoreImportant(stat
.status
->text_status
, stat
.status
->prop_status
);
421 // if ((stat.status->entry)&&(stat.status->entry->uuid))
422 // uuidTarget = CUnicodeUtils::StdGetUnicode(stat.status->entry->uuid);
427 // sometimes, git_client_status() returns with an error.
428 // in that case, we have to check if the working copy is versioned
429 // anyway to show the 'correct' context menu
430 if (askedpath
.HasAdminDir())
431 status
= git_wc_status_normal
;
435 //if ((status != git_wc_status_unversioned)&&(status != git_wc_status_ignored)&&(status != git_wc_status_none))
436 itemStatesFolder
|= askedpath
.GetAdminDirMask();
438 if ((status
== git_wc_status_unversioned
)||(status
== git_wc_status_ignored
)||(status
== git_wc_status_none
))
439 itemStates
&= ~ITEMIS_INGIT
;
441 if (status
== git_wc_status_normal
)
442 itemStatesFolder
|= ITEMIS_NORMAL
;
443 if (status
== git_wc_status_conflicted
)
444 itemStatesFolder
|= ITEMIS_CONFLICTED
;
445 if (status
== git_wc_status_added
)
446 itemStatesFolder
|= ITEMIS_ADDED
;
447 if (status
== git_wc_status_deleted
)
448 itemStatesFolder
|= ITEMIS_DELETED
;
453 CTraceToOutputDebugString::Instance()(__FUNCTION__
": Exception in GitStatus::GetStatus()\n");
458 status
= fetchedstatus
;
460 //if ((status != git_wc_status_unversioned)&&(status != git_wc_status_ignored)&&(status != git_wc_status_none))
461 itemStatesFolder
|= askedpath
.GetAdminDirMask();
463 if (status
== git_wc_status_ignored
)
464 itemStatesFolder
|= ITEMIS_IGNORED
;
465 itemStatesFolder
|= ITEMIS_FOLDER
;
467 itemStates
|= ITEMIS_ONLYONE
;
468 if (m_State
!= FileStateDropHandler
)
469 itemStates
|= itemStatesFolder
;
474 status
= fetchedstatus
;
477 if (files_
.size() == 2)
478 itemStates
|= ITEMIS_TWO
;
479 if ((files_
.size() == 1)&&(g_ShellCache
.IsContextPathAllowed(files_
.front().c_str())))
482 itemStates
|= ITEMIS_ONLYONE
;
483 if (m_State
!= FileStateDropHandler
)
485 if (PathIsDirectory(files_
.front().c_str()))
487 folder_
= files_
.front();
488 git_wc_status_kind status
= git_wc_status_none
;
490 askedpath
.SetFromWin(folder_
.c_str());
492 if (folder_
.compare(statuspath
)!=0)
497 stat
.GetStatus(CTGitPath(folder_
.c_str()), false, false, true);
500 status
= GitStatus::GetMoreImportant(stat
.status
->text_status
, stat
.status
->prop_status
);
501 // if ((stat.status->entry)&&(stat.status->entry->uuid))
502 // uuidTarget = CUnicodeUtils::StdGetUnicode(stat.status->entry->uuid);
507 CTraceToOutputDebugString::Instance()(__FUNCTION__
": Exception in GitStatus::GetStatus()\n");
512 status
= fetchedstatus
;
514 //if ((status != git_wc_status_unversioned)&&(status != git_wc_status_ignored)&&(status != git_wc_status_none))
515 itemStates
|= askedpath
.GetAdminDirMask();
517 if ((status
== git_wc_status_unversioned
)||(status
== git_wc_status_ignored
)||(status
== git_wc_status_none
))
518 itemStates
&= ~ITEMIS_INGIT
;
520 if (status
== git_wc_status_ignored
)
521 itemStates
|= ITEMIS_IGNORED
;
522 itemStates
|= ITEMIS_FOLDER
;
523 if (status
== git_wc_status_added
)
524 itemStates
|= ITEMIS_ADDED
;
525 if (status
== git_wc_status_deleted
)
526 itemStates
|= ITEMIS_DELETED
;
536 void CShellExt::InsertGitMenu(BOOL istop
, HMENU menu
, UINT pos
, UINT_PTR id
, UINT stringid
, UINT icon
, UINT idCmdFirst
, GitCommands com
, UINT
/*uFlags*/)
538 TCHAR menutextbuffer
[512] = {0};
539 TCHAR verbsbuffer
[255] = {0};
540 MAKESTRING(stringid
);
544 //menu entry for the top context menu, so append an "Git " before
545 //the menu text to indicate where the entry comes from
546 _tcscpy_s(menutextbuffer
, 255, _T("Git "));
547 if (!g_ShellCache
.HasShellMenuAccelerators())
549 // remove the accelerators
550 tstring temp
= stringtablebuffer
;
551 temp
.erase(std::remove(temp
.begin(), temp
.end(), '&'), temp
.end());
552 _tcscpy_s(stringtablebuffer
, 255, temp
.c_str());
555 _tcscat_s(menutextbuffer
, 255, stringtablebuffer
);
557 // insert branch name into "Git Commit..." entry, so it looks like "Git Commit "master"..."
558 // so we have an easy and fast way to check the current branch
559 // (the other alternative is using a separate disabled menu entry, the code is already done but commented out)
560 if (com
== ShellMenuCommit
)
563 CTGitPath
path(folder_
.empty() ? files_
.front().c_str() : folder_
.c_str());
564 CString sProjectRoot
;
567 if (path
.GetAdminDirMask() & ITEMIS_SUBMODULE
)
570 _tcscpy_s(menutextbuffer
, 255, _T("Git "));
572 menutextbuffer
[0] = '\0';
573 MAKESTRING(IDS_MENUCOMMITSUBMODULE
);
574 _tcscat_s(menutextbuffer
, 255, stringtablebuffer
);
577 if (path
.HasAdminDir(&sProjectRoot
) && !CGit::GetCurrentBranchFromFile(sProjectRoot
, sBranchName
))
579 if (sBranchName
.GetLength() == 40)
581 // if SHA1 only show 4 first bytes
583 for (int i
=0; i
<40; i
++)
584 if ( !iswxdigit(sBranchName
[i
]) )
590 sBranchName
= sBranchName
.Left(8) + _T("....");
594 if (sBranchName
.GetLength() > 64)
595 sBranchName
= sBranchName
.Left(64) + _T("...");
597 // scan to before "..."
598 LPTSTR s
= menutextbuffer
+ _tcslen(menutextbuffer
)-1;
599 if (s
> menutextbuffer
)
601 while (s
> menutextbuffer
)
616 // append branch name and end with ...
617 _tcscpy_s(s
, 255 - _tcslen(menutextbuffer
) - 1, _T(" -> \"") + sBranchName
+ _T("\"..."));
621 if (com
== ShellMenuDiffLater
)
623 std::wstring sPath
= regDiffLater
;
626 // add the path of the saved file
627 wchar_t compact
[40] = {0};
628 PathCompactPathEx(compact
, sPath
.c_str(), _countof(compact
) - 1, 0);
629 MAKESTRING(IDS_MENUDIFFNOW
);
631 sMenu
.Format(CString(stringtablebuffer
), compact
);
632 wcscpy_s(menutextbuffer
, sMenu
);
636 MENUITEMINFO menuiteminfo
;
637 SecureZeroMemory(&menuiteminfo
, sizeof(menuiteminfo
));
638 menuiteminfo
.cbSize
= sizeof(menuiteminfo
);
639 menuiteminfo
.fMask
= MIIM_FTYPE
| MIIM_ID
| MIIM_STRING
;
640 menuiteminfo
.fType
= MFT_STRING
;
641 menuiteminfo
.dwTypeData
= menutextbuffer
;
644 menuiteminfo
.fMask
|= MIIM_BITMAP
;
645 menuiteminfo
.hbmpItem
= SysInfo::Instance().IsVistaOrLater() ? m_iconBitmapUtils
.IconToBitmapPARGB32(g_hResInst
, icon
) : HBMMENU_CALLBACK
;
647 menuiteminfo
.wID
= (UINT
)id
;
648 InsertMenuItem(menu
, pos
, TRUE
, &menuiteminfo
);
652 //menu entry for the top context menu, so append an "Git " before
653 //the menu text to indicate where the entry comes from
654 _tcscpy_s(menutextbuffer
, 255, _T("Git "));
656 LoadString(g_hResInst
, stringid
, verbsbuffer
, _countof(verbsbuffer
));
657 _tcscat_s(menutextbuffer
, 255, verbsbuffer
);
658 stdstring verb
= stdstring(menutextbuffer
);
659 if (verb
.find('&') != -1)
661 verb
.erase(verb
.find('&'),1);
663 myVerbsMap
[verb
] = id
- idCmdFirst
;
664 myVerbsMap
[verb
] = id
;
665 myVerbsIDMap
[id
- idCmdFirst
] = verb
;
666 myVerbsIDMap
[id
] = verb
;
667 // We store the relative and absolute diameter
668 // (drawitem callback uses absolute, others relative)
669 myIDMap
[id
- idCmdFirst
] = com
;
672 mySubMenuMap
[pos
] = com
;
675 bool CShellExt::WriteClipboardPathsToTempFile(stdstring
& tempfile
)
678 tempfile
= stdstring();
679 //write all selected files and paths to a temporary file
680 //for TortoiseGitProc.exe to read out again.
682 DWORD pathlength
= GetTortoiseGitTempPath(0, NULL
);
683 std::unique_ptr
<TCHAR
[]> path(new TCHAR
[pathlength
+ 1]);
684 std::unique_ptr
<TCHAR
[]> tempFile(new TCHAR
[pathlength
+ 100]);
685 GetTortoiseGitTempPath(pathlength
+1, path
.get());
686 GetTempFileName(path
.get(), _T("git"), 0, tempFile
.get());
687 tempfile
= stdstring(tempFile
.get());
689 CAutoFile file
= ::CreateFile(tempFile
.get(),
694 FILE_ATTRIBUTE_TEMPORARY
,
700 if (!IsClipboardFormatAvailable(CF_HDROP
))
702 if (!OpenClipboard(NULL
))
705 stdstring sClipboardText
;
706 HGLOBAL hglb
= GetClipboardData(CF_HDROP
);
707 HDROP hDrop
= (HDROP
)GlobalLock(hglb
);
710 TCHAR szFileName
[MAX_PATH
] = {0};
711 UINT cFiles
= DragQueryFile(hDrop
, 0xFFFFFFFF, NULL
, 0);
712 for(UINT i
= 0; i
< cFiles
; ++i
)
714 DragQueryFile(hDrop
, i
, szFileName
, _countof(szFileName
));
715 stdstring filename
= szFileName
;
716 ::WriteFile (file
, filename
.c_str(), (DWORD
)filename
.size()*sizeof(TCHAR
), &written
, 0);
717 ::WriteFile (file
, _T("\n"), 2, &written
, 0);
729 stdstring
CShellExt::WriteFileListToTempFile()
731 //write all selected files and paths to a temporary file
732 //for TortoiseGitProc.exe to read out again.
733 DWORD pathlength
= GetTortoiseGitTempPath(0, NULL
);
734 std::unique_ptr
<TCHAR
[]> path(new TCHAR
[pathlength
+ 1]);
735 std::unique_ptr
<TCHAR
[]> tempFile(new TCHAR
[pathlength
+ 100]);
736 GetTortoiseGitTempPath(pathlength
+ 1, path
.get());
737 GetTempFileName(path
.get(), _T("git"), 0, tempFile
.get());
738 stdstring retFilePath
= stdstring(tempFile
.get());
740 CAutoFile file
= ::CreateFile (tempFile
.get(),
745 FILE_ATTRIBUTE_TEMPORARY
,
754 ::WriteFile (file
, folder_
.c_str(), (DWORD
)folder_
.size()*sizeof(TCHAR
), &written
, 0);
755 ::WriteFile (file
, _T("\n"), 2, &written
, 0);
758 for (std::vector
<stdstring
>::iterator I
= files_
.begin(); I
!= files_
.end(); ++I
)
760 ::WriteFile (file
, I
->c_str(), (DWORD
)I
->size()*sizeof(TCHAR
), &written
, 0);
761 ::WriteFile (file
, _T("\n"), 2, &written
, 0);
766 STDMETHODIMP
CShellExt::QueryDropContext(UINT uFlags
, UINT idCmdFirst
, HMENU hMenu
, UINT
&indexMenu
)
768 if (!CRegStdDWORD(L
"Software\\TortoiseGit\\EnableDragContextMenu", TRUE
))
771 PreserveChdir preserveChdir
;
774 if ((uFlags
& CMF_DEFAULTONLY
)!=0)
775 return S_OK
; //we don't change the default action
777 if (files_
.empty() || folder_
.empty())
780 if (((uFlags
& 0x000f)!=CMF_NORMAL
)&&(!(uFlags
& CMF_EXPLORE
))&&(!(uFlags
& CMF_VERBSONLY
)))
783 bool bSourceAndTargetFromSameRepository
= (uuidSource
.compare(uuidTarget
) == 0) || uuidSource
.empty() || uuidTarget
.empty();
785 //the drop handler only has eight commands, but not all are visible at the same time:
786 //if the source file(s) are under version control then those files can be moved
787 //to the new location or they can be moved with a rename,
788 //if they are unversioned then they can be added to the working copy
789 //if they are versioned, they also can be exported to an unversioned location
790 UINT idCmd
= idCmdFirst
;
792 bool moveAvailable
= false;
794 // available if source is versioned but not added, target is versioned, source and target from same repository or target folder is added
795 if ((bSourceAndTargetFromSameRepository
|| (itemStatesFolder
& ITEMIS_ADDED
)) && (itemStatesFolder
& ITEMIS_FOLDERINGIT
) && ((itemStates
& (ITEMIS_NORMAL
| ITEMIS_INGIT
| ITEMIS_FOLDERINGIT
)) && ((~itemStates
) & ITEMIS_ADDED
)))
797 InsertGitMenu(FALSE
, hMenu
, indexMenu
++, idCmd
++, IDS_DROPMOVEMENU
, 0, idCmdFirst
, ShellMenuDropMove
, uFlags
);
798 moveAvailable
= true;
801 // Git move and rename here
802 // 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
803 if ((bSourceAndTargetFromSameRepository
|| (itemStatesFolder
& ITEMIS_ADDED
)) && (itemStatesFolder
& ITEMIS_FOLDERINGIT
) && (itemStates
& (ITEMIS_NORMAL
| ITEMIS_INGIT
| ITEMIS_FOLDERINGIT
)) && (itemStates
& ITEMIS_ONLYONE
) && ((~itemStates
) & ITEMIS_ADDED
))
805 InsertGitMenu(FALSE
, hMenu
, indexMenu
++, idCmd
++, IDS_DROPMOVERENAMEMENU
, 0, idCmdFirst
, ShellMenuDropMoveRename
, uFlags
);
806 moveAvailable
= true;
810 // available if source is versioned but not added, target is versioned, source and target from same repository or target folder is added
811 //if ((bSourceAndTargetFromSameRepository||(itemStatesFolder & ITEMIS_ADDED))&&(itemStatesFolder & ITEMIS_FOLDERINGIT)&&(itemStates & ITEMIS_INGIT)&&((~itemStates) & ITEMIS_ADDED))
812 // InsertGitMenu(FALSE, hMenu, indexMenu++, idCmd++, IDS_DROPCOPYMENU, 0, idCmdFirst, ShellMenuDropCopy, uFlags);
814 // Git copy and rename here, source and target from same repository
815 // available if source is a single, versioned but not added item, target is versioned or target folder is added
816 //if ((bSourceAndTargetFromSameRepository||(itemStatesFolder & ITEMIS_ADDED))&&(itemStatesFolder & ITEMIS_FOLDERINGIT)&&(itemStates & ITEMIS_INGIT)&&(itemStates & ITEMIS_ONLYONE)&&((~itemStates) & ITEMIS_ADDED))
817 // InsertGitMenu(FALSE, hMenu, indexMenu++, idCmd++, IDS_DROPCOPYRENAMEMENU, 0, idCmdFirst, ShellMenuDropCopyRename, uFlags);
820 // available if target is versioned and source is either unversioned or from another repository
821 if ((itemStatesFolder
& ITEMIS_FOLDERINGIT
) && (((~itemStates
) & ITEMIS_INGIT
) || !bSourceAndTargetFromSameRepository
) && !moveAvailable
)
822 InsertGitMenu(FALSE
, hMenu
, indexMenu
++, idCmd
++, IDS_DROPCOPYADDMENU
, 0, idCmdFirst
, ShellMenuDropCopyAdd
, uFlags
);
825 // available if source is versioned and a folder
826 //if ((itemStates & ITEMIS_INGIT)&&(itemStates & ITEMIS_FOLDER))
827 // InsertGitMenu(FALSE, hMenu, indexMenu++, idCmd++, IDS_DROPEXPORTMENU, 0, idCmdFirst, ShellMenuDropExport, uFlags);
829 // Git export all here
830 // available if source is versioned and a folder
831 //if ((itemStates & ITEMIS_INGIT)&&(itemStates & ITEMIS_FOLDER))
832 // InsertGitMenu(FALSE, hMenu, indexMenu++, idCmd++, IDS_DROPEXPORTEXTENDEDMENU, 0, idCmdFirst, ShellMenuDropExportExtended, uFlags);
835 // available if source is a patchfile
836 if (itemStates
& ITEMIS_PATCHFILE
)
837 InsertGitMenu(FALSE
, hMenu
, indexMenu
++, idCmd
++, IDS_MENUAPPLYPATCH
, 0, idCmdFirst
, ShellMenuApplyPatch
, uFlags
);
840 if (idCmd
!= idCmdFirst
)
841 InsertMenu(hMenu
, indexMenu
++, MF_SEPARATOR
|MF_BYPOSITION
, 0, NULL
);
845 return ResultFromScode(MAKE_SCODE(SEVERITY_SUCCESS
, 0, (USHORT
)(idCmd
- idCmdFirst
)));
848 STDMETHODIMP
CShellExt::QueryContextMenu(HMENU hMenu
,
856 return QueryContextMenu_Wrap(hMenu
, indexMenu
, idCmdFirst
, idCmdLast
, uFlags
);
858 __except(CCrashReport::Instance().SendReport(GetExceptionInformation()))
864 STDMETHODIMP
CShellExt::QueryContextMenu_Wrap(HMENU hMenu
,
870 CTraceToOutputDebugString::Instance()(__FUNCTION__
": Shell :: QueryContextMenu itemStates=%ld\n", itemStates
);
871 PreserveChdir preserveChdir
;
873 //first check if our drop handler is called
874 //and then (if true) provide the context menu for the
876 if (m_State
== FileStateDropHandler
)
878 return QueryDropContext(uFlags
, idCmdFirst
, hMenu
, indexMenu
);
881 if ((uFlags
& CMF_DEFAULTONLY
)!=0)
882 return S_OK
; //we don't change the default action
884 if (files_
.empty() && folder_
.empty())
887 if (((uFlags
& 0x000f)!=CMF_NORMAL
)&&(!(uFlags
& CMF_EXPLORE
))&&(!(uFlags
& CMF_VERBSONLY
)))
894 CSIDL_COMMON_FAVORITES
,
895 CSIDL_COMMON_STARTMENU
,
896 CSIDL_COMPUTERSNEARME
,
904 CSIDL_INTERNET_CACHE
,
914 if (IsIllegalFolder(folder_
, csidlarray
))
919 // folder is empty, but maybe files are selected
921 return S_OK
; // nothing selected - we don't have a menu to show
922 // check whether a selected entry is an UID - those are namespace extensions
923 // which we can't handle
924 for (std::vector
<stdstring
>::const_iterator it
= files_
.begin(); it
!= files_
.end(); ++it
)
926 if (_tcsncmp(it
->c_str(), _T("::{"), 3)==0)
932 if (_tcsncmp(folder_
.c_str(), _T("::{"), 3) == 0)
936 if (((uFlags
& CMF_EXTENDEDVERBS
) == 0) && g_ShellCache
.HideMenusForUnversionedItems())
938 if ((itemStates
& (ITEMIS_INGIT
|ITEMIS_INVERSIONEDFOLDER
|ITEMIS_FOLDERINGIT
|ITEMIS_BAREREPO
|ITEMIS_TWO
))==0)
942 //check if our menu is requested for a git admin directory
943 if (GitAdminDir::IsAdminDirPath(folder_
.c_str()))
946 if (uFlags
& CMF_EXTENDEDVERBS
)
947 itemStates
|= ITEMIS_EXTENDED
;
950 if (!std::wstring(regDiffLater
).empty())
951 itemStates
|= ITEMIS_HASDIFFLATER
;
953 const BOOL bShortcut
= !!(uFlags
& CMF_VERBSONLY
);
954 if ( bShortcut
&& (files_
.size()==1))
956 // Don't show the context menu for a link if the
957 // destination is not part of a working copy.
958 // It would only show the standard menu items
959 // which are already shown for the lnk-file.
960 CString path
= files_
.front().c_str();
961 if (!GitAdminDir::HasAdminDir(path
))
967 //check if we already added our menu entry for a folder.
968 //we check that by iterating through all menu entries and check if
969 //the dwItemData member points to our global ID string. That string is set
970 //by our shell extension when the folder menu is inserted.
971 TCHAR menubuf
[MAX_PATH
] = {0};
972 int count
= GetMenuItemCount(hMenu
);
973 for (int i
=0; i
<count
; ++i
)
976 SecureZeroMemory(&miif
, sizeof(MENUITEMINFO
));
977 miif
.cbSize
= sizeof(MENUITEMINFO
);
978 miif
.fMask
= MIIM_DATA
;
979 miif
.dwTypeData
= menubuf
;
980 miif
.cch
= _countof(menubuf
);
981 GetMenuItemInfo(hMenu
, i
, TRUE
, &miif
);
982 if (miif
.dwItemData
== (ULONG_PTR
)g_MenuIDString
)
987 UINT idCmd
= idCmdFirst
;
989 //create the sub menu
990 HMENU subMenu
= CreateMenu();
991 int indexSubMenu
= 0;
993 unsigned __int64 topmenu
= g_ShellCache
.GetMenuLayout();
994 unsigned __int64 menumask
= g_ShellCache
.GetMenuMask();
995 unsigned __int64 menuex
= g_ShellCache
.GetMenuExt();
998 bool bAddSeparator
= false;
999 bool bMenuEntryAdded
= false;
1000 bool bMenuEmpty
= true;
1001 // insert separator at start
1002 InsertMenu(hMenu
, indexMenu
++, MF_SEPARATOR
|MF_BYPOSITION
, 0, NULL
); idCmd
++;
1003 bool bShowIcons
= !!DWORD(CRegStdDWORD(_T("Software\\TortoiseGit\\ShowContextMenuIcons"), TRUE
));
1005 while (menuInfo
[menuIndex
].command
!= ShellMenuLastEntry
)
1007 if (menuInfo
[menuIndex
].command
== ShellSeparator
)
1009 // we don't add a separator immediately. Because there might not be
1010 // another 'normal' menu entry after we insert a separator.
1011 // we simply set a flag here, indicating that before the next
1012 // 'normal' menu entry, a separator should be added.
1014 bAddSeparator
= true;
1015 if (bMenuEntryAdded
)
1016 bAddSeparator
= true;
1020 // check the conditions whether to show the menu entry or not
1021 bool bInsertMenu
= ShouldInsertItem(menuInfo
[menuIndex
]);
1022 if (menuInfo
[menuIndex
].menuID
& menuex
)
1024 if( !(itemStates
& ITEMIS_EXTENDED
) )
1026 bInsertMenu
= false;
1030 if (menuInfo
[menuIndex
].menuID
& (~menumask
))
1034 bool bIsTop
= ((topmenu
& menuInfo
[menuIndex
].menuID
) != 0);
1035 // insert a separator
1036 if ((bMenuEntryAdded
)&&(bAddSeparator
)&&(!bIsTop
))
1038 bAddSeparator
= false;
1039 bMenuEntryAdded
= false;
1040 InsertMenu(subMenu
, indexSubMenu
++, MF_SEPARATOR
|MF_BYPOSITION
, 0, NULL
);
1044 // handle special cases (sub menus)
1045 if ((menuInfo
[menuIndex
].command
== ShellMenuIgnoreSub
)||(menuInfo
[menuIndex
].command
== ShellMenuUnIgnoreSub
)||(menuInfo
[menuIndex
].command
== ShellMenuDeleteIgnoreSub
))
1047 if(InsertIgnoreSubmenus(idCmd
, idCmdFirst
, hMenu
, subMenu
, indexMenu
, indexSubMenu
, topmenu
, bShowIcons
, uFlags
))
1048 bMenuEntryAdded
= true;
1052 bIsTop
= ((topmenu
& menuInfo
[menuIndex
].menuID
) != 0);
1054 // insert the menu entry
1055 InsertGitMenu( bIsTop
,
1056 bIsTop
? hMenu
: subMenu
,
1057 bIsTop
? indexMenu
++ : indexSubMenu
++,
1059 menuInfo
[menuIndex
].menuTextID
,
1060 bShowIcons
? menuInfo
[menuIndex
].iconID
: 0,
1062 menuInfo
[menuIndex
].command
,
1066 bMenuEntryAdded
= true;
1068 bAddSeparator
= false;
1077 // do not show TortoiseGit menu if it's empty
1080 if (idCmd
- idCmdFirst
> 0)
1083 InsertMenu(hMenu
, indexMenu
++, MF_SEPARATOR
|MF_BYPOSITION
, 0, NULL
); idCmd
++;
1087 //return number of menu items added
1088 return ResultFromScode(MAKE_SCODE(SEVERITY_SUCCESS
, 0, (USHORT
)(idCmd
- idCmdFirst
)));
1091 //add sub menu to main context menu
1092 //don't use InsertMenu because this will lead to multiple menu entries in the explorer file menu.
1093 //see http://support.microsoft.com/default.aspx?scid=kb;en-us;214477 for details of that.
1094 MAKESTRING(IDS_MENUSUBMENU
);
1095 if (!g_ShellCache
.HasShellMenuAccelerators())
1097 // remove the accelerators
1098 tstring temp
= stringtablebuffer
;
1099 temp
.erase(std::remove(temp
.begin(), temp
.end(), '&'), temp
.end());
1100 _tcscpy_s(stringtablebuffer
, temp
.c_str());
1102 MENUITEMINFO menuiteminfo
;
1103 SecureZeroMemory(&menuiteminfo
, sizeof(menuiteminfo
));
1104 menuiteminfo
.cbSize
= sizeof(menuiteminfo
);
1105 menuiteminfo
.fType
= MFT_STRING
;
1106 menuiteminfo
.dwTypeData
= stringtablebuffer
;
1108 UINT uIcon
= bShowIcons
? IDI_APP
: 0;
1109 if (!folder_
.empty())
1111 uIcon
= bShowIcons
? IDI_MENUFOLDER
: 0;
1112 myIDMap
[idCmd
- idCmdFirst
] = ShellSubMenuFolder
;
1113 myIDMap
[idCmd
] = ShellSubMenuFolder
;
1114 menuiteminfo
.dwItemData
= (ULONG_PTR
)g_MenuIDString
;
1116 else if (!bShortcut
&& (files_
.size()==1))
1118 uIcon
= bShowIcons
? IDI_MENUFILE
: 0;
1119 myIDMap
[idCmd
- idCmdFirst
] = ShellSubMenuFile
;
1120 myIDMap
[idCmd
] = ShellSubMenuFile
;
1122 else if (bShortcut
&& (files_
.size()==1))
1124 uIcon
= bShowIcons
? IDI_MENULINK
: 0;
1125 myIDMap
[idCmd
- idCmdFirst
] = ShellSubMenuLink
;
1126 myIDMap
[idCmd
] = ShellSubMenuLink
;
1128 else if (!files_
.empty())
1130 uIcon
= bShowIcons
? IDI_MENUMULTIPLE
: 0;
1131 myIDMap
[idCmd
- idCmdFirst
] = ShellSubMenuMultiple
;
1132 myIDMap
[idCmd
] = ShellSubMenuMultiple
;
1136 myIDMap
[idCmd
- idCmdFirst
] = ShellSubMenu
;
1137 myIDMap
[idCmd
] = ShellSubMenu
;
1139 menuiteminfo
.fMask
= MIIM_FTYPE
| MIIM_ID
| MIIM_SUBMENU
| MIIM_DATA
| MIIM_STRING
;
1142 menuiteminfo
.fMask
|= MIIM_BITMAP
;
1143 menuiteminfo
.hbmpItem
= SysInfo::Instance().IsVistaOrLater() ? m_iconBitmapUtils
.IconToBitmapPARGB32(g_hResInst
, uIcon
) : HBMMENU_CALLBACK
;
1145 menuiteminfo
.hSubMenu
= subMenu
;
1146 menuiteminfo
.wID
= idCmd
++;
1147 InsertMenuItem(hMenu
, indexMenu
++, TRUE
, &menuiteminfo
);
1150 InsertMenu(hMenu
, indexMenu
++, MF_SEPARATOR
|MF_BYPOSITION
, 0, NULL
); idCmd
++;
1154 //return number of menu items added
1155 return ResultFromScode(MAKE_SCODE(SEVERITY_SUCCESS
, 0, (USHORT
)(idCmd
- idCmdFirst
)));
1158 void CShellExt::TweakMenu(HMENU hMenu
)
1160 MENUINFO MenuInfo
= {};
1161 MenuInfo
.cbSize
= sizeof(MenuInfo
);
1162 MenuInfo
.fMask
= MIM_STYLE
| MIM_APPLYTOSUBMENUS
;
1163 MenuInfo
.dwStyle
= MNS_CHECKORBMP
;
1164 SetMenuInfo(hMenu
, &MenuInfo
);
1167 void CShellExt::AddPathCommand(tstring
& gitCmd
, LPCTSTR command
, bool bFilesAllowed
)
1170 gitCmd
+= _T(" /path:\"");
1171 if ((bFilesAllowed
) && !files_
.empty())
1172 gitCmd
+= files_
.front();
1178 void CShellExt::AddPathFileCommand(tstring
& gitCmd
, LPCTSTR command
)
1180 tstring tempfile
= WriteFileListToTempFile();
1182 gitCmd
+= _T(" /pathfile:\"");
1185 gitCmd
+= _T(" /deletepathfile");
1188 void CShellExt::AddPathFileDropCommand(tstring
& gitCmd
, LPCTSTR command
)
1190 tstring tempfile
= WriteFileListToTempFile();
1192 gitCmd
+= _T(" /pathfile:\"");
1195 gitCmd
+= _T(" /deletepathfile");
1196 gitCmd
+= _T(" /droptarget:\"");
1201 STDMETHODIMP
CShellExt::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi
)
1205 return InvokeCommand_Wrap(lpcmi
);
1207 __except(CCrashReport::Instance().SendReport(GetExceptionInformation()))
1213 // This is called when you invoke a command on the menu:
1214 STDMETHODIMP
CShellExt::InvokeCommand_Wrap(LPCMINVOKECOMMANDINFO lpcmi
)
1216 PreserveChdir preserveChdir
;
1217 HRESULT hr
= E_INVALIDARG
;
1221 if (!files_
.empty() || !folder_
.empty())
1223 UINT_PTR idCmd
= LOWORD(lpcmi
->lpVerb
);
1225 if (HIWORD(lpcmi
->lpVerb
))
1227 stdstring verb
= stdstring(MultibyteToWide(lpcmi
->lpVerb
));
1228 std::map
<stdstring
, UINT_PTR
>::const_iterator verb_it
= myVerbsMap
.lower_bound(verb
);
1229 if (verb_it
!= myVerbsMap
.end() && verb_it
->first
== verb
)
1230 idCmd
= verb_it
->second
;
1235 // See if we have a handler interface for this id
1236 std::map
<UINT_PTR
, UINT_PTR
>::const_iterator id_it
= myIDMap
.lower_bound(idCmd
);
1237 if (id_it
!= myIDMap
.end() && id_it
->first
== idCmd
)
1239 tstring tortoiseProcPath
= CPathUtils::GetAppDirectory(g_hmodThisDll
) + _T("TortoiseGitProc.exe");
1240 tstring tortoiseMergePath
= CPathUtils::GetAppDirectory(g_hmodThisDll
) + _T("TortoiseGitMerge.exe");
1242 //TortoiseGitProc expects a command line of the form:
1243 //"/command:<commandname> /pathfile:<path> /startrev:<startrevision> /endrev:<endrevision> /deletepathfile
1245 //"/command:<commandname> /path:<path> /startrev:<startrevision> /endrev:<endrevision>
1247 //* path is a path to a single file/directory for commands which only act on single items (log, checkout, ...)
1248 //* pathfile is a path to a temporary file which contains a list of file paths
1249 stdstring gitCmd
= _T(" /command:");
1251 switch (id_it
->second
)
1256 TCHAR syncSeq
[12] = { 0 };
1257 _stprintf_s(syncSeq
, _T("%d"), g_syncSeq
++);
1258 AddPathCommand(gitCmd
, L
"sync", false);
1259 gitCmd
+= _T(" /seq:");
1263 case ShellMenuSubSync
:
1264 AddPathFileCommand(gitCmd
, L
"subsync");
1265 if (itemStatesFolder
& ITEMIS_SUBMODULECONTAINER
|| (itemStates
& ITEMIS_SUBMODULECONTAINER
&& itemStates
& ITEMIS_WCROOT
&& itemStates
& ITEMIS_ONLYONE
))
1267 gitCmd
+= _T(" /bkpath:\"");
1272 case ShellMenuUpdateExt
:
1273 AddPathFileCommand(gitCmd
, L
"subupdate");
1274 if (itemStatesFolder
& ITEMIS_SUBMODULECONTAINER
|| (itemStates
& ITEMIS_SUBMODULECONTAINER
&& itemStates
& ITEMIS_WCROOT
&& itemStates
& ITEMIS_ONLYONE
))
1276 gitCmd
+= _T(" /bkpath:\"");
1281 case ShellMenuCommit
:
1282 AddPathFileCommand(gitCmd
, L
"commit");
1285 AddPathFileCommand(gitCmd
, L
"add");
1287 case ShellMenuIgnore
:
1288 AddPathFileCommand(gitCmd
, L
"ignore");
1290 case ShellMenuIgnoreCaseSensitive
:
1291 AddPathFileCommand(gitCmd
, L
"ignore");
1292 gitCmd
+= _T(" /onlymask");
1294 case ShellMenuDeleteIgnore
:
1295 AddPathFileCommand(gitCmd
, L
"ignore");
1296 gitCmd
+= _T(" /delete");
1298 case ShellMenuDeleteIgnoreCaseSensitive
:
1299 AddPathFileCommand(gitCmd
, L
"ignore");
1300 gitCmd
+= _T(" /delete /onlymask");
1302 case ShellMenuUnIgnore
:
1303 AddPathFileCommand(gitCmd
, L
"unignore");
1305 case ShellMenuUnIgnoreCaseSensitive
:
1306 AddPathFileCommand(gitCmd
, L
"unignore");
1307 gitCmd
+= _T(" /onlymask");
1309 case ShellMenuMergeAbort
:
1310 AddPathCommand(gitCmd
, L
"merge", false);
1311 gitCmd
+= _T(" /abort");
1313 case ShellMenuRevert
:
1314 AddPathFileCommand(gitCmd
, L
"revert");
1316 case ShellMenuCleanup
:
1317 AddPathFileCommand(gitCmd
, L
"cleanup");
1319 case ShellMenuSendMail
:
1320 AddPathFileCommand(gitCmd
, L
"sendmail");
1322 case ShellMenuResolve
:
1323 AddPathFileCommand(gitCmd
, L
"resolve");
1325 case ShellMenuSwitch
:
1326 AddPathCommand(gitCmd
, L
"switch", false);
1328 case ShellMenuExport
:
1329 AddPathCommand(gitCmd
, L
"export", false);
1331 case ShellMenuAbout
:
1332 gitCmd
+= _T("about");
1334 case ShellMenuCreateRepos
:
1335 AddPathCommand(gitCmd
, L
"repocreate", false);
1337 case ShellMenuMerge
:
1338 AddPathCommand(gitCmd
, L
"merge", false);
1341 AddPathCommand(gitCmd
, L
"copy", true);
1343 case ShellMenuSettings
:
1344 AddPathCommand(gitCmd
, L
"settings", true);
1347 gitCmd
+= _T("help");
1349 case ShellMenuRename
:
1350 AddPathCommand(gitCmd
, L
"rename", true);
1352 case ShellMenuRemove
:
1353 AddPathFileCommand(gitCmd
, L
"remove");
1355 case ShellMenuRemoveKeep
:
1356 AddPathFileCommand(gitCmd
, L
"remove");
1357 gitCmd
+= _T(" /keep");
1360 gitCmd
+= _T("diff /path:\"");
1361 if (files_
.size() == 1)
1362 gitCmd
+= files_
.front();
1363 else if (files_
.size() == 2)
1365 std::vector
<stdstring
>::iterator I
= files_
.begin();
1368 gitCmd
+= _T("\" /path2:\"");
1374 if (GetAsyncKeyState(VK_SHIFT
) & 0x8000)
1375 gitCmd
+= _T(" /alternative");
1377 case ShellMenuDiffLater
:
1378 if (lpcmi
->fMask
& CMIC_MASK_CONTROL_DOWN
)
1381 regDiffLater
.removeValue();
1383 else if (files_
.size() == 1)
1385 if (std::wstring(regDiffLater
).empty())
1388 regDiffLater
= files_
[0];
1392 AddPathCommand(gitCmd
, L
"diff", true);
1393 gitCmd
+= _T(" /path2:\"");
1394 gitCmd
+= std::wstring(regDiffLater
);
1396 if (GetAsyncKeyState(VK_SHIFT
) & 0x8000)
1397 gitCmd
+= _T(" /alternative");
1398 regDiffLater
.removeValue();
1406 case ShellMenuPrevDiff
:
1407 AddPathCommand(gitCmd
, L
"prevdiff", true);
1408 if (GetAsyncKeyState(VK_SHIFT
) & 0x8000)
1409 gitCmd
+= _T(" /alternative");
1411 case ShellMenuDiffTwo
:
1412 AddPathCommand(gitCmd
, L
"diffcommits", true);
1414 case ShellMenuDropCopyAdd
:
1415 AddPathFileDropCommand(gitCmd
, L
"dropcopyadd");
1417 case ShellMenuDropCopy
:
1418 AddPathFileDropCommand(gitCmd
, L
"dropcopy");
1420 case ShellMenuDropCopyRename
:
1421 AddPathFileDropCommand(gitCmd
, L
"dropcopy");
1422 gitCmd
+= _T("\" /rename";)
1424 case ShellMenuDropMove
:
1425 AddPathFileDropCommand(gitCmd
, L
"dropmove");
1427 case ShellMenuDropMoveRename
:
1428 AddPathFileDropCommand(gitCmd
, L
"dropmove");
1429 gitCmd
+= _T("\" /rename";)
1431 case ShellMenuDropExport
:
1432 AddPathFileDropCommand(gitCmd
, L
"dropexport");
1434 case ShellMenuDropExportExtended
:
1435 AddPathFileDropCommand(gitCmd
, L
"dropexport");
1436 gitCmd
+= _T(" /extended");
1439 case ShellMenuLogSubmoduleFolder
:
1440 AddPathCommand(gitCmd
, L
"log", true);
1441 if (id_it
->second
== ShellMenuLogSubmoduleFolder
)
1442 gitCmd
+= _T(" /submodule");
1444 case ShellMenuDaemon
:
1445 AddPathCommand(gitCmd
, L
"daemon", true);
1447 case ShellMenuRevisionGraph
:
1448 AddPathCommand(gitCmd
, L
"revisiongraph", true);
1450 case ShellMenuConflictEditor
:
1451 AddPathCommand(gitCmd
, L
"conflicteditor", true);
1453 case ShellMenuGitSVNRebase
:
1454 AddPathCommand(gitCmd
, L
"svnrebase", false);
1456 case ShellMenuGitSVNDCommit
:
1457 AddPathCommand(gitCmd
, L
"svndcommit", true);
1459 case ShellMenuGitSVNDFetch
:
1460 AddPathCommand(gitCmd
, L
"svnfetch", false);
1462 case ShellMenuGitSVNIgnore
:
1463 AddPathCommand(gitCmd
, L
"svnignore", false);
1465 case ShellMenuRebase
:
1466 AddPathCommand(gitCmd
, L
"rebase", false);
1468 case ShellMenuShowChanged
:
1469 if (files_
.size() > 1)
1471 AddPathFileCommand(gitCmd
, L
"repostatus");
1475 AddPathCommand(gitCmd
, L
"repostatus", true);
1478 case ShellMenuRepoBrowse
:
1479 AddPathCommand(gitCmd
, L
"repobrowser", false);
1481 case ShellMenuRefBrowse
:
1482 AddPathCommand(gitCmd
, L
"refbrowse", false);
1484 case ShellMenuRefLog
:
1485 AddPathCommand(gitCmd
, L
"reflog", false);
1487 case ShellMenuStashSave
:
1488 AddPathCommand(gitCmd
, L
"stashsave", true);
1490 case ShellMenuStashApply
:
1491 AddPathCommand(gitCmd
, L
"stashapply", false);
1493 case ShellMenuStashPop
:
1494 AddPathCommand(gitCmd
, L
"stashpop", false);
1496 case ShellMenuStashList
:
1497 AddPathCommand(gitCmd
, L
"reflog", false);
1498 gitCmd
+= _T(" /ref:refs/stash");
1500 case ShellMenuBisectStart
:
1501 AddPathCommand(gitCmd
, L
"bisect", false);
1502 gitCmd
+= _T("\" /start");
1504 case ShellMenuBisectGood
:
1505 AddPathCommand(gitCmd
, L
"bisect", false);
1506 gitCmd
+= _T("\" /good");
1508 case ShellMenuBisectBad
:
1509 AddPathCommand(gitCmd
, L
"bisect", false);
1510 gitCmd
+= _T("\" /bad");
1512 case ShellMenuBisectReset
:
1513 AddPathCommand(gitCmd
, L
"bisect", false);
1514 gitCmd
+= _T("\" /reset");
1516 case ShellMenuSubAdd
:
1517 AddPathCommand(gitCmd
, L
"subadd", false);
1519 case ShellMenuBlame
:
1520 AddPathCommand(gitCmd
, L
"blame", true);
1522 case ShellMenuApplyPatch
:
1523 if ((itemStates
& ITEMIS_PATCHINCLIPBOARD
) && ((~itemStates
) & ITEMIS_PATCHFILE
))
1525 // if there's a patch file in the clipboard, we save it
1526 // to a temporary file and tell TortoiseGitMerge to use that one
1527 UINT cFormat
= RegisterClipboardFormat(_T("TGIT_UNIFIEDDIFF"));
1528 if ((cFormat
)&&(OpenClipboard(NULL
)))
1530 HGLOBAL hglb
= GetClipboardData(cFormat
);
1531 LPCSTR lpstr
= (LPCSTR
)GlobalLock(hglb
);
1533 DWORD len
= GetTortoiseGitTempPath(0, NULL
);
1534 std::unique_ptr
<TCHAR
[]> path(new TCHAR
[len
+ 1]);
1535 std::unique_ptr
<TCHAR
[]> tempF(new TCHAR
[len
+ 100]);
1536 GetTortoiseGitTempPath(len
+ 1, path
.get());
1537 GetTempFileName(path
.get(), TEXT("git"), 0, tempF
.get());
1538 std::wstring sTempFile
= std::wstring(tempF
.get());
1541 size_t patchlen
= strlen(lpstr
);
1542 _tfopen_s(&outFile
, sTempFile
.c_str(), _T("wb"));
1545 size_t size
= fwrite(lpstr
, sizeof(char), patchlen
, outFile
);
1546 if (size
== patchlen
)
1548 itemStates
|= ITEMIS_PATCHFILE
;
1550 files_
.push_back(sTempFile
);
1558 if (itemStates
& ITEMIS_PATCHFILE
)
1560 gitCmd
= _T(" /diff:\"");
1561 if (!files_
.empty())
1563 gitCmd
+= files_
.front();
1564 if (itemStatesFolder
& ITEMIS_FOLDERINGIT
)
1566 gitCmd
+= _T("\" /patchpath:\"");
1572 if (itemStates
& ITEMIS_INVERSIONEDFOLDER
)
1573 gitCmd
+= _T("\" /wc");
1579 gitCmd
= _T(" /patchpath:\"");
1580 if (!files_
.empty())
1581 gitCmd
+= files_
.front();
1587 myVerbsIDMap
.clear();
1589 RunCommand(tortoiseMergePath
, gitCmd
, _T("TortoiseGitMerge launch failed"));
1592 case ShellMenuClipPaste
:
1593 if (WriteClipboardPathsToTempFile(tempfile
))
1596 UINT cPrefDropFormat
= RegisterClipboardFormat(_T("Preferred DropEffect"));
1597 if (cPrefDropFormat
)
1599 if (OpenClipboard(lpcmi
->hwnd
))
1601 HGLOBAL hglb
= GetClipboardData(cPrefDropFormat
);
1604 DWORD
* effect
= (DWORD
*) GlobalLock(hglb
);
1605 if (*effect
== DROPEFFECT_MOVE
)
1614 gitCmd
+= _T("pastecopy /pathfile:\"");
1616 gitCmd
+= _T("pastemove /pathfile:\"");
1619 gitCmd
+= _T(" /deletepathfile");
1620 gitCmd
+= _T(" /droptarget:\"");
1626 case ShellMenuClone
:
1627 AddPathCommand(gitCmd
, L
"clone", false);
1630 AddPathCommand(gitCmd
, L
"pull", false);
1633 AddPathCommand(gitCmd
, L
"push", false);
1635 case ShellMenuBranch
:
1636 AddPathCommand(gitCmd
, L
"branch", false);
1639 AddPathCommand(gitCmd
, L
"tag", false);
1641 case ShellMenuFormatPatch
:
1642 AddPathCommand(gitCmd
, L
"formatpatch", false);
1644 case ShellMenuImportPatch
:
1645 AddPathFileCommand(gitCmd
, L
"importpatch");
1647 case ShellMenuFetch
:
1648 AddPathCommand(gitCmd
, L
"fetch", false);
1654 } // switch (id_it->second)
1655 if (!gitCmd
.empty())
1657 gitCmd
+= _T(" /hwnd:");
1658 TCHAR buf
[30] = { 0 };
1659 _stprintf_s(buf
, _T("%p"), (void*)lpcmi
->hwnd
);
1662 myVerbsIDMap
.clear();
1664 RunCommand(tortoiseProcPath
, gitCmd
, _T("TortoiseProc launch failed"));
1667 } // if (id_it != myIDMap.end() && id_it->first == idCmd)
1668 } // if (files_.empty() || folder_.empty())
1673 // This is for the status bar and things like that:
1674 STDMETHODIMP
CShellExt::GetCommandString(UINT_PTR idCmd
,
1676 UINT FAR
* reserved
,
1682 return GetCommandString_Wrap(idCmd
, uFlags
, reserved
, pszName
, cchMax
);
1684 __except(CCrashReport::Instance().SendReport(GetExceptionInformation()))
1690 // This is for the status bar and things like that:
1691 STDMETHODIMP
CShellExt::GetCommandString_Wrap(UINT_PTR idCmd
,
1693 UINT FAR
* /*reserved*/,
1697 PreserveChdir preserveChdir
;
1698 //do we know the id?
1699 std::map
<UINT_PTR
, UINT_PTR
>::const_iterator id_it
= myIDMap
.lower_bound(idCmd
);
1700 if (id_it
== myIDMap
.end() || id_it
->first
!= idCmd
)
1702 return E_INVALIDARG
; //no, we don't
1706 HRESULT hr
= E_INVALIDARG
;
1708 MAKESTRING(IDS_MENUDESCDEFAULT
);
1710 while (menuInfo
[menuIndex
].command
!= ShellMenuLastEntry
)
1712 if (menuInfo
[menuIndex
].command
== (GitCommands
)id_it
->second
)
1714 MAKESTRING(menuInfo
[menuIndex
].menuDescID
);
1720 const TCHAR
* desc
= stringtablebuffer
;
1725 std::string help
= WideToMultibyte(desc
);
1726 lstrcpynA(pszName
, help
.c_str(), cchMax
- 1);
1732 wide_string help
= desc
;
1733 lstrcpynW((LPWSTR
)pszName
, help
.c_str(), cchMax
- 1);
1739 std::map
<UINT_PTR
, stdstring
>::const_iterator verb_id_it
= myVerbsIDMap
.lower_bound(idCmd
);
1740 if (verb_id_it
!= myVerbsIDMap
.end() && verb_id_it
->first
== idCmd
)
1742 std::string help
= WideToMultibyte(verb_id_it
->second
);
1743 lstrcpynA(pszName
, help
.c_str(), cchMax
- 1);
1750 std::map
<UINT_PTR
, stdstring
>::const_iterator verb_id_it
= myVerbsIDMap
.lower_bound(idCmd
);
1751 if (verb_id_it
!= myVerbsIDMap
.end() && verb_id_it
->first
== idCmd
)
1753 wide_string help
= verb_id_it
->second
;
1754 CTraceToOutputDebugString::Instance()(__FUNCTION__
": verb : %ws\n", help
.c_str());
1755 lstrcpynW((LPWSTR
)pszName
, help
.c_str(), cchMax
- 1);
1764 STDMETHODIMP
CShellExt::HandleMenuMsg(UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
1768 return HandleMenuMsg_Wrap(uMsg
, wParam
, lParam
);
1770 __except(CCrashReport::Instance().SendReport(GetExceptionInformation()))
1776 STDMETHODIMP
CShellExt::HandleMenuMsg_Wrap(UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
1779 return HandleMenuMsg2(uMsg
, wParam
, lParam
, &res
);
1782 STDMETHODIMP
CShellExt::HandleMenuMsg2(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, LRESULT
*pResult
)
1786 return HandleMenuMsg2_Wrap(uMsg
, wParam
, lParam
, pResult
);
1788 __except(CCrashReport::Instance().SendReport(GetExceptionInformation()))
1794 STDMETHODIMP
CShellExt::HandleMenuMsg2_Wrap(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, LRESULT
*pResult
)
1796 PreserveChdir preserveChdir
;
1799 if (pResult
== NULL
)
1806 case WM_MEASUREITEM
:
1808 MEASUREITEMSTRUCT
* lpmis
= (MEASUREITEMSTRUCT
*)lParam
;
1811 lpmis
->itemWidth
= 16;
1812 lpmis
->itemHeight
= 16;
1819 DRAWITEMSTRUCT
* lpdis
= (DRAWITEMSTRUCT
*)lParam
;
1820 if ((lpdis
==NULL
)||(lpdis
->CtlType
!= ODT_MENU
))
1821 return S_OK
; //not for a menu
1822 resource
= GetMenuTextFromResource((int)myIDMap
[lpdis
->itemID
]);
1823 if (resource
== NULL
)
1825 HICON hIcon
= (HICON
)LoadImage(g_hResInst
, resource
, IMAGE_ICON
, 16, 16, LR_DEFAULTCOLOR
);
1828 DrawIconEx(lpdis
->hDC
,
1830 lpdis
->rcItem
.top
+ (lpdis
->rcItem
.bottom
- lpdis
->rcItem
.top
- 16) / 2,
1832 0, NULL
, DI_NORMAL
);
1840 if (HIWORD(wParam
) != MF_POPUP
)
1842 int nChar
= LOWORD(wParam
);
1843 if (_istascii((wint_t)nChar
) && _istupper((wint_t)nChar
))
1844 nChar
= tolower(nChar
);
1845 // we have the char the user pressed, now search that char in all our
1847 std::vector
<UINT_PTR
> accmenus
;
1848 for (std::map
<UINT_PTR
, UINT_PTR
>::iterator It
= mySubMenuMap
.begin(); It
!= mySubMenuMap
.end(); ++It
)
1850 LPCTSTR resource
= GetMenuTextFromResource((int)mySubMenuMap
[It
->first
]);
1851 if (resource
== NULL
)
1853 szItem
= stringtablebuffer
;
1854 TCHAR
* amp
= _tcschr(szItem
, '&');
1858 int ampChar
= LOWORD(*amp
);
1859 if (_istascii((wint_t)ampChar
) && _istupper((wint_t)ampChar
))
1860 ampChar
= tolower(ampChar
);
1861 if (ampChar
== nChar
)
1863 // yep, we found a menu which has the pressed key
1864 // as an accelerator. Add that menu to the list to
1866 accmenus
.push_back(It
->first
);
1869 if (accmenus
.empty())
1871 // no menu with that accelerator key.
1872 *pResult
= MAKELONG(0, MNC_IGNORE
);
1875 if (accmenus
.size() == 1)
1877 // Only one menu with that accelerator key. We're lucky!
1878 // So just execute that menu entry.
1879 *pResult
= MAKELONG(accmenus
[0], MNC_EXECUTE
);
1882 if (accmenus
.size() > 1)
1884 // we have more than one menu item with this accelerator key!
1886 mif
.cbSize
= sizeof(MENUITEMINFO
);
1887 mif
.fMask
= MIIM_STATE
;
1888 for (std::vector
<UINT_PTR
>::iterator it
= accmenus
.begin(); it
!= accmenus
.end(); ++it
)
1890 GetMenuItemInfo((HMENU
)lParam
, (UINT
)*it
, TRUE
, &mif
);
1891 if (mif
.fState
== MFS_HILITE
)
1893 // this is the selected item, so select the next one
1895 if (it
== accmenus
.end())
1896 *pResult
= MAKELONG(accmenus
[0], MNC_SELECT
);
1898 *pResult
= MAKELONG(*it
, MNC_SELECT
);
1902 *pResult
= MAKELONG(accmenus
[0], MNC_SELECT
);
1913 LPCTSTR
CShellExt::GetMenuTextFromResource(int id
)
1915 TCHAR textbuf
[255] = { 0 };
1916 LPCTSTR resource
= NULL
;
1917 unsigned __int64 layout
= g_ShellCache
.GetMenuLayout();
1921 while (menuInfo
[menuIndex
].command
!= ShellMenuLastEntry
)
1923 if (menuInfo
[menuIndex
].command
== id
)
1925 MAKESTRING(menuInfo
[menuIndex
].menuTextID
);
1926 resource
= MAKEINTRESOURCE(menuInfo
[menuIndex
].iconID
);
1929 case ShellSubMenuMultiple
:
1930 case ShellSubMenuLink
:
1931 case ShellSubMenuFolder
:
1932 case ShellSubMenuFile
:
1937 space
= (layout
& menuInfo
[menuIndex
].menuID
) ? 0 : 6;
1938 if (layout
& menuInfo
[menuIndex
].menuID
)
1940 _tcscpy_s(textbuf
, 255, _T("Git "));
1941 _tcscat_s(textbuf
, 255, stringtablebuffer
);
1942 _tcscpy_s(stringtablebuffer
, 255, textbuf
);
1953 bool CShellExt::IsIllegalFolder(std::wstring folder
, int * cslidarray
)
1956 TCHAR buf
[MAX_PATH
] = {0}; //MAX_PATH ok, since SHGetSpecialFolderPath doesn't return the required buffer length!
1957 LPITEMIDLIST pidl
= NULL
;
1958 while (cslidarray
[i
])
1962 if (SHGetFolderLocation(NULL
, cslidarray
[i
-1], NULL
, 0, &pidl
)!=S_OK
)
1964 if (!SHGetPathFromIDList(pidl
, buf
))
1966 // not a file system path, definitely illegal for our use
1967 CoTaskMemFree(pidl
);
1970 CoTaskMemFree(pidl
);
1971 if (_tcslen(buf
)==0)
1973 if (_tcscmp(buf
, folder
.c_str())==0)
1979 bool CShellExt::InsertIgnoreSubmenus(UINT
&idCmd
, UINT idCmdFirst
, HMENU hMenu
, HMENU subMenu
, UINT
&indexMenu
, int &indexSubMenu
, unsigned __int64 topmenu
, bool bShowIcons
, UINT
/*uFlags*/)
1981 HMENU ignoresubmenu
= NULL
;
1982 int indexignoresub
= 0;
1983 bool bShowIgnoreMenu
= false;
1984 TCHAR maskbuf
[MAX_PATH
] = {0}; // MAX_PATH is ok, since this only holds a filename
1985 TCHAR ignorepath
[MAX_PATH
] = {0}; // MAX_PATH is ok, since this only holds a filename
1988 UINT icon
= bShowIcons
? IDI_IGNORE
: 0;
1990 std::vector
<stdstring
>::iterator I
= files_
.begin();
1991 if (_tcsrchr(I
->c_str(), '\\'))
1992 _tcscpy_s(ignorepath
, MAX_PATH
, _tcsrchr(I
->c_str(), '\\')+1);
1994 _tcscpy_s(ignorepath
, MAX_PATH
, I
->c_str());
1995 if ((itemStates
& ITEMIS_IGNORED
) && (!ignoredprops
.empty()))
1997 // check if the item name is ignored or the mask
1999 while ( (p
=ignoredprops
.find( ignorepath
,p
)) != -1 )
2001 if ( (p
==0 || ignoredprops
[p
-1]==TCHAR('\n'))
2002 && (p
+_tcslen(ignorepath
)==ignoredprops
.length() || ignoredprops
[p
+_tcslen(ignorepath
)+1]==TCHAR('\n')) )
2010 ignoresubmenu
= CreateMenu();
2011 InsertMenu(ignoresubmenu
, indexignoresub
++, MF_BYPOSITION
| MF_STRING
, idCmd
, ignorepath
);
2012 stdstring verb
= stdstring(ignorepath
);
2013 myVerbsMap
[verb
] = idCmd
- idCmdFirst
;
2014 myVerbsMap
[verb
] = idCmd
;
2015 myVerbsIDMap
[idCmd
- idCmdFirst
] = verb
;
2016 myVerbsIDMap
[idCmd
] = verb
;
2017 myIDMap
[idCmd
- idCmdFirst
] = ShellMenuUnIgnore
;
2018 myIDMap
[idCmd
++] = ShellMenuUnIgnore
;
2019 bShowIgnoreMenu
= true;
2021 _tcscpy_s(maskbuf
, MAX_PATH
, _T("*"));
2022 if (_tcsrchr(ignorepath
, '.'))
2024 _tcscat_s(maskbuf
, MAX_PATH
, _tcsrchr(ignorepath
, '.'));
2025 p
= ignoredprops
.find(maskbuf
);
2027 ((ignoredprops
.compare(maskbuf
)==0) || (ignoredprops
.find('\n', p
)==p
+_tcslen(maskbuf
)+1) || (ignoredprops
.rfind('\n', p
)==p
-1)))
2029 if (ignoresubmenu
==NULL
)
2030 ignoresubmenu
= CreateMenu();
2032 InsertMenu(ignoresubmenu
, indexignoresub
++, MF_BYPOSITION
| MF_STRING
, idCmd
, maskbuf
);
2033 stdstring verb
= stdstring(maskbuf
);
2034 myVerbsMap
[verb
] = idCmd
- idCmdFirst
;
2035 myVerbsMap
[verb
] = idCmd
;
2036 myVerbsIDMap
[idCmd
- idCmdFirst
] = verb
;
2037 myVerbsIDMap
[idCmd
] = verb
;
2038 myIDMap
[idCmd
- idCmdFirst
] = ShellMenuUnIgnoreCaseSensitive
;
2039 myIDMap
[idCmd
++] = ShellMenuUnIgnoreCaseSensitive
;
2040 bShowIgnoreMenu
= true;
2044 else if ((itemStates
& ITEMIS_IGNORED
) == 0)
2046 bShowIgnoreMenu
= true;
2047 ignoresubmenu
= CreateMenu();
2048 if (itemStates
& ITEMIS_ONLYONE
)
2050 if (itemStates
& ITEMIS_INGIT
)
2052 InsertMenu(ignoresubmenu
, indexignoresub
++, MF_BYPOSITION
| MF_STRING
, idCmd
, ignorepath
);
2053 myIDMap
[idCmd
- idCmdFirst
] = ShellMenuDeleteIgnore
;
2054 myIDMap
[idCmd
++] = ShellMenuDeleteIgnore
;
2056 _tcscpy_s(maskbuf
, MAX_PATH
, _T("*"));
2057 if (!(itemStates
& ITEMIS_FOLDER
) && _tcsrchr(ignorepath
, '.'))
2059 _tcscat_s(maskbuf
, MAX_PATH
, _tcsrchr(ignorepath
, '.'));
2060 InsertMenu(ignoresubmenu
, indexignoresub
++, MF_BYPOSITION
| MF_STRING
, idCmd
, maskbuf
);
2061 stdstring verb
= stdstring(maskbuf
);
2062 myVerbsMap
[verb
] = idCmd
- idCmdFirst
;
2063 myVerbsMap
[verb
] = idCmd
;
2064 myVerbsIDMap
[idCmd
- idCmdFirst
] = verb
;
2065 myVerbsIDMap
[idCmd
] = verb
;
2066 myIDMap
[idCmd
- idCmdFirst
] = ShellMenuDeleteIgnoreCaseSensitive
;
2067 myIDMap
[idCmd
++] = ShellMenuDeleteIgnoreCaseSensitive
;
2072 InsertMenu(ignoresubmenu
, indexignoresub
++, MF_BYPOSITION
| MF_STRING
, idCmd
, ignorepath
);
2073 myIDMap
[idCmd
- idCmdFirst
] = ShellMenuIgnore
;
2074 myIDMap
[idCmd
++] = ShellMenuIgnore
;
2076 _tcscpy_s(maskbuf
, MAX_PATH
, _T("*"));
2077 if (!(itemStates
& ITEMIS_FOLDER
) && _tcsrchr(ignorepath
, '.'))
2079 _tcscat_s(maskbuf
, MAX_PATH
, _tcsrchr(ignorepath
, '.'));
2080 InsertMenu(ignoresubmenu
, indexignoresub
++, MF_BYPOSITION
| MF_STRING
, idCmd
, maskbuf
);
2081 stdstring verb
= stdstring(maskbuf
);
2082 myVerbsMap
[verb
] = idCmd
- idCmdFirst
;
2083 myVerbsMap
[verb
] = idCmd
;
2084 myVerbsIDMap
[idCmd
- idCmdFirst
] = verb
;
2085 myVerbsIDMap
[idCmd
] = verb
;
2086 myIDMap
[idCmd
- idCmdFirst
] = ShellMenuIgnoreCaseSensitive
;
2087 myIDMap
[idCmd
++] = ShellMenuIgnoreCaseSensitive
;
2093 if (itemStates
& ITEMIS_INGIT
)
2095 MAKESTRING(IDS_MENUDELETEIGNOREMULTIPLE
);
2096 _stprintf_s(ignorepath
, MAX_PATH
, stringtablebuffer
, files_
.size());
2097 InsertMenu(ignoresubmenu
, indexignoresub
++, MF_BYPOSITION
| MF_STRING
, idCmd
, ignorepath
);
2098 stdstring verb
= stdstring(ignorepath
);
2099 myVerbsMap
[verb
] = idCmd
- idCmdFirst
;
2100 myVerbsMap
[verb
] = idCmd
;
2101 myVerbsIDMap
[idCmd
- idCmdFirst
] = verb
;
2102 myVerbsIDMap
[idCmd
] = verb
;
2103 myIDMap
[idCmd
- idCmdFirst
] = ShellMenuDeleteIgnore
;
2104 myIDMap
[idCmd
++] = ShellMenuDeleteIgnore
;
2106 MAKESTRING(IDS_MENUDELETEIGNOREMULTIPLEMASK
);
2107 _stprintf_s(ignorepath
, MAX_PATH
, stringtablebuffer
, files_
.size());
2108 InsertMenu(ignoresubmenu
, indexignoresub
++, MF_BYPOSITION
| MF_STRING
, idCmd
, ignorepath
);
2109 verb
= stdstring(ignorepath
);
2110 myVerbsMap
[verb
] = idCmd
- idCmdFirst
;
2111 myVerbsMap
[verb
] = idCmd
;
2112 myVerbsIDMap
[idCmd
- idCmdFirst
] = verb
;
2113 myVerbsIDMap
[idCmd
] = verb
;
2114 myIDMap
[idCmd
- idCmdFirst
] = ShellMenuDeleteIgnoreCaseSensitive
;
2115 myIDMap
[idCmd
++] = ShellMenuDeleteIgnoreCaseSensitive
;
2119 MAKESTRING(IDS_MENUIGNOREMULTIPLE
);
2120 _stprintf_s(ignorepath
, MAX_PATH
, stringtablebuffer
, files_
.size());
2121 InsertMenu(ignoresubmenu
, indexignoresub
++, MF_BYPOSITION
| MF_STRING
, idCmd
, ignorepath
);
2122 stdstring verb
= stdstring(ignorepath
);
2123 myVerbsMap
[verb
] = idCmd
- idCmdFirst
;
2124 myVerbsMap
[verb
] = idCmd
;
2125 myVerbsIDMap
[idCmd
- idCmdFirst
] = verb
;
2126 myVerbsIDMap
[idCmd
] = verb
;
2127 myIDMap
[idCmd
- idCmdFirst
] = ShellMenuIgnore
;
2128 myIDMap
[idCmd
++] = ShellMenuIgnore
;
2130 MAKESTRING(IDS_MENUIGNOREMULTIPLEMASK
);
2131 _stprintf_s(ignorepath
, MAX_PATH
, stringtablebuffer
, files_
.size());
2132 InsertMenu(ignoresubmenu
, indexignoresub
++, MF_BYPOSITION
| MF_STRING
, idCmd
, ignorepath
);
2133 verb
= stdstring(ignorepath
);
2134 myVerbsMap
[verb
] = idCmd
- idCmdFirst
;
2135 myVerbsMap
[verb
] = idCmd
;
2136 myVerbsIDMap
[idCmd
- idCmdFirst
] = verb
;
2137 myVerbsIDMap
[idCmd
] = verb
;
2138 myIDMap
[idCmd
- idCmdFirst
] = ShellMenuIgnoreCaseSensitive
;
2139 myIDMap
[idCmd
++] = ShellMenuIgnoreCaseSensitive
;
2144 if (bShowIgnoreMenu
)
2146 MENUITEMINFO menuiteminfo
;
2147 SecureZeroMemory(&menuiteminfo
, sizeof(menuiteminfo
));
2148 menuiteminfo
.cbSize
= sizeof(menuiteminfo
);
2149 menuiteminfo
.fMask
= MIIM_FTYPE
| MIIM_ID
| MIIM_SUBMENU
| MIIM_DATA
| MIIM_STRING
;
2152 menuiteminfo
.fMask
|= MIIM_BITMAP
;
2153 menuiteminfo
.hbmpItem
= SysInfo::Instance().IsVistaOrLater() ? m_iconBitmapUtils
.IconToBitmapPARGB32(g_hResInst
, icon
) : m_iconBitmapUtils
.IconToBitmap(g_hResInst
, icon
);
2155 menuiteminfo
.fType
= MFT_STRING
;
2156 menuiteminfo
.hSubMenu
= ignoresubmenu
;
2157 menuiteminfo
.wID
= idCmd
;
2158 SecureZeroMemory(stringtablebuffer
, sizeof(stringtablebuffer
));
2159 if (itemStates
& ITEMIS_IGNORED
)
2160 GetMenuTextFromResource(ShellMenuUnIgnoreSub
);
2161 else if (itemStates
& ITEMIS_INGIT
)
2162 GetMenuTextFromResource(ShellMenuDeleteIgnoreSub
);
2164 GetMenuTextFromResource(ShellMenuIgnoreSub
);
2165 menuiteminfo
.dwTypeData
= stringtablebuffer
;
2166 menuiteminfo
.cch
= (UINT
)min(_tcslen(menuiteminfo
.dwTypeData
), UINT_MAX
);
2168 InsertMenuItem((topmenu
& MENUIGNORE
) ? hMenu
: subMenu
, (topmenu
& MENUIGNORE
) ? indexMenu
++ : indexSubMenu
++, TRUE
, &menuiteminfo
);
2169 if (itemStates
& ITEMIS_IGNORED
)
2171 myIDMap
[idCmd
- idCmdFirst
] = ShellMenuUnIgnoreSub
;
2172 myIDMap
[idCmd
++] = ShellMenuUnIgnoreSub
;
2176 myIDMap
[idCmd
- idCmdFirst
] = ShellMenuIgnoreSub
;
2177 myIDMap
[idCmd
++] = ShellMenuIgnoreSub
;
2180 return bShowIgnoreMenu
;
2183 void CShellExt::RunCommand(const tstring
& path
, const tstring
& command
, LPCTSTR errorMessage
)
2185 if (CCreateProcessHelper::CreateProcessDetached(path
.c_str(), const_cast<TCHAR
*>(command
.c_str())))
2187 // process started - exit
2191 MessageBox(NULL
, CFormatMessageWrapper(), errorMessage
, MB_OK
| MB_ICONINFORMATION
);
2194 bool CShellExt::ShouldInsertItem(const MenuInfo
& item
) const
2196 return ShouldEnableMenu(item
.first
) || ShouldEnableMenu(item
.second
) ||
2197 ShouldEnableMenu(item
.third
) || ShouldEnableMenu(item
.fourth
);
2200 bool CShellExt::ShouldEnableMenu(const YesNoPair
& pair
) const
2202 if (pair
.yes
&& pair
.no
)
2204 if (((pair
.yes
& itemStates
) == pair
.yes
) && ((pair
.no
& (~itemStates
)) == pair
.no
))
2207 else if ((pair
.yes
) && ((pair
.yes
& itemStates
) == pair
.yes
))
2209 else if ((pair
.no
) && ((pair
.no
& (~itemStates
)) == pair
.no
))