1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2003-2011 - TortoiseSVN
4 // Copyright (C) 2008-2011 - TortoiseGit
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software Foundation,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include "ItemIDList.h"
23 #include "PreserveChdir.h"
24 #include "UnicodeUtils.h"
25 //#include "GitProperties.h"
26 #include "GitStatus.h"
29 #define GetPIDLFolder(pida) (LPCITEMIDLIST)(((LPBYTE)pida)+(pida)->aoffset[0])
30 #define GetPIDLItem(pida, i) (LPCITEMIDLIST)(((LPBYTE)pida)+(pida)->aoffset[i+1])
32 int g_shellidlist
=RegisterClipboardFormat(CFSTR_SHELLIDLIST
);
34 extern MenuInfo menuInfo
[];
37 STDMETHODIMP
CShellExt::Initialize(LPCITEMIDLIST pIDFolder
,
38 LPDATAOBJECT pDataObj
,
42 ATLTRACE("Shell :: Initialize\n");
43 PreserveChdir preserveChdir
;
51 git_wc_status_kind fetchedstatus
= git_wc_status_none
;
52 // get selected files/folders
56 FORMATETC fmte
= {(CLIPFORMAT
)g_shellidlist
,
57 (DVTARGETDEVICE FAR
*)NULL
,
61 HRESULT hres
= pDataObj
->GetData(&fmte
, &medium
);
63 if (SUCCEEDED(hres
) && medium
.hGlobal
)
65 if (m_State
== FileStateDropHandler
)
68 FORMATETC etc
= { CF_HDROP
, NULL
, DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
};
69 STGMEDIUM stg
= { TYMED_HGLOBAL
};
70 if ( FAILED( pDataObj
->GetData ( &etc
, &stg
)))
72 ReleaseStgMedium ( &medium
);
77 HDROP drop
= (HDROP
)GlobalLock(stg
.hGlobal
);
80 ReleaseStgMedium ( &stg
);
81 ReleaseStgMedium ( &medium
);
85 int count
= DragQueryFile(drop
, (UINT
)-1, NULL
, 0);
87 itemStates
|= ITEMIS_ONLYONE
;
88 for (int i
= 0; i
< count
; i
++)
90 // find the path length in chars
91 UINT len
= DragQueryFile(drop
, i
, NULL
, 0);
94 TCHAR
* szFileName
= new TCHAR
[len
+1];
95 if (0 == DragQueryFile(drop
, i
, szFileName
, len
+1))
100 stdstring str
= stdstring(szFileName
);
101 delete [] szFileName
;
102 if ((str
.empty() == false)&&(g_ShellCache
.IsContextPathAllowed(szFileName
)))
104 if (itemStates
& ITEMIS_ONLYONE
)
107 strpath
.SetFromWin(str
.c_str());
108 itemStates
|= (strpath
.GetFileExtension().CompareNoCase(_T(".diff"))==0) ? ITEMIS_PATCHFILE
: 0;
109 itemStates
|= (strpath
.GetFileExtension().CompareNoCase(_T(".patch"))==0) ? ITEMIS_PATCHFILE
: 0;
111 files_
.push_back(str
);
114 //get the Subversion status of the item
115 git_wc_status_kind status
= git_wc_status_none
;
117 askedpath
.SetFromWin(str
.c_str());
121 stat
.GetStatus(CTGitPath(str
.c_str()), false, false, true);
125 status
= GitStatus::GetMoreImportant(stat
.status
->text_status
, stat
.status
->prop_status
);
126 fetchedstatus
= status
;
127 //if ((stat.status->entry)&&(stat.status->entry->lock_token))
128 // itemStates |= (stat.status->entry->lock_token[0] != 0) ? ITEMIS_LOCKED : 0;
129 if ( askedpath
.IsDirectory() )//if ((stat.status->entry)&&(stat.status->entry->kind == git_node_dir))
131 itemStates
|= ITEMIS_FOLDER
;
132 if ((status
!= git_wc_status_unversioned
)&&(status
!= git_wc_status_ignored
)&&(status
!= git_wc_status_none
))
133 itemStates
|= ITEMIS_FOLDERINSVN
;
135 //if ((stat.status->entry)&&(stat.status->entry->present_props))
137 // if (strstr(stat.status->entry->present_props, "svn:needs-lock"))
138 // itemStates |= ITEMIS_NEEDSLOCK;
140 //if ((stat.status->entry)&&(stat.status->entry->uuid))
141 // uuidSource = CUnicodeUtils::StdGetUnicode(stat.status->entry->uuid);
145 // sometimes, git_client_status() returns with an error.
146 // in that case, we have to check if the working copy is versioned
147 // anyway to show the 'correct' context menu
148 if (askedpath
.HasAdminDir())
149 status
= git_wc_status_normal
;
154 ATLTRACE2(_T("Exception in GitStatus::GetStatus()\n"));
157 // TODO: should we really assume any sub-directory to be versioned
158 // or only if it contains versioned files
159 itemStates
|= askedpath
.GetAdminDirMask();
161 if ((status
== git_wc_status_unversioned
) || (status
== git_wc_status_ignored
) || (status
== git_wc_status_none
))
162 itemStates
&= ~ITEMIS_INSVN
;
164 if (status
== git_wc_status_ignored
)
165 itemStates
|= ITEMIS_IGNORED
;
166 if (status
== git_wc_status_normal
)
167 itemStates
|= ITEMIS_NORMAL
;
168 if (status
== git_wc_status_conflicted
)
169 itemStates
|= ITEMIS_CONFLICTED
;
170 if (status
== git_wc_status_added
)
171 itemStates
|= ITEMIS_ADDED
;
172 if (status
== git_wc_status_deleted
)
173 itemStates
|= ITEMIS_DELETED
;
176 } // for (int i = 0; i < count; i++)
177 GlobalUnlock ( drop
);
178 ReleaseStgMedium ( &stg
);
180 } // if (m_State == FileStateDropHandler)
184 //Enumerate PIDLs which the user has selected
185 CIDA
* cida
= (CIDA
*)GlobalLock(medium
.hGlobal
);
186 ItemIDList
parent( GetPIDLFolder (cida
));
188 int count
= cida
->cidl
;
189 BOOL statfetched
= FALSE
;
190 for (int i
= 0; i
< count
; ++i
)
192 ItemIDList
child (GetPIDLItem (cida
, i
), &parent
);
193 stdstring str
= child
.toString();
194 if ((str
.empty() == false)&&(g_ShellCache
.IsContextPathAllowed(str
.c_str())))
196 //check if our menu is requested for a subversion admin directory
197 if (g_GitAdminDir
.IsAdminDirPath(str
.c_str()))
200 files_
.push_back(str
);
202 strpath
.SetFromWin(str
.c_str());
203 itemStates
|= (strpath
.GetFileExtension().CompareNoCase(_T(".diff"))==0) ? ITEMIS_PATCHFILE
: 0;
204 itemStates
|= (strpath
.GetFileExtension().CompareNoCase(_T(".patch"))==0) ? ITEMIS_PATCHFILE
: 0;
207 //get the Subversion status of the item
208 git_wc_status_kind status
= git_wc_status_none
;
209 if ((g_ShellCache
.IsSimpleContext())&&(strpath
.IsDirectory()))
211 if (strpath
.HasAdminDir())
212 status
= git_wc_status_normal
;
219 if (strpath
.HasAdminDir())
220 stat
.GetStatus(strpath
, false, false, true);
224 status
= GitStatus::GetMoreImportant(stat
.status
->text_status
, stat
.status
->prop_status
);
225 fetchedstatus
= status
;
226 //if ((stat.status->entry)&&(stat.status->entry->lock_token))
227 // itemStates |= (stat.status->entry->lock_token[0] != 0) ? ITEMIS_LOCKED : 0;
228 if ( strpath
.IsDirectory() )//if ((stat.status->entry)&&(stat.status->entry->kind == git_node_dir))
230 itemStates
|= ITEMIS_FOLDER
;
231 if ((status
!= git_wc_status_unversioned
)&&(status
!= git_wc_status_ignored
)&&(status
!= git_wc_status_none
))
232 itemStates
|= ITEMIS_FOLDERINSVN
;
234 // TODO: do we need to check that it's not a dir? does conflict options makes sense for dir in git?
235 if (status
== git_wc_status_conflicted
)//if ((stat.status->entry)&&(stat.status->entry->conflict_wrk))
236 itemStates
|= ITEMIS_CONFLICTED
;
237 //if ((stat.status->entry)&&(stat.status->entry->present_props))
239 // if (strstr(stat.status->entry->present_props, "svn:needs-lock"))
240 // itemStates |= ITEMIS_NEEDSLOCK;
242 //if ((stat.status->entry)&&(stat.status->entry->uuid))
243 // uuidSource = CUnicodeUtils::StdGetUnicode(stat.status->entry->uuid);
247 // sometimes, git_client_status() returns with an error.
248 // in that case, we have to check if the working copy is versioned
249 // anyway to show the 'correct' context menu
250 if (strpath
.HasAdminDir())
252 status
= git_wc_status_normal
;
253 fetchedstatus
= status
;
260 ATLTRACE2(_T("Exception in GitStatus::GetStatus()\n"));
264 itemStates
|= strpath
.GetAdminDirMask();
266 if ((status
== git_wc_status_unversioned
)||(status
== git_wc_status_ignored
)||(status
== git_wc_status_none
))
267 itemStates
&= ~ITEMIS_INSVN
;
268 if (status
== git_wc_status_ignored
)
270 itemStates
|= ITEMIS_IGNORED
;
271 // the item is ignored. Get the svn:ignored properties so we can (maybe) later
272 // offer a 'remove from ignored list' entry
273 // GitProperties props(strpath.GetContainingDirectory(), false);
274 // ignoredprops.empty();
275 // for (int p=0; p<props.GetCount(); ++p)
277 // if (props.GetItemName(p).compare(stdstring(_T("svn:ignore")))==0)
279 // std::string st = props.GetItemValue(p);
280 // ignoredprops = MultibyteToWide(st.c_str());
281 // // remove all escape chars ('\\')
282 // std::remove(ignoredprops.begin(), ignoredprops.end(), '\\');
288 if (status
== git_wc_status_normal
)
289 itemStates
|= ITEMIS_NORMAL
;
290 if (status
== git_wc_status_conflicted
)
291 itemStates
|= ITEMIS_CONFLICTED
;
292 if (status
== git_wc_status_added
)
293 itemStates
|= ITEMIS_ADDED
;
294 if (status
== git_wc_status_deleted
)
295 itemStates
|= ITEMIS_DELETED
;
298 } // for (int i = 0; i < count; ++i)
299 ItemIDList
child (GetPIDLItem (cida
, 0), &parent
);
300 if (g_ShellCache
.HasSVNAdminDir(child
.toString().c_str(), FALSE
))
301 itemStates
|= ITEMIS_INVERSIONEDFOLDER
;
302 GlobalUnlock(medium
.hGlobal
);
304 // if the item is a versioned folder, check if there's a patch file
305 // in the clipboard to be used in "Apply Patch"
306 UINT cFormatDiff
= RegisterClipboardFormat(_T("TGIT_UNIFIEDDIFF"));
309 if (IsClipboardFormatAvailable(cFormatDiff
))
310 itemStates
|= ITEMIS_PATCHINCLIPBOARD
;
312 if (IsClipboardFormatAvailable(CF_HDROP
))
313 itemStates
|= ITEMIS_PATHINCLIPBOARD
;
317 ReleaseStgMedium ( &medium
);
318 if (medium
.pUnkForRelease
)
320 IUnknown
* relInterface
= (IUnknown
*)medium
.pUnkForRelease
;
321 relInterface
->Release();
326 // get folder background
330 ItemIDList
list(pIDFolder
);
331 folder_
= list
.toString();
332 git_wc_status_kind status
= git_wc_status_none
;
333 if (IsClipboardFormatAvailable(CF_HDROP
))
334 itemStatesFolder
|= ITEMIS_PATHINCLIPBOARD
;
337 askedpath
.SetFromWin(folder_
.c_str());
339 if (g_ShellCache
.IsContextPathAllowed(folder_
.c_str()))
341 if (folder_
.compare(statuspath
)!=0)
347 stat
.GetStatus(CTGitPath(folder_
.c_str()), false, false, true);
350 status
= GitStatus::GetMoreImportant(stat
.status
->text_status
, stat
.status
->prop_status
);
351 // if ((stat.status->entry)&&(stat.status->entry->lock_token))
352 // itemStatesFolder |= (stat.status->entry->lock_token[0] != 0) ? ITEMIS_LOCKED : 0;
353 // if ((stat.status->entry)&&(stat.status->entry->present_props))
355 // if (strstr(stat.status->entry->present_props, "svn:needs-lock"))
356 // itemStatesFolder |= ITEMIS_NEEDSLOCK;
358 // if ((stat.status->entry)&&(stat.status->entry->uuid))
359 // uuidTarget = CUnicodeUtils::StdGetUnicode(stat.status->entry->uuid);
364 // sometimes, git_client_status() returns with an error.
365 // in that case, we have to check if the working copy is versioned
366 // anyway to show the 'correct' context menu
367 if (askedpath
.HasAdminDir())
368 status
= git_wc_status_normal
;
371 //if ((status != git_wc_status_unversioned)&&(status != git_wc_status_ignored)&&(status != git_wc_status_none))
372 itemStatesFolder
|= askedpath
.GetAdminDirMask();
374 if ((status
== git_wc_status_unversioned
)||(status
== git_wc_status_ignored
)||(status
== git_wc_status_none
))
375 itemStates
&= ~ITEMIS_INSVN
;
377 if (status
== git_wc_status_normal
)
378 itemStatesFolder
|= ITEMIS_NORMAL
;
379 if (status
== git_wc_status_conflicted
)
380 itemStatesFolder
|= ITEMIS_CONFLICTED
;
381 if (status
== git_wc_status_added
)
382 itemStatesFolder
|= ITEMIS_ADDED
;
383 if (status
== git_wc_status_deleted
)
384 itemStatesFolder
|= ITEMIS_DELETED
;
389 ATLTRACE2(_T("Exception in GitStatus::GetStatus()\n"));
394 status
= fetchedstatus
;
396 //if ((status != git_wc_status_unversioned)&&(status != git_wc_status_ignored)&&(status != git_wc_status_none))
397 itemStatesFolder
|= askedpath
.GetAdminDirMask();
399 if (status
== git_wc_status_ignored
)
400 itemStatesFolder
|= ITEMIS_IGNORED
;
401 itemStatesFolder
|= ITEMIS_FOLDER
;
402 if (files_
.size() == 0)
403 itemStates
|= ITEMIS_ONLYONE
;
404 if (m_State
!= FileStateDropHandler
)
405 itemStates
|= itemStatesFolder
;
410 status
= fetchedstatus
;
413 if (files_
.size() == 2)
414 itemStates
|= ITEMIS_TWO
;
415 if ((files_
.size() == 1)&&(g_ShellCache
.IsContextPathAllowed(files_
.front().c_str())))
418 itemStates
|= ITEMIS_ONLYONE
;
419 if (m_State
!= FileStateDropHandler
)
421 if (PathIsDirectory(files_
.front().c_str()))
423 folder_
= files_
.front();
424 git_wc_status_kind status
= git_wc_status_none
;
426 askedpath
.SetFromWin(folder_
.c_str());
428 if (folder_
.compare(statuspath
)!=0)
433 stat
.GetStatus(CTGitPath(folder_
.c_str()), false, false, true);
436 status
= GitStatus::GetMoreImportant(stat
.status
->text_status
, stat
.status
->prop_status
);
437 // if ((stat.status->entry)&&(stat.status->entry->lock_token))
438 // itemStates |= (stat.status->entry->lock_token[0] != 0) ? ITEMIS_LOCKED : 0;
439 // if ((stat.status->entry)&&(stat.status->entry->present_props))
441 // if (strstr(stat.status->entry->present_props, "svn:needs-lock"))
442 // itemStates |= ITEMIS_NEEDSLOCK;
444 // if ((stat.status->entry)&&(stat.status->entry->uuid))
445 // uuidTarget = CUnicodeUtils::StdGetUnicode(stat.status->entry->uuid);
450 ATLTRACE2(_T("Exception in GitStatus::GetStatus()\n"));
455 status
= fetchedstatus
;
457 //if ((status != git_wc_status_unversioned)&&(status != git_wc_status_ignored)&&(status != git_wc_status_none))
458 itemStates
|= askedpath
.GetAdminDirMask();
460 if ((status
== git_wc_status_unversioned
)||(status
== git_wc_status_ignored
)||(status
== git_wc_status_none
))
461 itemStates
&= ~ITEMIS_INSVN
;
463 if (status
== git_wc_status_ignored
)
464 itemStates
|= ITEMIS_IGNORED
;
465 itemStates
|= ITEMIS_FOLDER
;
466 if (status
== git_wc_status_added
)
467 itemStates
|= ITEMIS_ADDED
;
468 if (status
== git_wc_status_deleted
)
469 itemStates
|= ITEMIS_DELETED
;
479 void CShellExt::InsertGitMenu(BOOL istop
, HMENU menu
, UINT pos
, UINT_PTR id
, UINT stringid
, UINT icon
, UINT idCmdFirst
, GitCommands com
, UINT uFlags
)
481 TCHAR menutextbuffer
[512] = {0};
482 TCHAR verbsbuffer
[255] = {0};
483 MAKESTRING(stringid
);
487 //menu entry for the top context menu, so append an "Git " before
488 //the menu text to indicate where the entry comes from
489 _tcscpy_s(menutextbuffer
, 255, _T("Git "));
491 _tcscat_s(menutextbuffer
, 255, stringtablebuffer
);
493 // insert branch name into "Git Commit..." entry, so it looks like "Git Commit "master"..."
494 // so we have an easy and fast way to check the current branch
495 // (the other alternative is using a separate disabled menu entry, the code is already done but commented out)
496 if (com
== ShellMenuCommit
)
499 CTGitPath
path(folder_
.empty() ? files_
.front().c_str() : folder_
.c_str());
500 CString sProjectRoot
;
503 if (path
.HasAdminDir(&sProjectRoot
) && !g_Git
.GetCurrentBranchFromFile(sProjectRoot
, sBranchName
))
505 if (sBranchName
.GetLength() == 40)
507 // if SHA1 only show 4 first bytes
509 for (int i
=0; i
<40; i
++)
510 if ( !iswxdigit(sBranchName
[i
]) )
516 sBranchName
= sBranchName
.Left(8) + _T("....");
520 if (sBranchName
.GetLength() > 64)
521 sBranchName
= sBranchName
.Left(64) + _T("...");
523 // scan to before "..."
524 LPTSTR s
= menutextbuffer
+ _tcslen(menutextbuffer
)-1;
525 if (s
> menutextbuffer
)
527 while (s
> menutextbuffer
)
542 // append branch name and end with ...
543 _tcscpy(s
, _T(" -> \"") + sBranchName
+ _T("\"..."));
547 if ((fullver
< 0x500)||(fullver
== 0x500 && !(uFlags
&~(CMF_RESERVED
|CMF_EXPLORE
|CMF_EXTENDEDVERBS
))))
549 // on win2k, the context menu does not work properly if we use
550 // icon bitmaps. At least the menu text is empty in the context menu
551 // for folder backgrounds (seems like a win2k bug).
552 // the workaround is to use the check/unchecked bitmaps, which are drawn
553 // with AND raster op, but it's better than nothing at all
554 InsertMenu(menu
, pos
, MF_BYPOSITION
| MF_STRING
, id
, menutextbuffer
);
557 HBITMAP bmp
= IconToBitmap(icon
);
558 SetMenuItemBitmaps(menu
, pos
, MF_BYPOSITION
, bmp
, bmp
);
563 MENUITEMINFO menuiteminfo
;
564 SecureZeroMemory(&menuiteminfo
, sizeof(menuiteminfo
));
565 menuiteminfo
.cbSize
= sizeof(menuiteminfo
);
566 menuiteminfo
.fMask
= MIIM_FTYPE
| MIIM_ID
| MIIM_STRING
;
567 menuiteminfo
.fType
= MFT_STRING
;
568 menuiteminfo
.dwTypeData
= menutextbuffer
;
571 menuiteminfo
.fMask
|= MIIM_BITMAP
;
572 menuiteminfo
.hbmpItem
= (fullver
>= 0x600) ? IconToBitmapPARGB32(icon
) : HBMMENU_CALLBACK
;
574 menuiteminfo
.wID
= id
;
575 InsertMenuItem(menu
, pos
, TRUE
, &menuiteminfo
);
580 //menu entry for the top context menu, so append an "Git " before
581 //the menu text to indicate where the entry comes from
582 _tcscpy_s(menutextbuffer
, 255, _T("Git "));
584 LoadString(g_hResInst
, stringid
, verbsbuffer
, sizeof(verbsbuffer
));
585 _tcscat_s(menutextbuffer
, 255, verbsbuffer
);
586 stdstring verb
= stdstring(menutextbuffer
);
587 if (verb
.find('&') != -1)
589 verb
.erase(verb
.find('&'),1);
591 myVerbsMap
[verb
] = id
- idCmdFirst
;
592 myVerbsMap
[verb
] = id
;
593 myVerbsIDMap
[id
- idCmdFirst
] = verb
;
594 myVerbsIDMap
[id
] = verb
;
595 // We store the relative and absolute diameter
596 // (drawitem callback uses absolute, others relative)
597 myIDMap
[id
- idCmdFirst
] = com
;
600 mySubMenuMap
[pos
] = com
;
603 HBITMAP
CShellExt::IconToBitmap(UINT uIcon
)
605 std::map
<UINT
, HBITMAP
>::iterator bitmap_it
= bitmaps
.lower_bound(uIcon
);
606 if (bitmap_it
!= bitmaps
.end() && bitmap_it
->first
== uIcon
)
607 return bitmap_it
->second
;
609 HICON hIcon
= (HICON
)LoadImage(g_hResInst
, MAKEINTRESOURCE(uIcon
), IMAGE_ICON
, 12, 12, LR_DEFAULTCOLOR
);
615 rect
.right
= ::GetSystemMetrics(SM_CXMENUCHECK
);
616 rect
.bottom
= ::GetSystemMetrics(SM_CYMENUCHECK
);
618 rect
.left
= rect
.top
= 0;
620 HWND desktop
= ::GetDesktopWindow();
627 HDC screen_dev
= ::GetDC(desktop
);
628 if (screen_dev
== NULL
)
634 // Create a compatible DC
635 HDC dst_hdc
= ::CreateCompatibleDC(screen_dev
);
639 ::ReleaseDC(desktop
, screen_dev
);
643 // Create a new bitmap of icon size
644 HBITMAP bmp
= ::CreateCompatibleBitmap(screen_dev
, rect
.right
, rect
.bottom
);
649 ::ReleaseDC(desktop
, screen_dev
);
653 // Select it into the compatible DC
654 HBITMAP old_dst_bmp
= (HBITMAP
)::SelectObject(dst_hdc
, bmp
);
655 if (old_dst_bmp
== NULL
)
661 // Fill the background of the compatible DC with the white color
662 // that is taken by menu routines as transparent
663 ::SetBkColor(dst_hdc
, RGB(255, 255, 255));
664 ::ExtTextOut(dst_hdc
, 0, 0, ETO_OPAQUE
, &rect
, NULL
, 0, NULL
);
666 // Draw the icon into the compatible DC
667 ::DrawIconEx(dst_hdc
, 0, 0, hIcon
, rect
.right
, rect
.bottom
, 0, NULL
, DI_NORMAL
);
670 ::SelectObject(dst_hdc
, old_dst_bmp
);
672 ::ReleaseDC(desktop
, screen_dev
);
675 bitmaps
.insert(bitmap_it
, std::make_pair(uIcon
, bmp
));
679 bool CShellExt::WriteClipboardPathsToTempFile(stdstring
& tempfile
)
682 tempfile
= stdstring();
683 //write all selected files and paths to a temporary file
684 //for TortoiseProc.exe to read out again.
686 DWORD pathlength
= GetTempPath(0, NULL
);
687 TCHAR
* path
= new TCHAR
[pathlength
+1];
688 TCHAR
* tempFile
= new TCHAR
[pathlength
+ 100];
689 GetTempPath (pathlength
+1, path
);
690 GetTempFileName (path
, _T("git"), 0, tempFile
);
691 tempfile
= stdstring(tempFile
);
693 HANDLE file
= ::CreateFile (tempFile
,
698 FILE_ATTRIBUTE_TEMPORARY
,
703 if (file
== INVALID_HANDLE_VALUE
)
706 if (!IsClipboardFormatAvailable(CF_HDROP
))
708 if (!OpenClipboard(NULL
))
711 stdstring sClipboardText
;
712 HGLOBAL hglb
= GetClipboardData(CF_HDROP
);
713 HDROP hDrop
= (HDROP
)GlobalLock(hglb
);
716 TCHAR szFileName
[MAX_PATH
];
717 UINT cFiles
= DragQueryFile(hDrop
, 0xFFFFFFFF, NULL
, 0);
718 for(UINT i
= 0; i
< cFiles
; ++i
)
720 DragQueryFile(hDrop
, i
, szFileName
, sizeof(szFileName
));
721 stdstring filename
= szFileName
;
722 ::WriteFile (file
, filename
.c_str(), filename
.size()*sizeof(TCHAR
), &written
, 0);
723 ::WriteFile (file
, _T("\n"), 2, &written
, 0);
736 stdstring
CShellExt::WriteFileListToTempFile()
738 //write all selected files and paths to a temporary file
739 //for TortoiseProc.exe to read out again.
740 DWORD pathlength
= GetTempPath(0, NULL
);
741 TCHAR
* path
= new TCHAR
[pathlength
+1];
742 TCHAR
* tempFile
= new TCHAR
[pathlength
+ 100];
743 GetTempPath (pathlength
+1, path
);
744 GetTempFileName (path
, _T("git"), 0, tempFile
);
745 stdstring retFilePath
= stdstring(tempFile
);
747 HANDLE file
= ::CreateFile (tempFile
,
752 FILE_ATTRIBUTE_TEMPORARY
,
757 if (file
== INVALID_HANDLE_VALUE
)
763 ::WriteFile (file
, folder_
.c_str(), folder_
.size()*sizeof(TCHAR
), &written
, 0);
764 ::WriteFile (file
, _T("\n"), 2, &written
, 0);
767 for (std::vector
<stdstring
>::iterator I
= files_
.begin(); I
!= files_
.end(); ++I
)
769 ::WriteFile (file
, I
->c_str(), I
->size()*sizeof(TCHAR
), &written
, 0);
770 ::WriteFile (file
, _T("\n"), 2, &written
, 0);
776 STDMETHODIMP
CShellExt::QueryDropContext(UINT uFlags
, UINT idCmdFirst
, HMENU hMenu
, UINT
&indexMenu
)
778 PreserveChdir preserveChdir
;
781 if ((uFlags
& CMF_DEFAULTONLY
)!=0)
782 return NOERROR
; //we don't change the default action
784 if ((files_
.size() == 0)||(folder_
.size() == 0))
787 if (((uFlags
& 0x000f)!=CMF_NORMAL
)&&(!(uFlags
& CMF_EXPLORE
))&&(!(uFlags
& CMF_VERBSONLY
)))
790 bool bSourceAndTargetFromSameRepository
= (uuidSource
.compare(uuidTarget
) == 0) || uuidSource
.empty() || uuidTarget
.empty();
792 //the drop handler only has eight commands, but not all are visible at the same time:
793 //if the source file(s) are under version control then those files can be moved
794 //to the new location or they can be moved with a rename,
795 //if they are unversioned then they can be added to the working copy
796 //if they are versioned, they also can be exported to an unversioned location
797 UINT idCmd
= idCmdFirst
;
800 // available if source is versioned but not added, target is versioned, source and target from same repository or target folder is added
801 if ((bSourceAndTargetFromSameRepository
||(itemStatesFolder
& ITEMIS_ADDED
))&&(itemStatesFolder
& ITEMIS_FOLDERINSVN
)&&((itemStates
& ITEMIS_INSVN
)&&((~itemStates
) & ITEMIS_ADDED
)))
802 InsertGitMenu(FALSE
, hMenu
, indexMenu
++, idCmd
++, IDS_DROPMOVEMENU
, 0, idCmdFirst
, ShellMenuDropMove
, uFlags
);
804 // Git move and rename here
805 // 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
806 if ((bSourceAndTargetFromSameRepository
||(itemStatesFolder
& ITEMIS_ADDED
))&&(itemStatesFolder
& ITEMIS_FOLDERINSVN
)&&(itemStates
& ITEMIS_INSVN
)&&(itemStates
& ITEMIS_ONLYONE
)&&((~itemStates
) & ITEMIS_ADDED
))
807 InsertGitMenu(FALSE
, hMenu
, indexMenu
++, idCmd
++, IDS_DROPMOVERENAMEMENU
, 0, idCmdFirst
, ShellMenuDropMoveRename
, uFlags
);
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_FOLDERINSVN
)&&(itemStates
& ITEMIS_INSVN
)&&((~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_FOLDERINSVN
)&&(itemStates
& ITEMIS_INSVN
)&&(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_FOLDERINSVN
)&&(((~itemStates
) & ITEMIS_INSVN
)||!bSourceAndTargetFromSameRepository
))
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_INSVN
)&&(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_INSVN
)&&(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
);
843 return ResultFromScode(MAKE_SCODE(SEVERITY_SUCCESS
, 0, (USHORT
)(idCmd
- idCmdFirst
)));
846 STDMETHODIMP
CShellExt::QueryContextMenu(HMENU hMenu
,
852 ATLTRACE("Shell :: QueryContextMenu\n");
853 PreserveChdir preserveChdir
;
855 //first check if our drop handler is called
856 //and then (if true) provide the context menu for the
858 if (m_State
== FileStateDropHandler
)
860 return QueryDropContext(uFlags
, idCmdFirst
, hMenu
, indexMenu
);
863 if ((uFlags
& CMF_DEFAULTONLY
)!=0)
864 return NOERROR
; //we don't change the default action
866 if ((files_
.size() == 0)&&(folder_
.size() == 0))
869 if (((uFlags
& 0x000f)!=CMF_NORMAL
)&&(!(uFlags
& CMF_EXPLORE
))&&(!(uFlags
& CMF_VERBSONLY
)))
876 CSIDL_COMMON_FAVORITES
,
877 CSIDL_COMMON_STARTMENU
,
878 CSIDL_COMPUTERSNEARME
,
886 CSIDL_INTERNET_CACHE
,
896 if (IsIllegalFolder(folder_
, csidlarray
))
901 // folder is empty, but maybe files are selected
902 if (files_
.size() == 0)
903 return NOERROR
; // nothing selected - we don't have a menu to show
904 // check whether a selected entry is an UID - those are namespace extensions
905 // which we can't handle
906 for (std::vector
<stdstring
>::const_iterator it
= files_
.begin(); it
!= files_
.end(); ++it
)
908 if (_tcsncmp(it
->c_str(), _T("::{"), 3)==0)
913 if (((uFlags
& CMF_EXTENDEDVERBS
) == 0) && g_ShellCache
.HideMenusForUnversionedItems())
915 if ((itemStates
& (ITEMIS_INSVN
|ITEMIS_INVERSIONEDFOLDER
|ITEMIS_FOLDERINSVN
))==0)
919 //check if our menu is requested for a subversion admin directory
920 if (g_GitAdminDir
.IsAdminDirPath(folder_
.c_str()))
923 if (uFlags
& CMF_EXTENDEDVERBS
)
924 itemStates
|= ITEMIS_EXTENDED
;
926 const BOOL bShortcut
= !!(uFlags
& CMF_VERBSONLY
);
927 if ( bShortcut
&& (files_
.size()==1))
929 // Don't show the context menu for a link if the
930 // destination is not part of a working copy.
931 // It would only show the standard menu items
932 // which are already shown for the lnk-file.
933 CString path
= files_
.front().c_str();
934 if ( !g_GitAdminDir
.HasAdminDir(path
) )
940 //check if we already added our menu entry for a folder.
941 //we check that by iterating through all menu entries and check if
942 //the dwItemData member points to our global ID string. That string is set
943 //by our shell extension when the folder menu is inserted.
944 TCHAR menubuf
[MAX_PATH
];
945 int count
= GetMenuItemCount(hMenu
);
946 for (int i
=0; i
<count
; ++i
)
949 SecureZeroMemory(&miif
, sizeof(MENUITEMINFO
));
950 miif
.cbSize
= sizeof(MENUITEMINFO
);
951 miif
.fMask
= MIIM_DATA
;
952 miif
.dwTypeData
= menubuf
;
953 miif
.cch
= _countof(menubuf
);
954 GetMenuItemInfo(hMenu
, i
, TRUE
, &miif
);
955 if (miif
.dwItemData
== (ULONG_PTR
)g_MenuIDString
)
960 UINT idCmd
= idCmdFirst
;
962 //create the sub menu
963 HMENU subMenu
= CreateMenu();
964 int indexSubMenu
= 0;
966 unsigned __int64 topmenu
= g_ShellCache
.GetMenuLayout();
967 unsigned __int64 menumask
= g_ShellCache
.GetMenuMask();
968 unsigned __int64 menuex
= g_ShellCache
.GetMenuExt();
971 bool bAddSeparator
= false;
972 bool bMenuEntryAdded
= false;
973 bool bMenuEmpty
= true;
974 // insert separator at start
975 InsertMenu(hMenu
, indexMenu
++, MF_SEPARATOR
|MF_BYPOSITION
, 0, NULL
); idCmd
++;
976 bool bShowIcons
= !!DWORD(CRegStdDWORD(_T("Software\\TortoiseGit\\ShowContextMenuIcons"), TRUE
));
977 // ?? TSV disabled icons for win2k and earlier, but they work for win2k and should work for win95 and up
978 /*if (fullver <= 0x500)
979 bShowIcons = false;*/
982 if (itemStates
& (ITEMIS_INSVN
|ITEMIS_FOLDERINSVN
))
984 // show current branch name (as a "read-only" menu entry)
986 CTGitPath
path(folder_
.empty() ? files_
.front().c_str() : folder_
.c_str());
987 CString sProjectRoot
;
990 if (path
.HasAdminDir(&sProjectRoot
) && !g_Git
.GetCurrentBranchFromFile(sProjectRoot
, sBranchName
))
992 if (sBranchName
.GetLength() == 40)
994 // if SHA1 only show 4 first bytes
996 for (int i
=0; i
<40; i
++)
997 if ( !iswxdigit(sBranchName
[i
]) )
1003 sBranchName
= sBranchName
.Left(8) + _T("....");
1006 sBranchName
= _T('"') + sBranchName
+ _T('"');
1008 const int icon
= IDI_COPY
;
1009 const int pos
= indexMenu
++;
1010 const int id
= idCmd
++;
1012 if ((fullver
< 0x500)||(fullver
== 0x500 && !(uFlags
&~(CMF_RESERVED
|CMF_EXPLORE
|CMF_EXTENDEDVERBS
))))
1014 InsertMenu(hMenu
, pos
, MF_DISABLED
|MF_GRAYED
|MF_BYPOSITION
|MF_STRING
, id
, sBranchName
);
1015 HBITMAP bmp
= IconToBitmap(icon
);
1016 SetMenuItemBitmaps(hMenu
, pos
, MF_BYPOSITION
, bmp
, bmp
);
1020 MENUITEMINFO menuiteminfo
;
1021 SecureZeroMemory(&menuiteminfo
, sizeof(menuiteminfo
));
1022 menuiteminfo
.cbSize
= sizeof(menuiteminfo
);
1023 menuiteminfo
.fMask
= MIIM_FTYPE
| MIIM_ID
| MIIM_STRING
| MIIM_STATE
;
1024 menuiteminfo
.fState
= MFS_DISABLED
;
1025 menuiteminfo
.fType
= MFT_STRING
;
1026 menuiteminfo
.dwTypeData
= (LPWSTR
)sBranchName
.GetString();
1029 menuiteminfo
.fMask
|= MIIM_BITMAP
;
1030 menuiteminfo
.hbmpItem
= (fullver
>= 0x600) ? IconToBitmapPARGB32(icon
) : HBMMENU_CALLBACK
;
1032 if (menuiteminfo
.hbmpItem
== HBMMENU_CALLBACK
)
1034 // WM_DRAWITEM uses myIDMap to get icon, we use the same icon as create branch
1035 myIDMap
[id
- idCmdFirst
] = ShellMenuBranch
;
1036 myIDMap
[id
] = ShellMenuBranch
;
1039 menuiteminfo
.wID
= id
;
1040 InsertMenuItem(hMenu
, pos
, TRUE
, &menuiteminfo
);
1046 while (menuInfo
[menuIndex
].command
!= ShellMenuLastEntry
)
1048 if (menuInfo
[menuIndex
].command
== ShellSeparator
)
1050 // we don't add a separator immediately. Because there might not be
1051 // another 'normal' menu entry after we insert a separator.
1052 // we simply set a flag here, indicating that before the next
1053 // 'normal' menu entry, a separator should be added.
1055 bAddSeparator
= true;
1059 // check the conditions whether to show the menu entry or not
1060 bool bInsertMenu
= false;
1062 if (menuInfo
[menuIndex
].firstyes
&& menuInfo
[menuIndex
].firstno
)
1064 if (((menuInfo
[menuIndex
].firstyes
& itemStates
) == menuInfo
[menuIndex
].firstyes
)
1066 ((menuInfo
[menuIndex
].firstno
& (~itemStates
)) == menuInfo
[menuIndex
].firstno
))
1069 else if ((menuInfo
[menuIndex
].firstyes
)&&((menuInfo
[menuIndex
].firstyes
& itemStates
) == menuInfo
[menuIndex
].firstyes
))
1071 else if ((menuInfo
[menuIndex
].firstno
)&&((menuInfo
[menuIndex
].firstno
& (~itemStates
)) == menuInfo
[menuIndex
].firstno
))
1074 if (menuInfo
[menuIndex
].secondyes
&& menuInfo
[menuIndex
].secondno
)
1076 if (((menuInfo
[menuIndex
].secondyes
& itemStates
) == menuInfo
[menuIndex
].secondyes
)
1078 ((menuInfo
[menuIndex
].secondno
& (~itemStates
)) == menuInfo
[menuIndex
].secondno
))
1081 else if ((menuInfo
[menuIndex
].secondyes
)&&((menuInfo
[menuIndex
].secondyes
& itemStates
) == menuInfo
[menuIndex
].secondyes
))
1083 else if ((menuInfo
[menuIndex
].secondno
)&&((menuInfo
[menuIndex
].secondno
& (~itemStates
)) == menuInfo
[menuIndex
].secondno
))
1086 if (menuInfo
[menuIndex
].thirdyes
&& menuInfo
[menuIndex
].thirdno
)
1088 if (((menuInfo
[menuIndex
].thirdyes
& itemStates
) == menuInfo
[menuIndex
].thirdyes
)
1090 ((menuInfo
[menuIndex
].thirdno
& (~itemStates
)) == menuInfo
[menuIndex
].thirdno
))
1093 else if ((menuInfo
[menuIndex
].thirdyes
)&&((menuInfo
[menuIndex
].thirdyes
& itemStates
) == menuInfo
[menuIndex
].thirdyes
))
1095 else if ((menuInfo
[menuIndex
].thirdno
)&&((menuInfo
[menuIndex
].thirdno
& (~itemStates
)) == menuInfo
[menuIndex
].thirdno
))
1098 if (menuInfo
[menuIndex
].fourthyes
&& menuInfo
[menuIndex
].fourthno
)
1100 if (((menuInfo
[menuIndex
].fourthyes
& itemStates
) == menuInfo
[menuIndex
].fourthyes
)
1102 ((menuInfo
[menuIndex
].fourthno
& (~itemStates
)) == menuInfo
[menuIndex
].fourthno
))
1105 else if ((menuInfo
[menuIndex
].fourthyes
)&&((menuInfo
[menuIndex
].fourthyes
& itemStates
) == menuInfo
[menuIndex
].fourthyes
))
1107 else if ((menuInfo
[menuIndex
].fourthno
)&&((menuInfo
[menuIndex
].fourthno
& (~itemStates
)) == menuInfo
[menuIndex
].fourthno
))
1110 if (menuInfo
[menuIndex
].menuID
& menuex
)
1112 if( !(itemStates
& ITEMIS_EXTENDED
) )
1114 bInsertMenu
= false;
1118 if (menuInfo
[menuIndex
].menuID
& (~menumask
))
1122 bool bIsTop
= ((topmenu
& menuInfo
[menuIndex
].menuID
) != 0);
1123 // insert a separator
1124 if ((bMenuEntryAdded
)&&(bAddSeparator
)&&(!bIsTop
))
1126 bAddSeparator
= false;
1127 bMenuEntryAdded
= false;
1128 InsertMenu(subMenu
, indexSubMenu
++, MF_SEPARATOR
|MF_BYPOSITION
, 0, NULL
);
1132 // handle special cases (sub menus)
1133 if ((menuInfo
[menuIndex
].command
== ShellMenuIgnoreSub
)||(menuInfo
[menuIndex
].command
== ShellMenuUnIgnoreSub
)||(menuInfo
[menuIndex
].command
== ShellMenuDeleteIgnoreSub
))
1135 if(InsertIgnoreSubmenus(idCmd
, idCmdFirst
, hMenu
, subMenu
, indexMenu
, indexSubMenu
, topmenu
, bShowIcons
, uFlags
))
1137 bMenuEntryAdded
= true;
1143 bool bIsTop
= ((topmenu
& menuInfo
[menuIndex
].menuID
) != 0);
1145 // insert the menu entry
1146 InsertGitMenu( bIsTop
,
1147 bIsTop
? hMenu
: subMenu
,
1148 bIsTop
? indexMenu
++ : indexSubMenu
++,
1150 menuInfo
[menuIndex
].menuTextID
,
1151 bShowIcons
? menuInfo
[menuIndex
].iconID
: 0,
1153 menuInfo
[menuIndex
].command
,
1157 bMenuEntryAdded
= true;
1159 bAddSeparator
= false;
1168 //add sub menu to main context menu
1169 //don't use InsertMenu because this will lead to multiple menu entries in the explorer file menu.
1170 //see http://support.microsoft.com/default.aspx?scid=kb;en-us;214477 for details of that.
1171 MAKESTRING(IDS_MENUSUBMENU
);
1172 MENUITEMINFO menuiteminfo
;
1173 SecureZeroMemory(&menuiteminfo
, sizeof(menuiteminfo
));
1174 menuiteminfo
.cbSize
= sizeof(menuiteminfo
);
1175 menuiteminfo
.fType
= MFT_STRING
;
1176 menuiteminfo
.dwTypeData
= stringtablebuffer
;
1178 UINT uIcon
= bShowIcons
? IDI_APP
: 0;
1181 uIcon
= bShowIcons
? IDI_MENUFOLDER
: 0;
1182 myIDMap
[idCmd
- idCmdFirst
] = ShellSubMenuFolder
;
1183 myIDMap
[idCmd
] = ShellSubMenuFolder
;
1184 menuiteminfo
.dwItemData
= (ULONG_PTR
)g_MenuIDString
;
1186 else if (!bShortcut
&& (files_
.size()==1))
1188 uIcon
= bShowIcons
? IDI_MENUFILE
: 0;
1189 myIDMap
[idCmd
- idCmdFirst
] = ShellSubMenuFile
;
1190 myIDMap
[idCmd
] = ShellSubMenuFile
;
1192 else if (bShortcut
&& (files_
.size()==1))
1194 uIcon
= bShowIcons
? IDI_MENULINK
: 0;
1195 myIDMap
[idCmd
- idCmdFirst
] = ShellSubMenuLink
;
1196 myIDMap
[idCmd
] = ShellSubMenuLink
;
1198 else if (files_
.size() > 1)
1200 uIcon
= bShowIcons
? IDI_MENUMULTIPLE
: 0;
1201 myIDMap
[idCmd
- idCmdFirst
] = ShellSubMenuMultiple
;
1202 myIDMap
[idCmd
] = ShellSubMenuMultiple
;
1206 myIDMap
[idCmd
- idCmdFirst
] = ShellSubMenu
;
1207 myIDMap
[idCmd
] = ShellSubMenu
;
1210 if ((fullver
< 0x500)||(fullver
== 0x500 && !(uFlags
&~(CMF_RESERVED
|CMF_EXPLORE
|CMF_EXTENDEDVERBS
))))
1212 menuiteminfo
.fMask
= MIIM_STRING
| MIIM_ID
| MIIM_SUBMENU
| MIIM_DATA
;
1215 menuiteminfo
.fMask
|= MIIM_CHECKMARKS
;
1216 bmp
= IconToBitmap(uIcon
);
1217 menuiteminfo
.hbmpChecked
= bmp
;
1218 menuiteminfo
.hbmpUnchecked
= bmp
;
1223 menuiteminfo
.fMask
= MIIM_FTYPE
| MIIM_ID
| MIIM_SUBMENU
| MIIM_DATA
| MIIM_STRING
;
1226 menuiteminfo
.fMask
|= MIIM_BITMAP
;
1227 menuiteminfo
.hbmpItem
= (fullver
>= 0x600) ? IconToBitmapPARGB32(uIcon
) : HBMMENU_CALLBACK
;
1230 menuiteminfo
.hSubMenu
= subMenu
;
1231 menuiteminfo
.wID
= idCmd
++;
1232 InsertMenuItem(hMenu
, indexMenu
++, TRUE
, &menuiteminfo
);
1235 InsertMenu(hMenu
, indexMenu
++, MF_SEPARATOR
|MF_BYPOSITION
, 0, NULL
); idCmd
++;
1237 //return number of menu items added
1238 return ResultFromScode(MAKE_SCODE(SEVERITY_SUCCESS
, 0, (USHORT
)(idCmd
- idCmdFirst
)));
1242 // This is called when you invoke a command on the menu:
1243 STDMETHODIMP
CShellExt::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi
)
1245 PreserveChdir preserveChdir
;
1246 HRESULT hr
= E_INVALIDARG
;
1250 std::string command
;
1254 if ((files_
.size() > 0)||(folder_
.size() > 0))
1256 UINT idCmd
= LOWORD(lpcmi
->lpVerb
);
1258 if (HIWORD(lpcmi
->lpVerb
))
1260 stdstring verb
= stdstring(MultibyteToWide(lpcmi
->lpVerb
));
1261 std::map
<stdstring
, UINT_PTR
>::const_iterator verb_it
= myVerbsMap
.lower_bound(verb
);
1262 if (verb_it
!= myVerbsMap
.end() && verb_it
->first
== verb
)
1263 idCmd
= verb_it
->second
;
1268 // See if we have a handler interface for this id
1269 std::map
<UINT_PTR
, UINT_PTR
>::const_iterator id_it
= myIDMap
.lower_bound(idCmd
);
1270 if (id_it
!= myIDMap
.end() && id_it
->first
== idCmd
)
1272 STARTUPINFO startup
;
1273 PROCESS_INFORMATION process
;
1274 memset(&startup
, 0, sizeof(startup
));
1275 startup
.cb
= sizeof(startup
);
1276 memset(&process
, 0, sizeof(process
));
1277 CRegStdString
tortoiseProcPath(_T("Software\\TortoiseGit\\ProcPath"), _T("TortoiseProc.exe"), false, HKEY_LOCAL_MACHINE
);
1278 CRegStdString
tortoiseMergePath(_T("Software\\TortoiseGit\\TMergePath"), _T("TortoiseMerge.exe"), false, HKEY_LOCAL_MACHINE
);
1280 //TortoiseProc expects a command line of the form:
1281 //"/command:<commandname> /pathfile:<path> /startrev:<startrevision> /endrev:<endrevision> /deletepathfile
1283 //"/command:<commandname> /path:<path> /startrev:<startrevision> /endrev:<endrevision>
1285 //* path is a path to a single file/directory for commands which only act on single items (log, checkout, ...)
1286 //* pathfile is a path to a temporary file which contains a list of file paths
1287 stdstring svnCmd
= _T(" /command:");
1289 switch (id_it
->second
)
1293 svnCmd
+= _T("sync /path:\"");
1294 if (files_
.size() > 0)
1295 svnCmd
+= files_
.front();
1300 case ShellMenuCheckout
:
1301 svnCmd
+= _T("checkout /path:\"");
1302 if (files_
.size() > 0)
1303 svnCmd
+= files_
.front();
1308 case ShellMenuUpdate
:
1309 tempfile
= WriteFileListToTempFile();
1310 svnCmd
+= _T("update /pathfile:\"");
1313 svnCmd
+= _T(" /deletepathfile");
1315 case ShellMenuSubSync
:
1316 tempfile
= WriteFileListToTempFile();
1317 svnCmd
+= _T("subsync /pathfile:\"");
1320 svnCmd
+= _T(" /deletepathfile");
1321 if(itemStatesFolder
&ITEMIS_SUBMODULECONTAINER
)
1323 svnCmd
+= _T(" /bkpath:\"");
1328 case ShellMenuUpdateExt
:
1329 tempfile
= WriteFileListToTempFile();
1330 svnCmd
+= _T("subupdate /pathfile:\"");
1333 svnCmd
+= _T(" /deletepathfile");
1334 if(itemStatesFolder
&ITEMIS_SUBMODULECONTAINER
)
1336 svnCmd
+= _T(" /bkpath:\"");
1341 case ShellMenuCommit
:
1342 tempfile
= WriteFileListToTempFile();
1343 svnCmd
+= _T("commit /pathfile:\"");
1346 svnCmd
+= _T(" /deletepathfile");
1349 case ShellMenuAddAsReplacement
:
1350 tempfile
= WriteFileListToTempFile();
1351 svnCmd
+= _T("add /pathfile:\"");
1354 svnCmd
+= _T(" /deletepathfile");
1356 case ShellMenuIgnore
:
1357 tempfile
= WriteFileListToTempFile();
1358 svnCmd
+= _T("ignore /pathfile:\"");
1361 svnCmd
+= _T(" /deletepathfile");
1363 case ShellMenuIgnoreCaseSensitive
:
1364 tempfile
= WriteFileListToTempFile();
1365 svnCmd
+= _T("ignore /pathfile:\"");
1368 svnCmd
+= _T(" /deletepathfile");
1369 svnCmd
+= _T(" /onlymask");
1371 case ShellMenuDeleteIgnore
:
1372 tempfile
= WriteFileListToTempFile();
1373 svnCmd
+= _T("ignore /delete /pathfile:\"");
1376 svnCmd
+= _T(" /deletepathfile");
1378 case ShellMenuDeleteIgnoreCaseSensitive
:
1379 tempfile
= WriteFileListToTempFile();
1380 svnCmd
+= _T("ignore /delete /pathfile:\"");
1383 svnCmd
+= _T(" /deletepathfile");
1384 svnCmd
+= _T(" /onlymask");
1386 case ShellMenuUnIgnore
:
1387 tempfile
= WriteFileListToTempFile();
1388 svnCmd
+= _T("unignore /pathfile:\"");
1391 svnCmd
+= _T(" /deletepathfile");
1393 case ShellMenuUnIgnoreCaseSensitive
:
1394 tempfile
= WriteFileListToTempFile();
1395 svnCmd
+= _T("unignore /pathfile:\"");
1398 svnCmd
+= _T(" /deletepathfile");
1399 svnCmd
+= _T(" /onlymask");
1401 case ShellMenuRevert
:
1402 tempfile
= WriteFileListToTempFile();
1403 svnCmd
+= _T("revert /pathfile:\"");
1406 svnCmd
+= _T(" /deletepathfile");
1408 case ShellMenuDelUnversioned
:
1409 svnCmd
+= _T("delunversioned /path:\"");
1413 case ShellMenuCleanup
:
1414 tempfile
= WriteFileListToTempFile();
1415 svnCmd
+= _T("cleanup /pathfile:\"");
1418 svnCmd
+= _T(" /deletepathfile");
1420 case ShellMenuSendMail
:
1421 tempfile
= WriteFileListToTempFile();
1422 svnCmd
+= _T("sendmail /pathfile:\"");
1425 svnCmd
+= _T(" /deletepathfile");
1427 case ShellMenuResolve
:
1428 tempfile
= WriteFileListToTempFile();
1429 svnCmd
+= _T("resolve /pathfile:\"");
1432 svnCmd
+= _T(" /deletepathfile");
1434 case ShellMenuSwitch
:
1435 svnCmd
+= _T("switch /path:\"");
1436 if (files_
.size() > 0)
1437 svnCmd
+= files_
.front();
1442 case ShellMenuImport
:
1443 svnCmd
+= _T("import /path:\"");
1444 if (files_
.size() > 0)
1445 svnCmd
+= files_
.front();
1450 case ShellMenuExport
:
1451 svnCmd
+= _T("export /path:\"");
1452 if (files_
.size() > 0)
1453 svnCmd
+= files_
.front();
1458 case ShellMenuAbout
:
1459 svnCmd
+= _T("about");
1461 case ShellMenuCreateRepos
:
1462 svnCmd
+= _T("repocreate /path:\"");
1463 if (files_
.size() > 0)
1464 svnCmd
+= files_
.front();
1469 case ShellMenuMerge
:
1470 svnCmd
+= _T("merge /path:\"");
1471 if (files_
.size() > 0)
1472 svnCmd
+= files_
.front();
1477 case ShellMenuMergeAll
:
1478 svnCmd
+= _T("mergeall /path:\"");
1479 if (files_
.size() > 0)
1480 svnCmd
+= files_
.front();
1486 svnCmd
+= _T("copy /path:\"");
1487 if (files_
.size() > 0)
1488 svnCmd
+= files_
.front();
1493 case ShellMenuSettings
:
1494 svnCmd
+= _T("settings /path:\"");
1495 if (files_
.size() > 0)
1496 svnCmd
+= files_
.front();
1502 svnCmd
+= _T("help");
1504 case ShellMenuRename
:
1505 svnCmd
+= _T("rename /path:\"");
1506 if (files_
.size() > 0)
1507 svnCmd
+= files_
.front();
1512 case ShellMenuRemove
:
1513 tempfile
= WriteFileListToTempFile();
1514 svnCmd
+= _T("remove /pathfile:\"");
1517 svnCmd
+= _T(" /deletepathfile");
1519 case ShellMenuRemoveKeep
:
1520 tempfile
= WriteFileListToTempFile();
1521 svnCmd
+= _T("remove /pathfile:\"");
1524 svnCmd
+= _T(" /deletepathfile");
1525 svnCmd
+= _T(" /keep");
1528 svnCmd
+= _T("diff /path:\"");
1529 if (files_
.size() == 1)
1530 svnCmd
+= files_
.front();
1531 else if (files_
.size() == 2)
1533 std::vector
<stdstring
>::iterator I
= files_
.begin();
1536 svnCmd
+= _T("\" /path2:\"");
1542 if (GetAsyncKeyState(VK_SHIFT
) & 0x8000)
1543 svnCmd
+= _T(" /alternative");
1545 case ShellMenuPrevDiff
:
1546 svnCmd
+= _T("prevdiff /path:\"");
1547 if (files_
.size() == 1)
1548 svnCmd
+= files_
.front();
1552 if (GetAsyncKeyState(VK_SHIFT
) & 0x8000)
1553 svnCmd
+= _T(" /alternative");
1555 case ShellMenuUrlDiff
:
1556 svnCmd
+= _T("urldiff /path:\"");
1557 if (files_
.size() == 1)
1558 svnCmd
+= files_
.front();
1563 case ShellMenuDiffTwo
:
1564 svnCmd
+= _T("diffcommits /path:\"");
1565 if (files_
.size() == 1)
1566 svnCmd
+= files_
.front();
1571 case ShellMenuDropCopyAdd
:
1572 tempfile
= WriteFileListToTempFile();
1573 svnCmd
+= _T("dropcopyadd /pathfile:\"");
1576 svnCmd
+= _T(" /deletepathfile");
1577 svnCmd
+= _T(" /droptarget:\"");
1581 case ShellMenuDropCopy
:
1582 tempfile
= WriteFileListToTempFile();
1583 svnCmd
+= _T("dropcopy /pathfile:\"");
1586 svnCmd
+= _T(" /deletepathfile");
1587 svnCmd
+= _T(" /droptarget:\"");
1591 case ShellMenuDropCopyRename
:
1592 tempfile
= WriteFileListToTempFile();
1593 svnCmd
+= _T("dropcopy /pathfile:\"");
1596 svnCmd
+= _T(" /deletepathfile");
1597 svnCmd
+= _T(" /droptarget:\"");
1599 svnCmd
+= _T("\" /rename";)
1601 case ShellMenuDropMove
:
1602 tempfile
= WriteFileListToTempFile();
1603 svnCmd
+= _T("dropmove /pathfile:\"");
1606 svnCmd
+= _T(" /deletepathfile");
1607 svnCmd
+= _T(" /droptarget:\"");
1611 case ShellMenuDropMoveRename
:
1612 tempfile
= WriteFileListToTempFile();
1613 svnCmd
+= _T("dropmove /pathfile:\"");
1616 svnCmd
+= _T(" /deletepathfile");
1617 svnCmd
+= _T(" /droptarget:\"");
1619 svnCmd
+= _T("\" /rename";)
1621 case ShellMenuDropExport
:
1622 tempfile
= WriteFileListToTempFile();
1623 svnCmd
+= _T("dropexport /pathfile:\"");
1626 svnCmd
+= _T(" /deletepathfile");
1627 svnCmd
+= _T(" /droptarget:\"");
1631 case ShellMenuDropExportExtended
:
1632 tempfile
= WriteFileListToTempFile();
1633 svnCmd
+= _T("dropexport /pathfile:\"");
1636 svnCmd
+= _T(" /deletepathfile");
1637 svnCmd
+= _T(" /droptarget:\"");
1640 svnCmd
+= _T(" /extended");
1643 svnCmd
+= _T("log /path:\"");
1644 if (files_
.size() > 0)
1645 svnCmd
+= files_
.front();
1650 case ShellMenuConflictEditor
:
1651 svnCmd
+= _T("conflicteditor /path:\"");
1652 if (files_
.size() > 0)
1653 svnCmd
+= files_
.front();
1658 case ShellMenuRelocate
:
1659 svnCmd
+= _T("relocate /path:\"");
1660 if (files_
.size() > 0)
1661 svnCmd
+= files_
.front();
1666 case ShellMenuGitSVNRebase
:
1667 svnCmd
+= _T("svnrebase /path:\"");
1668 if (files_
.size() > 0)
1669 svnCmd
+= files_
.front();
1674 case ShellMenuGitSVNDCommit
:
1675 svnCmd
+= _T("svndcommit /path:\"");
1676 if (files_
.size() > 0)
1677 svnCmd
+= files_
.front();
1682 case ShellMenuGitSVNIgnore
:
1683 svnCmd
+= _T("svnignore /path:\"");
1684 if (files_
.size() > 0)
1685 svnCmd
+= files_
.front();
1690 case ShellMenuRebase
:
1691 svnCmd
+= _T("rebase /path:\"");
1692 if (files_
.size() > 0)
1693 svnCmd
+= files_
.front();
1698 case ShellMenuShowChanged
:
1699 if (files_
.size() > 1)
1701 tempfile
= WriteFileListToTempFile();
1702 svnCmd
+= _T("repostatus /pathfile:\"");
1705 svnCmd
+= _T(" /deletepathfile");
1709 svnCmd
+= _T("repostatus /path:\"");
1710 if (files_
.size() > 0)
1711 svnCmd
+= files_
.front();
1717 case ShellMenuRefBrowse
:
1718 svnCmd
+= _T("refbrowse /path:\"");
1719 if (files_
.size() > 0)
1720 svnCmd
+= files_
.front();
1725 case ShellMenuRefLog
:
1726 svnCmd
+= _T("reflog /path:\"");
1727 if (files_
.size() > 0)
1728 svnCmd
+= files_
.front();
1734 case ShellMenuStashSave
:
1735 svnCmd
+= _T("stashsave /path:\"");
1736 if (files_
.size() > 0)
1737 svnCmd
+= files_
.front();
1743 case ShellMenuStashApply
:
1744 svnCmd
+= _T("stashapply /path:\"");
1745 if (files_
.size() > 0)
1746 svnCmd
+= files_
.front();
1752 case ShellMenuStashPop
:
1753 svnCmd
+= _T("stashpop /path:\"");
1754 if (files_
.size() > 0)
1755 svnCmd
+= files_
.front();
1762 case ShellMenuStashList
:
1763 svnCmd
+= _T("reflog /path:\"");
1764 if (files_
.size() > 0)
1765 svnCmd
+= files_
.front();
1768 svnCmd
+= _T("\" /ref:refs/stash");
1771 case ShellMenuSubAdd
:
1772 svnCmd
+= _T("subadd /path:\"");
1773 if (files_
.size() > 0)
1774 svnCmd
+= files_
.front();
1780 case ShellMenuBlame
:
1781 svnCmd
+= _T("blame /path:\"");
1782 if (files_
.size() > 0)
1783 svnCmd
+= files_
.front();
1788 case ShellMenuCreatePatch
:
1789 tempfile
= WriteFileListToTempFile();
1790 svnCmd
+= _T("createpatch /pathfile:\"");
1793 svnCmd
+= _T(" /deletepathfile");
1795 case ShellMenuApplyPatch
:
1796 if ((itemStates
& ITEMIS_PATCHINCLIPBOARD
) && ((~itemStates
) & ITEMIS_PATCHFILE
))
1798 // if there's a patch file in the clipboard, we save it
1799 // to a temporary file and tell TortoiseMerge to use that one
1800 UINT cFormat
= RegisterClipboardFormat(_T("TGIT_UNIFIEDDIFF"));
1801 if ((cFormat
)&&(OpenClipboard(NULL
)))
1803 HGLOBAL hglb
= GetClipboardData(cFormat
);
1804 LPCSTR lpstr
= (LPCSTR
)GlobalLock(hglb
);
1806 DWORD len
= GetTempPath(0, NULL
);
1807 TCHAR
* path
= new TCHAR
[len
+1];
1808 TCHAR
* tempF
= new TCHAR
[len
+100];
1809 GetTempPath (len
+1, path
);
1810 GetTempFileName (path
, TEXT("git"), 0, tempF
);
1811 std::wstring sTempFile
= std::wstring(tempF
);
1816 size_t patchlen
= strlen(lpstr
);
1817 _tfopen_s(&outFile
, sTempFile
.c_str(), _T("wb"));
1820 size_t size
= fwrite(lpstr
, sizeof(char), patchlen
, outFile
);
1821 if (size
== patchlen
)
1823 itemStates
|= ITEMIS_PATCHFILE
;
1825 files_
.push_back(sTempFile
);
1833 if (itemStates
& ITEMIS_PATCHFILE
)
1835 svnCmd
= _T(" /diff:\"");
1836 if (files_
.size() > 0)
1838 svnCmd
+= files_
.front();
1839 if (itemStatesFolder
& ITEMIS_FOLDERINSVN
)
1841 svnCmd
+= _T("\" /patchpath:\"");
1847 if (itemStates
& ITEMIS_INVERSIONEDFOLDER
)
1848 svnCmd
+= _T("\" /wc");
1854 svnCmd
= _T(" /patchpath:\"");
1855 if (files_
.size() > 0)
1856 svnCmd
+= files_
.front();
1862 myVerbsIDMap
.clear();
1864 if (CreateProcess(((stdstring
)tortoiseMergePath
).c_str(), const_cast<TCHAR
*>(svnCmd
.c_str()), NULL
, NULL
, FALSE
, 0, 0, 0, &startup
, &process
)==0)
1867 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
|
1868 FORMAT_MESSAGE_FROM_SYSTEM
|
1869 FORMAT_MESSAGE_IGNORE_INSERTS
,
1872 MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
), // Default language
1877 MessageBox( NULL
, (LPCTSTR
)lpMsgBuf
, _T("TortoiseMerge launch failed"), MB_OK
| MB_ICONINFORMATION
);
1878 LocalFree( lpMsgBuf
);
1880 CloseHandle(process
.hThread
);
1881 CloseHandle(process
.hProcess
);
1884 case ShellMenuRevisionGraph
:
1885 svnCmd
+= _T("revisiongraph /path:\"");
1886 if (files_
.size() > 0)
1887 svnCmd
+= files_
.front();
1892 case ShellMenuProperties
:
1893 tempfile
= WriteFileListToTempFile();
1894 svnCmd
+= _T("properties /pathfile:\"");
1897 svnCmd
+= _T(" /deletepathfile");
1899 case ShellMenuClipPaste
:
1900 if (WriteClipboardPathsToTempFile(tempfile
))
1903 UINT cPrefDropFormat
= RegisterClipboardFormat(_T("Preferred DropEffect"));
1904 if (cPrefDropFormat
)
1906 if (OpenClipboard(lpcmi
->hwnd
))
1908 HGLOBAL hglb
= GetClipboardData(cPrefDropFormat
);
1911 DWORD
* effect
= (DWORD
*) GlobalLock(hglb
);
1912 if (*effect
== DROPEFFECT_MOVE
)
1921 svnCmd
+= _T("pastecopy /pathfile:\"");
1923 svnCmd
+= _T("pastemove /pathfile:\"");
1926 svnCmd
+= _T(" /deletepathfile");
1927 svnCmd
+= _T(" /droptarget:\"");
1931 else return NOERROR
;
1933 case ShellMenuClone
:
1934 svnCmd
+= _T("clone /path:\"");
1935 if (files_
.size() > 0)
1936 svnCmd
+= files_
.front();
1942 svnCmd
+= _T("pull /path:\"");
1943 if (files_
.size() > 0)
1944 svnCmd
+= files_
.front();
1950 svnCmd
+= _T("push /path:\"");
1951 if (files_
.size() > 0)
1952 svnCmd
+= files_
.front();
1957 case ShellMenuBranch
:
1958 svnCmd
+= _T("branch /path:\"");
1959 if (files_
.size() > 0)
1960 svnCmd
+= files_
.front();
1967 svnCmd
+= _T("tag /path:\"");
1968 if (files_
.size() > 0)
1969 svnCmd
+= files_
.front();
1975 case ShellMenuFormatPatch
:
1976 svnCmd
+= _T("formatpatch /path:\"");
1977 if (files_
.size() > 0)
1978 svnCmd
+= files_
.front();
1984 case ShellMenuImportPatch
:
1985 tempfile
= WriteFileListToTempFile();
1986 svnCmd
+= _T("importpatch /pathfile:\"");
1989 svnCmd
+= _T(" /deletepathfile");
1992 case ShellMenuCherryPick
:
1993 svnCmd
+= _T("cherrypick /path:\"");
1994 if (files_
.size() > 0)
1995 svnCmd
+= files_
.front();
2000 case ShellMenuFetch
:
2001 svnCmd
+= _T("fetch /path:\"");
2002 if (files_
.size() > 0)
2003 svnCmd
+= files_
.front();
2012 } // switch (id_it->second)
2013 svnCmd
+= _T(" /hwnd:");
2015 _stprintf_s(buf
, 30, _T("%d"), lpcmi
->hwnd
);
2018 myVerbsIDMap
.clear();
2020 if (CreateProcess(((stdstring
)tortoiseProcPath
).c_str(), const_cast<TCHAR
*>(svnCmd
.c_str()), NULL
, NULL
, FALSE
, 0, 0, 0, &startup
, &process
)==0)
2023 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
|
2024 FORMAT_MESSAGE_FROM_SYSTEM
|
2025 FORMAT_MESSAGE_IGNORE_INSERTS
,
2028 MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
), // Default language
2033 MessageBox( NULL
, (LPCTSTR
)lpMsgBuf
, _T("TortoiseProc Launch failed"), MB_OK
| MB_ICONINFORMATION
);
2034 LocalFree( lpMsgBuf
);
2036 CloseHandle(process
.hThread
);
2037 CloseHandle(process
.hProcess
);
2039 } // if (id_it != myIDMap.end() && id_it->first == idCmd)
2040 } // if ((files_.size() > 0)||(folder_.size() > 0))
2045 // This is for the status bar and things like that:
2046 STDMETHODIMP
CShellExt::GetCommandString(UINT_PTR idCmd
,
2048 UINT FAR
* /*reserved*/,
2052 PreserveChdir preserveChdir
;
2053 //do we know the id?
2054 std::map
<UINT_PTR
, UINT_PTR
>::const_iterator id_it
= myIDMap
.lower_bound(idCmd
);
2055 if (id_it
== myIDMap
.end() || id_it
->first
!= idCmd
)
2057 return E_INVALIDARG
; //no, we don't
2061 HRESULT hr
= E_INVALIDARG
;
2063 MAKESTRING(IDS_MENUDESCDEFAULT
);
2065 while (menuInfo
[menuIndex
].command
!= ShellMenuLastEntry
)
2067 if (menuInfo
[menuIndex
].command
== (GitCommands
)id_it
->second
)
2069 MAKESTRING(menuInfo
[menuIndex
].menuDescID
);
2075 const TCHAR
* desc
= stringtablebuffer
;
2080 std::string help
= WideToMultibyte(desc
);
2081 lstrcpynA(pszName
, help
.c_str(), cchMax
);
2087 wide_string help
= desc
;
2088 lstrcpynW((LPWSTR
)pszName
, help
.c_str(), cchMax
);
2094 std::map
<UINT_PTR
, stdstring
>::const_iterator verb_id_it
= myVerbsIDMap
.lower_bound(idCmd
);
2095 if (verb_id_it
!= myVerbsIDMap
.end() && verb_id_it
->first
== idCmd
)
2097 std::string help
= WideToMultibyte(verb_id_it
->second
);
2098 lstrcpynA(pszName
, help
.c_str(), cchMax
);
2105 std::map
<UINT_PTR
, stdstring
>::const_iterator verb_id_it
= myVerbsIDMap
.lower_bound(idCmd
);
2106 if (verb_id_it
!= myVerbsIDMap
.end() && verb_id_it
->first
== idCmd
)
2108 wide_string help
= verb_id_it
->second
;
2109 ATLTRACE("verb : %ws\n", help
.c_str());
2110 lstrcpynW((LPWSTR
)pszName
, help
.c_str(), cchMax
);
2119 STDMETHODIMP
CShellExt::HandleMenuMsg(UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
2122 return HandleMenuMsg2(uMsg
, wParam
, lParam
, &res
);
2125 STDMETHODIMP
CShellExt::HandleMenuMsg2(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, LRESULT
*pResult
)
2127 PreserveChdir preserveChdir
;
2130 if (pResult
== NULL
)
2137 case WM_MEASUREITEM
:
2139 MEASUREITEMSTRUCT
* lpmis
= (MEASUREITEMSTRUCT
*)lParam
;
2140 if (lpmis
==NULL
||lpmis
->CtlType
!=ODT_MENU
)
2142 lpmis
->itemWidth
+= 2;
2143 if (lpmis
->itemHeight
< 16)
2144 lpmis
->itemHeight
= 16;
2151 DRAWITEMSTRUCT
* lpdis
= (DRAWITEMSTRUCT
*)lParam
;
2152 if ((lpdis
==NULL
)||(lpdis
->CtlType
!= ODT_MENU
))
2153 return S_OK
; //not for a menu
2154 resource
= GetMenuTextFromResource(myIDMap
[lpdis
->itemID
]);
2155 if (resource
== NULL
)
2157 HICON hIcon
= (HICON
)LoadImage(g_hResInst
, resource
, IMAGE_ICON
, 16, 16, LR_DEFAULTCOLOR
);
2160 DrawIconEx(lpdis
->hDC
,
2161 lpdis
->rcItem
.left
< 16 ? lpdis
->rcItem
.left
: lpdis
->rcItem
.left
- 16,
2162 lpdis
->rcItem
.top
+ (lpdis
->rcItem
.bottom
- lpdis
->rcItem
.top
- 16) / 2,
2164 0, NULL
, DI_NORMAL
);
2173 if (HIWORD(wParam
) != MF_POPUP
)
2175 int nChar
= LOWORD(wParam
);
2176 if (_istascii((wint_t)nChar
) && _istupper((wint_t)nChar
))
2177 nChar
= tolower(nChar
);
2178 // we have the char the user pressed, now search that char in all our
2180 std::vector
<int> accmenus
;
2181 for (std::map
<UINT_PTR
, UINT_PTR
>::iterator It
= mySubMenuMap
.begin(); It
!= mySubMenuMap
.end(); ++It
)
2183 resource
= GetMenuTextFromResource(mySubMenuMap
[It
->first
]);
2184 if (resource
== NULL
)
2186 szItem
= stringtablebuffer
;
2187 TCHAR
* amp
= _tcschr(szItem
, '&');
2191 int ampChar
= LOWORD(*amp
);
2192 if (_istascii((wint_t)ampChar
) && _istupper((wint_t)ampChar
))
2193 ampChar
= tolower(ampChar
);
2194 if (ampChar
== nChar
)
2196 // yep, we found a menu which has the pressed key
2197 // as an accelerator. Add that menu to the list to
2199 accmenus
.push_back(It
->first
);
2202 if (accmenus
.size() == 0)
2204 // no menu with that accelerator key.
2205 *pResult
= MAKELONG(0, MNC_IGNORE
);
2208 if (accmenus
.size() == 1)
2210 // Only one menu with that accelerator key. We're lucky!
2211 // So just execute that menu entry.
2212 *pResult
= MAKELONG(accmenus
[0], MNC_EXECUTE
);
2215 if (accmenus
.size() > 1)
2217 // we have more than one menu item with this accelerator key!
2219 mif
.cbSize
= sizeof(MENUITEMINFO
);
2220 mif
.fMask
= MIIM_STATE
;
2221 for (std::vector
<int>::iterator it
= accmenus
.begin(); it
!= accmenus
.end(); ++it
)
2223 GetMenuItemInfo((HMENU
)lParam
, *it
, TRUE
, &mif
);
2224 if (mif
.fState
== MFS_HILITE
)
2226 // this is the selected item, so select the next one
2228 if (it
== accmenus
.end())
2229 *pResult
= MAKELONG(accmenus
[0], MNC_SELECT
);
2231 *pResult
= MAKELONG(*it
, MNC_SELECT
);
2235 *pResult
= MAKELONG(accmenus
[0], MNC_SELECT
);
2246 LPCTSTR
CShellExt::GetMenuTextFromResource(int id
)
2249 LPCTSTR resource
= NULL
;
2250 unsigned __int64 layout
= g_ShellCache
.GetMenuLayout();
2254 while (menuInfo
[menuIndex
].command
!= ShellMenuLastEntry
)
2256 if (menuInfo
[menuIndex
].command
== id
)
2258 MAKESTRING(menuInfo
[menuIndex
].menuTextID
);
2259 resource
= MAKEINTRESOURCE(menuInfo
[menuIndex
].iconID
);
2262 case ShellSubMenuMultiple
:
2263 case ShellSubMenuLink
:
2264 case ShellSubMenuFolder
:
2265 case ShellSubMenuFile
:
2270 space
= layout
& menuInfo
[menuIndex
].menuID
? 0 : 6;
2271 if (layout
& (menuInfo
[menuIndex
].menuID
))
2273 _tcscpy_s(textbuf
, 255, _T("Git "));
2274 _tcscat_s(textbuf
, 255, stringtablebuffer
);
2275 _tcscpy_s(stringtablebuffer
, 255, textbuf
);
2286 bool CShellExt::IsIllegalFolder(std::wstring folder
, int * cslidarray
)
2289 TCHAR buf
[MAX_PATH
]; //MAX_PATH ok, since SHGetSpecialFolderPath doesn't return the required buffer length!
2290 LPITEMIDLIST pidl
= NULL
;
2291 while (cslidarray
[i
])
2295 if (SHGetFolderLocation(NULL
, cslidarray
[i
-1], NULL
, 0, &pidl
)!=S_OK
)
2297 if (!SHGetPathFromIDList(pidl
, buf
))
2299 // not a file system path, definitely illegal for our use
2300 CoTaskMemFree(pidl
);
2303 CoTaskMemFree(pidl
);
2304 if (_tcslen(buf
)==0)
2306 if (_tcscmp(buf
, folder
.c_str())==0)
2312 bool CShellExt::InsertIgnoreSubmenus(UINT
&idCmd
, UINT idCmdFirst
, HMENU hMenu
, HMENU subMenu
, UINT
&indexMenu
, int &indexSubMenu
, unsigned __int64 topmenu
, bool bShowIcons
, UINT uFlags
)
2314 HMENU ignoresubmenu
= NULL
;
2315 int indexignoresub
= 0;
2316 bool bShowIgnoreMenu
= false;
2317 TCHAR maskbuf
[MAX_PATH
]; // MAX_PATH is ok, since this only holds a filename
2318 TCHAR ignorepath
[MAX_PATH
]; // MAX_PATH is ok, since this only holds a filename
2319 if (files_
.size() == 0 || (files_
.size() == 1 && folder_
.length() != 0 && g_GitAdminDir
.GetGitTopDir(folder_
.c_str()) == folder_
.c_str()))
2321 UINT icon
= bShowIcons
? IDI_IGNORE
: 0;
2323 std::vector
<stdstring
>::iterator I
= files_
.begin();
2324 if (_tcsrchr(I
->c_str(), '\\'))
2325 _tcscpy_s(ignorepath
, MAX_PATH
, _tcsrchr(I
->c_str(), '\\')+1);
2327 _tcscpy_s(ignorepath
, MAX_PATH
, I
->c_str());
2328 if ((itemStates
& ITEMIS_IGNORED
)&&(ignoredprops
.size() > 0))
2330 // check if the item name is ignored or the mask
2332 while ( (p
=ignoredprops
.find( ignorepath
,p
)) != -1 )
2334 if ( (p
==0 || ignoredprops
[p
-1]==TCHAR('\n'))
2335 && (p
+_tcslen(ignorepath
)==ignoredprops
.length() || ignoredprops
[p
+_tcslen(ignorepath
)+1]==TCHAR('\n')) )
2343 ignoresubmenu
= CreateMenu();
2344 InsertMenu(ignoresubmenu
, indexignoresub
++, MF_BYPOSITION
| MF_STRING
, idCmd
, ignorepath
);
2345 stdstring verb
= stdstring(ignorepath
);
2346 myVerbsMap
[verb
] = idCmd
- idCmdFirst
;
2347 myVerbsMap
[verb
] = idCmd
;
2348 myVerbsIDMap
[idCmd
- idCmdFirst
] = verb
;
2349 myVerbsIDMap
[idCmd
] = verb
;
2350 myIDMap
[idCmd
- idCmdFirst
] = ShellMenuUnIgnore
;
2351 myIDMap
[idCmd
++] = ShellMenuUnIgnore
;
2352 bShowIgnoreMenu
= true;
2354 _tcscpy_s(maskbuf
, MAX_PATH
, _T("*"));
2355 if (_tcsrchr(ignorepath
, '.'))
2357 _tcscat_s(maskbuf
, MAX_PATH
, _tcsrchr(ignorepath
, '.'));
2358 p
= ignoredprops
.find(maskbuf
);
2360 ((ignoredprops
.compare(maskbuf
)==0) || (ignoredprops
.find('\n', p
)==p
+_tcslen(maskbuf
)+1) || (ignoredprops
.rfind('\n', p
)==p
-1)))
2362 if (ignoresubmenu
==NULL
)
2363 ignoresubmenu
= CreateMenu();
2365 InsertMenu(ignoresubmenu
, indexignoresub
++, MF_BYPOSITION
| MF_STRING
, idCmd
, maskbuf
);
2366 stdstring verb
= stdstring(maskbuf
);
2367 myVerbsMap
[verb
] = idCmd
- idCmdFirst
;
2368 myVerbsMap
[verb
] = idCmd
;
2369 myVerbsIDMap
[idCmd
- idCmdFirst
] = verb
;
2370 myVerbsIDMap
[idCmd
] = verb
;
2371 myIDMap
[idCmd
- idCmdFirst
] = ShellMenuUnIgnoreCaseSensitive
;
2372 myIDMap
[idCmd
++] = ShellMenuUnIgnoreCaseSensitive
;
2373 bShowIgnoreMenu
= true;
2377 else if ((itemStates
& ITEMIS_IGNORED
) == 0)
2379 bShowIgnoreMenu
= true;
2380 ignoresubmenu
= CreateMenu();
2381 if (itemStates
& ITEMIS_ONLYONE
)
2383 if (itemStates
& ITEMIS_INSVN
)
2385 InsertMenu(ignoresubmenu
, indexignoresub
++, MF_BYPOSITION
| MF_STRING
, idCmd
, ignorepath
);
2386 myIDMap
[idCmd
- idCmdFirst
] = ShellMenuDeleteIgnore
;
2387 myIDMap
[idCmd
++] = ShellMenuDeleteIgnore
;
2389 _tcscpy_s(maskbuf
, MAX_PATH
, _T("*"));
2390 if (_tcsrchr(ignorepath
, '.'))
2392 _tcscat_s(maskbuf
, MAX_PATH
, _tcsrchr(ignorepath
, '.'));
2393 InsertMenu(ignoresubmenu
, indexignoresub
++, MF_BYPOSITION
| MF_STRING
, idCmd
, maskbuf
);
2394 stdstring verb
= stdstring(maskbuf
);
2395 myVerbsMap
[verb
] = idCmd
- idCmdFirst
;
2396 myVerbsMap
[verb
] = idCmd
;
2397 myVerbsIDMap
[idCmd
- idCmdFirst
] = verb
;
2398 myVerbsIDMap
[idCmd
] = verb
;
2399 myIDMap
[idCmd
- idCmdFirst
] = ShellMenuDeleteIgnoreCaseSensitive
;
2400 myIDMap
[idCmd
++] = ShellMenuDeleteIgnoreCaseSensitive
;
2405 InsertMenu(ignoresubmenu
, indexignoresub
++, MF_BYPOSITION
| MF_STRING
, idCmd
, ignorepath
);
2406 myIDMap
[idCmd
- idCmdFirst
] = ShellMenuIgnore
;
2407 myIDMap
[idCmd
++] = ShellMenuIgnore
;
2409 _tcscpy_s(maskbuf
, MAX_PATH
, _T("*"));
2410 if (_tcsrchr(ignorepath
, '.'))
2412 _tcscat_s(maskbuf
, MAX_PATH
, _tcsrchr(ignorepath
, '.'));
2413 InsertMenu(ignoresubmenu
, indexignoresub
++, MF_BYPOSITION
| MF_STRING
, idCmd
, maskbuf
);
2414 stdstring verb
= stdstring(maskbuf
);
2415 myVerbsMap
[verb
] = idCmd
- idCmdFirst
;
2416 myVerbsMap
[verb
] = idCmd
;
2417 myVerbsIDMap
[idCmd
- idCmdFirst
] = verb
;
2418 myVerbsIDMap
[idCmd
] = verb
;
2419 myIDMap
[idCmd
- idCmdFirst
] = ShellMenuIgnoreCaseSensitive
;
2420 myIDMap
[idCmd
++] = ShellMenuIgnoreCaseSensitive
;
2426 if (itemStates
& ITEMIS_INSVN
)
2428 MAKESTRING(IDS_MENUDELETEIGNOREMULTIPLE
);
2429 _stprintf_s(ignorepath
, MAX_PATH
, stringtablebuffer
, files_
.size());
2430 InsertMenu(ignoresubmenu
, indexignoresub
++, MF_BYPOSITION
| MF_STRING
, idCmd
, ignorepath
);
2431 stdstring verb
= stdstring(ignorepath
);
2432 myVerbsMap
[verb
] = idCmd
- idCmdFirst
;
2433 myVerbsMap
[verb
] = idCmd
;
2434 myVerbsIDMap
[idCmd
- idCmdFirst
] = verb
;
2435 myVerbsIDMap
[idCmd
] = verb
;
2436 myIDMap
[idCmd
- idCmdFirst
] = ShellMenuDeleteIgnore
;
2437 myIDMap
[idCmd
++] = ShellMenuDeleteIgnore
;
2439 MAKESTRING(IDS_MENUDELETEIGNOREMULTIPLEMASK
);
2440 _stprintf_s(ignorepath
, MAX_PATH
, stringtablebuffer
, files_
.size());
2441 InsertMenu(ignoresubmenu
, indexignoresub
++, MF_BYPOSITION
| MF_STRING
, idCmd
, ignorepath
);
2442 verb
= stdstring(ignorepath
);
2443 myVerbsMap
[verb
] = idCmd
- idCmdFirst
;
2444 myVerbsMap
[verb
] = idCmd
;
2445 myVerbsIDMap
[idCmd
- idCmdFirst
] = verb
;
2446 myVerbsIDMap
[idCmd
] = verb
;
2447 myIDMap
[idCmd
- idCmdFirst
] = ShellMenuDeleteIgnoreCaseSensitive
;
2448 myIDMap
[idCmd
++] = ShellMenuDeleteIgnoreCaseSensitive
;
2452 MAKESTRING(IDS_MENUIGNOREMULTIPLE
);
2453 _stprintf_s(ignorepath
, MAX_PATH
, stringtablebuffer
, files_
.size());
2454 InsertMenu(ignoresubmenu
, indexignoresub
++, MF_BYPOSITION
| MF_STRING
, idCmd
, ignorepath
);
2455 stdstring verb
= stdstring(ignorepath
);
2456 myVerbsMap
[verb
] = idCmd
- idCmdFirst
;
2457 myVerbsMap
[verb
] = idCmd
;
2458 myVerbsIDMap
[idCmd
- idCmdFirst
] = verb
;
2459 myVerbsIDMap
[idCmd
] = verb
;
2460 myIDMap
[idCmd
- idCmdFirst
] = ShellMenuIgnore
;
2461 myIDMap
[idCmd
++] = ShellMenuIgnore
;
2463 MAKESTRING(IDS_MENUIGNOREMULTIPLEMASK
);
2464 _stprintf_s(ignorepath
, MAX_PATH
, stringtablebuffer
, files_
.size());
2465 InsertMenu(ignoresubmenu
, indexignoresub
++, MF_BYPOSITION
| MF_STRING
, idCmd
, ignorepath
);
2466 verb
= stdstring(ignorepath
);
2467 myVerbsMap
[verb
] = idCmd
- idCmdFirst
;
2468 myVerbsMap
[verb
] = idCmd
;
2469 myVerbsIDMap
[idCmd
- idCmdFirst
] = verb
;
2470 myVerbsIDMap
[idCmd
] = verb
;
2471 myIDMap
[idCmd
- idCmdFirst
] = ShellMenuIgnoreCaseSensitive
;
2472 myIDMap
[idCmd
++] = ShellMenuIgnoreCaseSensitive
;
2477 if (bShowIgnoreMenu
)
2479 MENUITEMINFO menuiteminfo
;
2480 SecureZeroMemory(&menuiteminfo
, sizeof(menuiteminfo
));
2481 menuiteminfo
.cbSize
= sizeof(menuiteminfo
);
2482 if (fullver
< 0x500 || (fullver
== 0x500 && !(uFlags
&~(CMF_RESERVED
|CMF_EXPLORE
|CMF_EXTENDEDVERBS
))))
2484 menuiteminfo
.fMask
= MIIM_STRING
| MIIM_ID
| MIIM_SUBMENU
| MIIM_DATA
;
2487 HBITMAP bmp
= IconToBitmap(icon
);
2488 menuiteminfo
.fMask
|= MIIM_CHECKMARKS
;
2489 menuiteminfo
.hbmpChecked
= bmp
;
2490 menuiteminfo
.hbmpUnchecked
= bmp
;
2495 menuiteminfo
.fMask
= MIIM_FTYPE
| MIIM_ID
| MIIM_SUBMENU
| MIIM_DATA
| MIIM_STRING
;
2498 menuiteminfo
.fMask
|= MIIM_BITMAP
;
2499 menuiteminfo
.hbmpItem
= (fullver
>= 0x600) ? IconToBitmapPARGB32(icon
) : HBMMENU_CALLBACK
;
2502 menuiteminfo
.fType
= MFT_STRING
;
2503 menuiteminfo
.hSubMenu
= ignoresubmenu
;
2504 menuiteminfo
.wID
= idCmd
;
2505 SecureZeroMemory(stringtablebuffer
, sizeof(stringtablebuffer
));
2506 if (itemStates
& ITEMIS_IGNORED
)
2507 GetMenuTextFromResource(ShellMenuUnIgnoreSub
);
2508 else if (itemStates
& ITEMIS_INSVN
)
2509 GetMenuTextFromResource(ShellMenuDeleteIgnoreSub
);
2511 GetMenuTextFromResource(ShellMenuIgnoreSub
);
2512 menuiteminfo
.dwTypeData
= stringtablebuffer
;
2513 menuiteminfo
.cch
= (UINT
)min(_tcslen(menuiteminfo
.dwTypeData
), UINT_MAX
);
2515 InsertMenuItem((topmenu
& MENUIGNORE
) ? hMenu
: subMenu
, (topmenu
& MENUIGNORE
) ? indexMenu
++ : indexSubMenu
++, TRUE
, &menuiteminfo
);
2516 if (itemStates
& ITEMIS_IGNORED
)
2518 myIDMap
[idCmd
- idCmdFirst
] = ShellMenuUnIgnoreSub
;
2519 myIDMap
[idCmd
++] = ShellMenuUnIgnoreSub
;
2523 myIDMap
[idCmd
- idCmdFirst
] = ShellMenuIgnoreSub
;
2524 myIDMap
[idCmd
++] = ShellMenuIgnoreSub
;
2527 return bShowIgnoreMenu
;
2530 HBITMAP
CShellExt::IconToBitmapPARGB32(UINT uIcon
)
2532 std::map
<UINT
, HBITMAP
>::iterator bitmap_it
= bitmaps
.lower_bound(uIcon
);
2533 if (bitmap_it
!= bitmaps
.end() && bitmap_it
->first
== uIcon
)
2534 return bitmap_it
->second
;
2536 HICON hIcon
= (HICON
)LoadImage(g_hResInst
, MAKEINTRESOURCE(uIcon
), IMAGE_ICON
, 16, 16, LR_DEFAULTCOLOR
);
2540 if (pfnBeginBufferedPaint
== NULL
|| pfnEndBufferedPaint
== NULL
|| pfnGetBufferedPaintBits
== NULL
)
2544 sizIcon
.cx
= GetSystemMetrics(SM_CXSMICON
);
2545 sizIcon
.cy
= GetSystemMetrics(SM_CYSMICON
);
2548 SetRect(&rcIcon
, 0, 0, sizIcon
.cx
, sizIcon
.cy
);
2549 HBITMAP hBmp
= NULL
;
2551 HDC hdcDest
= CreateCompatibleDC(NULL
);
2554 if (SUCCEEDED(Create32BitHBITMAP(hdcDest
, &sizIcon
, NULL
, &hBmp
)))
2556 HBITMAP hbmpOld
= (HBITMAP
)SelectObject(hdcDest
, hBmp
);
2559 BLENDFUNCTION bfAlpha
= { AC_SRC_OVER
, 0, 255, AC_SRC_ALPHA
};
2560 BP_PAINTPARAMS paintParams
= {0};
2561 paintParams
.cbSize
= sizeof(paintParams
);
2562 paintParams
.dwFlags
= BPPF_ERASE
;
2563 paintParams
.pBlendFunction
= &bfAlpha
;
2566 HPAINTBUFFER hPaintBuffer
= pfnBeginBufferedPaint(hdcDest
, &rcIcon
, BPBF_DIB
, &paintParams
, &hdcBuffer
);
2569 if (DrawIconEx(hdcBuffer
, 0, 0, hIcon
, sizIcon
.cx
, sizIcon
.cy
, 0, NULL
, DI_NORMAL
))
2571 // If icon did not have an alpha channel we need to convert buffer to PARGB
2572 ConvertBufferToPARGB32(hPaintBuffer
, hdcDest
, hIcon
, sizIcon
);
2575 // This will write the buffer contents to the destination bitmap
2576 pfnEndBufferedPaint(hPaintBuffer
, TRUE
);
2579 SelectObject(hdcDest
, hbmpOld
);
2589 bitmaps
.insert(bitmap_it
, std::make_pair(uIcon
, hBmp
));
2593 HRESULT
CShellExt::Create32BitHBITMAP(HDC hdc
, const SIZE
*psize
, __deref_opt_out
void **ppvBits
, __out HBITMAP
* phBmp
)
2598 ZeroMemory(&bmi
, sizeof(bmi
));
2599 bmi
.bmiHeader
.biSize
= sizeof(BITMAPINFOHEADER
);
2600 bmi
.bmiHeader
.biPlanes
= 1;
2601 bmi
.bmiHeader
.biCompression
= BI_RGB
;
2603 bmi
.bmiHeader
.biWidth
= psize
->cx
;
2604 bmi
.bmiHeader
.biHeight
= psize
->cy
;
2605 bmi
.bmiHeader
.biBitCount
= 32;
2607 HDC hdcUsed
= hdc
? hdc
: GetDC(NULL
);
2610 *phBmp
= CreateDIBSection(hdcUsed
, &bmi
, DIB_RGB_COLORS
, ppvBits
, NULL
, 0);
2613 ReleaseDC(NULL
, hdcUsed
);
2616 return (NULL
== *phBmp
) ? E_OUTOFMEMORY
: S_OK
;
2619 HRESULT
CShellExt::ConvertBufferToPARGB32(HPAINTBUFFER hPaintBuffer
, HDC hdc
, HICON hicon
, SIZE
& sizIcon
)
2623 HRESULT hr
= pfnGetBufferedPaintBits(hPaintBuffer
, &prgbQuad
, &cxRow
);
2626 ARGB
*pargb
= reinterpret_cast<ARGB
*>(prgbQuad
);
2627 if (!HasAlpha(pargb
, sizIcon
, cxRow
))
2630 if (GetIconInfo(hicon
, &info
))
2634 hr
= ConvertToPARGB32(hdc
, pargb
, info
.hbmMask
, sizIcon
, cxRow
);
2637 DeleteObject(info
.hbmColor
);
2638 DeleteObject(info
.hbmMask
);
2646 bool CShellExt::HasAlpha(__in ARGB
*pargb
, SIZE
& sizImage
, int cxRow
)
2648 ULONG cxDelta
= cxRow
- sizImage
.cx
;
2649 for (ULONG y
= sizImage
.cy
; y
; --y
)
2651 for (ULONG x
= sizImage
.cx
; x
; --x
)
2653 if (*pargb
++ & 0xFF000000)
2665 HRESULT
CShellExt::ConvertToPARGB32(HDC hdc
, __inout ARGB
*pargb
, HBITMAP hbmp
, SIZE
& sizImage
, int cxRow
)
2668 ZeroMemory(&bmi
, sizeof(bmi
));
2669 bmi
.bmiHeader
.biSize
= sizeof(BITMAPINFOHEADER
);
2670 bmi
.bmiHeader
.biPlanes
= 1;
2671 bmi
.bmiHeader
.biCompression
= BI_RGB
;
2673 bmi
.bmiHeader
.biWidth
= sizImage
.cx
;
2674 bmi
.bmiHeader
.biHeight
= sizImage
.cy
;
2675 bmi
.bmiHeader
.biBitCount
= 32;
2677 HRESULT hr
= E_OUTOFMEMORY
;
2678 HANDLE hHeap
= GetProcessHeap();
2679 void *pvBits
= HeapAlloc(hHeap
, 0, bmi
.bmiHeader
.biWidth
* 4 * bmi
.bmiHeader
.biHeight
);
2683 if (GetDIBits(hdc
, hbmp
, 0, bmi
.bmiHeader
.biHeight
, pvBits
, &bmi
, DIB_RGB_COLORS
) == bmi
.bmiHeader
.biHeight
)
2685 ULONG cxDelta
= cxRow
- bmi
.bmiHeader
.biWidth
;
2686 ARGB
*pargbMask
= static_cast<ARGB
*>(pvBits
);
2688 for (ULONG y
= bmi
.bmiHeader
.biHeight
; y
; --y
)
2690 for (ULONG x
= bmi
.bmiHeader
.biWidth
; x
; --x
)
2694 // transparent pixel
2700 *pargb
++ |= 0xFF000000;
2710 HeapFree(hHeap
, 0, pvBits
);