OSDN Git Service

merge original branch.
[tortoisegit/TortoiseGitJp.git] / src / TortoiseShell / ContextMenu.cpp
1 // TortoiseGit - a Windows shell extension for easy version control\r
2 \r
3 // Copyright (C) 2003-2008 - TortoiseGit\r
4 \r
5 // This program is free software; you can redistribute it and/or\r
6 // modify it under the terms of the GNU General Public License\r
7 // as published by the Free Software Foundation; either version 2\r
8 // of the License, or (at your option) any later version.\r
9 \r
10 // This program is distributed in the hope that it will be useful,\r
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of\r
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
13 // GNU General Public License for more details.\r
14 \r
15 // You should have received a copy of the GNU General Public License\r
16 // along with this program; if not, write to the Free Software Foundation,\r
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
18 //\r
19 #include "stdafx.h"\r
20 #include "ShellExt.h"\r
21 #include "ItemIDList.h"\r
22 #include "PreserveChdir.h"\r
23 #include "UnicodeUtils.h"\r
24 //#include "GitProperties.h"\r
25 #include "GitStatus.h"\r
26 #include "TGitPath.h"\r
27 \r
28 #define GetPIDLFolder(pida) (LPCITEMIDLIST)(((LPBYTE)pida)+(pida)->aoffset[0])\r
29 #define GetPIDLItem(pida, i) (LPCITEMIDLIST)(((LPBYTE)pida)+(pida)->aoffset[i+1])\r
30 \r
31 int g_shellidlist=RegisterClipboardFormat(CFSTR_SHELLIDLIST);\r
32 \r
33 extern MenuInfo menuInfo[];\r
34 \r
35 \r
36 STDMETHODIMP CShellExt::Initialize(LPCITEMIDLIST pIDFolder,\r
37                                    LPDATAOBJECT pDataObj,\r
38                                    HKEY /* hRegKey */)\r
39 {\r
40 \r
41         ATLTRACE("Shell :: Initialize\n");\r
42         PreserveChdir preserveChdir;\r
43         files_.clear();\r
44         folder_.erase();\r
45         uuidSource.erase();\r
46         uuidTarget.erase();\r
47         itemStates = 0;\r
48         itemStatesFolder = 0;\r
49         stdstring statuspath;\r
50         git_wc_status_kind fetchedstatus = git_wc_status_none;\r
51         // get selected files/folders\r
52         if (pDataObj)\r
53         {\r
54                 STGMEDIUM medium;\r
55                 FORMATETC fmte = {(CLIPFORMAT)g_shellidlist,\r
56                         (DVTARGETDEVICE FAR *)NULL, \r
57                         DVASPECT_CONTENT, \r
58                         -1, \r
59                         TYMED_HGLOBAL};\r
60                 HRESULT hres = pDataObj->GetData(&fmte, &medium);\r
61 \r
62                 if (SUCCEEDED(hres) && medium.hGlobal)\r
63                 {\r
64                         if (m_State == FileStateDropHandler)\r
65                         {\r
66 \r
67                                 FORMATETC etc = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };\r
68                                 STGMEDIUM stg = { TYMED_HGLOBAL };\r
69                                 if ( FAILED( pDataObj->GetData ( &etc, &stg )))\r
70                                 {\r
71                                         ReleaseStgMedium ( &medium );\r
72                                         return E_INVALIDARG;\r
73                                 }\r
74 \r
75 \r
76                                 HDROP drop = (HDROP)GlobalLock(stg.hGlobal);\r
77                                 if ( NULL == drop )\r
78                                 {\r
79                                         ReleaseStgMedium ( &stg );\r
80                                         ReleaseStgMedium ( &medium );\r
81                                         return E_INVALIDARG;\r
82                                 }\r
83 \r
84                                 int count = DragQueryFile(drop, (UINT)-1, NULL, 0);\r
85                                 if (count == 1)\r
86                                         itemStates |= ITEMIS_ONLYONE;\r
87                                 for (int i = 0; i < count; i++)\r
88                                 {\r
89                                         // find the path length in chars\r
90                                         UINT len = DragQueryFile(drop, i, NULL, 0);\r
91                                         if (len == 0)\r
92                                                 continue;\r
93                                         TCHAR * szFileName = new TCHAR[len+1];\r
94                                         if (0 == DragQueryFile(drop, i, szFileName, len+1))\r
95                                         {\r
96                                                 delete [] szFileName;\r
97                                                 continue;\r
98                                         }\r
99                                         stdstring str = stdstring(szFileName);\r
100                                         delete [] szFileName;\r
101                                         if ((str.empty() == false)&&(g_ShellCache.IsContextPathAllowed(szFileName)))\r
102                                         {\r
103                                                 if (itemStates & ITEMIS_ONLYONE)\r
104                                                 {\r
105                                                         CTGitPath strpath;\r
106                                                         strpath.SetFromWin(str.c_str());\r
107                                                         itemStates |= (strpath.GetFileExtension().CompareNoCase(_T(".diff"))==0) ? ITEMIS_PATCHFILE : 0;\r
108                                                         itemStates |= (strpath.GetFileExtension().CompareNoCase(_T(".patch"))==0) ? ITEMIS_PATCHFILE : 0;\r
109                                                 }\r
110                                                 files_.push_back(str);\r
111                                                 if (i == 0)\r
112                                                 {\r
113                                                         //get the Subversion status of the item\r
114                                                         git_wc_status_kind status = git_wc_status_none;\r
115                                                         CTGitPath askedpath;\r
116                                                         askedpath.SetFromWin(str.c_str());\r
117                                                         try\r
118                                                         {\r
119                                                                 GitStatus stat;\r
120                                                                 stat.GetStatus(CTGitPath(str.c_str()), false, true, true);\r
121                                                                 if (stat.status)\r
122                                                                 {\r
123                                                                         statuspath = str;\r
124                                                                         status = GitStatus::GetMoreImportant(stat.status->text_status, stat.status->prop_status);\r
125                                                                         fetchedstatus = status;\r
126                                                                         //if ((stat.status->entry)&&(stat.status->entry->lock_token))\r
127                                                                         //      itemStates |= (stat.status->entry->lock_token[0] != 0) ? ITEMIS_LOCKED : 0;\r
128                                                                         if ( askedpath.IsDirectory() )//if ((stat.status->entry)&&(stat.status->entry->kind == git_node_dir))\r
129                                                                         {\r
130                                                                                 itemStates |= ITEMIS_FOLDER;\r
131                                                                                 if ((status != git_wc_status_unversioned)&&(status != git_wc_status_ignored)&&(status != git_wc_status_none))\r
132                                                                                         itemStates |= ITEMIS_FOLDERINSVN;\r
133                                                                         }\r
134                                                                         //if ((stat.status->entry)&&(stat.status->entry->present_props))\r
135                                                                         //{\r
136                                                                         //      if (strstr(stat.status->entry->present_props, "svn:needs-lock"))\r
137                                                                         //              itemStates |= ITEMIS_NEEDSLOCK;\r
138                                                                         //}\r
139                                                                         //if ((stat.status->entry)&&(stat.status->entry->uuid))\r
140                                                                         //      uuidSource = CUnicodeUtils::StdGetUnicode(stat.status->entry->uuid);\r
141                                                                 }\r
142                                                                 else\r
143                                                                 {\r
144                                                                         // sometimes, git_client_status() returns with an error.\r
145                                                                         // in that case, we have to check if the working copy is versioned\r
146                                                                         // anyway to show the 'correct' context menu\r
147                                                                         if (askedpath.HasAdminDir())\r
148                                                                                 status = git_wc_status_normal;\r
149                                                                 }\r
150                                                         }\r
151                                                         catch ( ... )\r
152                                                         {\r
153                                                                 ATLTRACE2(_T("Exception in GitStatus::GetStatus()\n"));\r
154                                                         }\r
155 \r
156                                                         // TODO: should we really assume any sub-directory to be versioned\r
157                                                         //       or only if it contains versioned files\r
158                                                         itemStates |= askedpath.GetAdminDirMask();\r
159                                                         \r
160                                                         if ((status == git_wc_status_unversioned) || (status == git_wc_status_ignored) || (status == git_wc_status_none))\r
161                                                                 itemStates &= ~ITEMIS_INSVN;\r
162 \r
163                                                         if (status == git_wc_status_ignored)\r
164                                                                 itemStates |= ITEMIS_IGNORED;\r
165                                                         if (status == git_wc_status_normal)\r
166                                                                 itemStates |= ITEMIS_NORMAL;\r
167                                                         if (status == git_wc_status_conflicted)\r
168                                                                 itemStates |= ITEMIS_CONFLICTED;\r
169                                                         if (status == git_wc_status_added)\r
170                                                                 itemStates |= ITEMIS_ADDED;\r
171                                                         if (status == git_wc_status_deleted)\r
172                                                                 itemStates |= ITEMIS_DELETED;\r
173                                                 }\r
174                                         }\r
175                                 } // for (int i = 0; i < count; i++)\r
176                                 GlobalUnlock ( drop );\r
177                                 ReleaseStgMedium ( &stg );\r
178 \r
179                         } // if (m_State == FileStateDropHandler) \r
180                         else\r
181                         {\r
182 \r
183                                 //Enumerate PIDLs which the user has selected\r
184                                 CIDA* cida = (CIDA*)GlobalLock(medium.hGlobal);\r
185                                 ItemIDList parent( GetPIDLFolder (cida));\r
186 \r
187                                 int count = cida->cidl;\r
188                                 BOOL statfetched = FALSE;\r
189                                 for (int i = 0; i < count; ++i)\r
190                                 {\r
191                                         ItemIDList child (GetPIDLItem (cida, i), &parent);\r
192                                         stdstring str = child.toString();\r
193                                         if ((str.empty() == false)&&(g_ShellCache.IsContextPathAllowed(str.c_str())))\r
194                                         {\r
195                                                 //check if our menu is requested for a subversion admin directory\r
196                                                 if (g_GitAdminDir.IsAdminDirPath(str.c_str()))\r
197                                                         continue;\r
198 \r
199                                                 files_.push_back(str);\r
200                                                 CTGitPath strpath;\r
201                                                 strpath.SetFromWin(str.c_str());\r
202                                                 itemStates |= (strpath.GetFileExtension().CompareNoCase(_T(".diff"))==0) ? ITEMIS_PATCHFILE : 0;\r
203                                                 itemStates |= (strpath.GetFileExtension().CompareNoCase(_T(".patch"))==0) ? ITEMIS_PATCHFILE : 0;\r
204                                                 if (!statfetched)\r
205                                                 {\r
206                                                         //get the Subversion status of the item\r
207                                                         git_wc_status_kind status = git_wc_status_none;\r
208                                                         if ((g_ShellCache.IsSimpleContext())&&(strpath.IsDirectory()))\r
209                                                         {\r
210                                                                 if (strpath.HasAdminDir())\r
211                                                                         status = git_wc_status_normal;\r
212                                                         }\r
213                                                         else\r
214                                                         {\r
215                                                                 try\r
216                                                                 {\r
217                                                                         GitStatus stat;\r
218                                                                         if (strpath.HasAdminDir())\r
219                                                                                 stat.GetStatus(strpath, false, true, true);\r
220                                                                         statuspath = str;\r
221                                                                         if (stat.status)\r
222                                                                         {\r
223                                                                                 status = GitStatus::GetMoreImportant(stat.status->text_status, stat.status->prop_status);\r
224                                                                                 fetchedstatus = status;\r
225                                                                                 //if ((stat.status->entry)&&(stat.status->entry->lock_token))\r
226                                                                                 //      itemStates |= (stat.status->entry->lock_token[0] != 0) ? ITEMIS_LOCKED : 0;\r
227                                                                                 if ( strpath.IsDirectory() )//if ((stat.status->entry)&&(stat.status->entry->kind == git_node_dir))\r
228                                                                                 {\r
229                                                                                         itemStates |= ITEMIS_FOLDER;\r
230                                                                                         if ((status != git_wc_status_unversioned)&&(status != git_wc_status_ignored)&&(status != git_wc_status_none))\r
231                                                                                                 itemStates |= ITEMIS_FOLDERINSVN;\r
232                                                                                 }\r
233                                                                                 // TODO: do we need to check that it's not a dir? does conflict options makes sense for dir in git?\r
234                                                                                 if (status == git_wc_status_conflicted)//if ((stat.status->entry)&&(stat.status->entry->conflict_wrk))\r
235                                                                                         itemStates |= ITEMIS_CONFLICTED;\r
236                                                                                 //if ((stat.status->entry)&&(stat.status->entry->present_props))\r
237                                                                                 //{\r
238                                                                                 //      if (strstr(stat.status->entry->present_props, "svn:needs-lock"))\r
239                                                                                 //              itemStates |= ITEMIS_NEEDSLOCK;\r
240                                                                                 //}\r
241                                                                                 //if ((stat.status->entry)&&(stat.status->entry->uuid))\r
242                                                                                 //      uuidSource = CUnicodeUtils::StdGetUnicode(stat.status->entry->uuid);\r
243                                                                         }       \r
244                                                                         else\r
245                                                                         {\r
246                                                                                 // sometimes, git_client_status() returns with an error.\r
247                                                                                 // in that case, we have to check if the working copy is versioned\r
248                                                                                 // anyway to show the 'correct' context menu\r
249                                                                                 if (strpath.HasAdminDir())\r
250                                                                                 {\r
251                                                                                         status = git_wc_status_normal;\r
252                                                                                         fetchedstatus = status;\r
253                                                                                 }\r
254                                                                         }\r
255                                                                         statfetched = TRUE;\r
256                                                                 }\r
257                                                                 catch ( ... )\r
258                                                                 {\r
259                                                                         ATLTRACE2(_T("Exception in GitStatus::GetStatus()\n"));\r
260                                                                 }\r
261                                                         }\r
262 \r
263                                                         itemStates |= strpath.GetAdminDirMask();                \r
264                                                         \r
265                                                         if ((status == git_wc_status_unversioned)||(status == git_wc_status_ignored)||(status == git_wc_status_none))\r
266                                                                 itemStates &= ~ITEMIS_INSVN;\r
267                                                         if (status == git_wc_status_ignored)\r
268                                                         {\r
269                                                                 itemStates |= ITEMIS_IGNORED;\r
270                                                                 // the item is ignored. Get the svn:ignored properties so we can (maybe) later\r
271                                                                 // offer a 'remove from ignored list' entry\r
272 //                                                              GitProperties props(strpath.GetContainingDirectory(), false);\r
273 //                                                              ignoredprops.empty();\r
274 //                                                              for (int p=0; p<props.GetCount(); ++p)\r
275 //                                                              {\r
276 //                                                                      if (props.GetItemName(p).compare(stdstring(_T("svn:ignore")))==0)\r
277 //                                                                      {\r
278 //                                                                              std::string st = props.GetItemValue(p);\r
279 //                                                                              ignoredprops = MultibyteToWide(st.c_str());\r
280 //                                                                              // remove all escape chars ('\\')\r
281 //                                                                              std::remove(ignoredprops.begin(), ignoredprops.end(), '\\');\r
282 //                                                                              break;\r
283 //                                                                      }\r
284 //                                                              }\r
285                                                         }\r
286                                         \r
287                                                         if (status == git_wc_status_normal)\r
288                                                                 itemStates |= ITEMIS_NORMAL;\r
289                                                         if (status == git_wc_status_conflicted)\r
290                                                                 itemStates |= ITEMIS_CONFLICTED;\r
291                                                         if (status == git_wc_status_added)\r
292                                                                 itemStates |= ITEMIS_ADDED;\r
293                                                         if (status == git_wc_status_deleted)\r
294                                                                 itemStates |= ITEMIS_DELETED;\r
295                                                 }\r
296                                         }\r
297                                 } // for (int i = 0; i < count; ++i)\r
298                                 ItemIDList child (GetPIDLItem (cida, 0), &parent);\r
299                                 if (g_ShellCache.HasSVNAdminDir(child.toString().c_str(), FALSE))\r
300                                         itemStates |= ITEMIS_INVERSIONEDFOLDER;\r
301                                 GlobalUnlock(medium.hGlobal);\r
302 \r
303                                 // if the item is a versioned folder, check if there's a patch file\r
304                                 // in the clipboard to be used in "Apply Patch"\r
305                                 UINT cFormatDiff = RegisterClipboardFormat(_T("TGIT_UNIFIEDDIFF"));\r
306                                 if (cFormatDiff)\r
307                                 {\r
308                                         if (IsClipboardFormatAvailable(cFormatDiff)) \r
309                                                 itemStates |= ITEMIS_PATCHINCLIPBOARD;\r
310                                 }\r
311                                 if (IsClipboardFormatAvailable(CF_HDROP)) \r
312                                         itemStates |= ITEMIS_PATHINCLIPBOARD;\r
313 \r
314                         }\r
315 \r
316                         ReleaseStgMedium ( &medium );\r
317                         if (medium.pUnkForRelease)\r
318                         {\r
319                                 IUnknown* relInterface = (IUnknown*)medium.pUnkForRelease;\r
320                                 relInterface->Release();\r
321                         }\r
322                 }\r
323         }\r
324 \r
325         // get folder background\r
326         if (pIDFolder)\r
327         {\r
328 \r
329                 ItemIDList list(pIDFolder);\r
330                 folder_ = list.toString();\r
331                 git_wc_status_kind status = git_wc_status_none;\r
332                 if (IsClipboardFormatAvailable(CF_HDROP)) \r
333                         itemStatesFolder |= ITEMIS_PATHINCLIPBOARD;\r
334                 \r
335                 CTGitPath askedpath;\r
336                 askedpath.SetFromWin(folder_.c_str());\r
337 \r
338                 if ((folder_.compare(statuspath)!=0)&&(g_ShellCache.IsContextPathAllowed(folder_.c_str())))\r
339                 {\r
340                         \r
341                         try\r
342                         {\r
343                                 GitStatus stat;\r
344                                 stat.GetStatus(CTGitPath(folder_.c_str()), false, true, true);\r
345                                 if (stat.status)\r
346                                 {\r
347                                         status = GitStatus::GetMoreImportant(stat.status->text_status, stat.status->prop_status);\r
348 //                                      if ((stat.status->entry)&&(stat.status->entry->lock_token))\r
349 //                                              itemStatesFolder |= (stat.status->entry->lock_token[0] != 0) ? ITEMIS_LOCKED : 0;\r
350 //                                      if ((stat.status->entry)&&(stat.status->entry->present_props))\r
351 //                                      {\r
352 //                                              if (strstr(stat.status->entry->present_props, "svn:needs-lock"))\r
353 //                                                      itemStatesFolder |= ITEMIS_NEEDSLOCK;\r
354 //                                      }\r
355 //                                      if ((stat.status->entry)&&(stat.status->entry->uuid))\r
356 //                                              uuidTarget = CUnicodeUtils::StdGetUnicode(stat.status->entry->uuid);\r
357                                 \r
358                                 }\r
359                                 else\r
360                                 {\r
361                                         // sometimes, git_client_status() returns with an error.\r
362                                         // in that case, we have to check if the working copy is versioned\r
363                                         // anyway to show the 'correct' context menu\r
364                                         if (askedpath.HasAdminDir())\r
365                                                 status = git_wc_status_normal;\r
366                                 }\r
367                                 \r
368                                 //if ((status != git_wc_status_unversioned)&&(status != git_wc_status_ignored)&&(status != git_wc_status_none))\r
369                                 itemStatesFolder |= askedpath.GetAdminDirMask();                                                        \r
370                                 \r
371                                 if (status == git_wc_status_normal)\r
372                                         itemStatesFolder |= ITEMIS_NORMAL;\r
373                                 if (status == git_wc_status_conflicted)\r
374                                         itemStatesFolder |= ITEMIS_CONFLICTED;\r
375                                 if (status == git_wc_status_added)\r
376                                         itemStatesFolder |= ITEMIS_ADDED;\r
377                                 if (status == git_wc_status_deleted)\r
378                                         itemStatesFolder |= ITEMIS_DELETED;\r
379 \r
380                         }\r
381                         catch ( ... )\r
382                         {\r
383                                 ATLTRACE2(_T("Exception in GitStatus::GetStatus()\n"));\r
384                         }\r
385                 }\r
386                 else\r
387                 {\r
388                         status = fetchedstatus;\r
389                 }\r
390                 //if ((status != git_wc_status_unversioned)&&(status != git_wc_status_ignored)&&(status != git_wc_status_none))\r
391                 itemStatesFolder |= askedpath.GetAdminDirMask();\r
392                 \r
393                 if (status == git_wc_status_ignored)\r
394                         itemStatesFolder |= ITEMIS_IGNORED;\r
395                 itemStatesFolder |= ITEMIS_FOLDER;\r
396                 if (files_.size() == 0)\r
397                         itemStates |= ITEMIS_ONLYONE;\r
398                 if (m_State != FileStateDropHandler)\r
399                         itemStates |= itemStatesFolder;\r
400                 \r
401 \r
402         }\r
403         if (files_.size() == 2)\r
404                 itemStates |= ITEMIS_TWO;\r
405         if ((files_.size() == 1)&&(g_ShellCache.IsContextPathAllowed(files_.front().c_str())))\r
406         {\r
407 \r
408                 itemStates |= ITEMIS_ONLYONE;\r
409                 if (m_State != FileStateDropHandler)\r
410                 {\r
411                         if (PathIsDirectory(files_.front().c_str()))\r
412                         {\r
413                                 folder_ = files_.front();\r
414                                 git_wc_status_kind status = git_wc_status_none;\r
415                                 CTGitPath askedpath;\r
416                                 askedpath.SetFromWin(folder_.c_str());\r
417 \r
418                                 if (folder_.compare(statuspath)!=0)\r
419                                 {                               \r
420                                         try\r
421                                         {\r
422                                                 GitStatus stat;\r
423                                                 stat.GetStatus(CTGitPath(folder_.c_str()), false, true, true);\r
424                                                 if (stat.status)\r
425                                                 {\r
426                                                         status = GitStatus::GetMoreImportant(stat.status->text_status, stat.status->prop_status);\r
427 //                                                      if ((stat.status->entry)&&(stat.status->entry->lock_token))\r
428 //                                                              itemStates |= (stat.status->entry->lock_token[0] != 0) ? ITEMIS_LOCKED : 0;\r
429 //                                                      if ((stat.status->entry)&&(stat.status->entry->present_props))\r
430 //                                                      {\r
431 //                                                              if (strstr(stat.status->entry->present_props, "svn:needs-lock"))\r
432 //                                                                      itemStates |= ITEMIS_NEEDSLOCK;\r
433 //                                                      }\r
434 //                                                      if ((stat.status->entry)&&(stat.status->entry->uuid))\r
435 //                                                              uuidTarget = CUnicodeUtils::StdGetUnicode(stat.status->entry->uuid);\r
436                                                 }\r
437                                         }\r
438                                         catch ( ... )\r
439                                         {\r
440                                                 ATLTRACE2(_T("Exception in GitStatus::GetStatus()\n"));\r
441                                         }\r
442                                 }\r
443                                 else\r
444                                 {\r
445                                         status = fetchedstatus;\r
446                                 }\r
447                                 //if ((status != git_wc_status_unversioned)&&(status != git_wc_status_ignored)&&(status != git_wc_status_none))\r
448                                 itemStates |= askedpath.GetAdminDirMask();\r
449                                 \r
450                                 if (status == git_wc_status_ignored)\r
451                                         itemStates |= ITEMIS_IGNORED;\r
452                                 itemStates |= ITEMIS_FOLDER;\r
453                                 if (status == git_wc_status_added)\r
454                                         itemStates |= ITEMIS_ADDED;\r
455                                 if (status == git_wc_status_deleted)\r
456                                         itemStates |= ITEMIS_DELETED;\r
457 \r
458                         }\r
459                 }\r
460         \r
461         }\r
462         \r
463         return NOERROR;\r
464 }\r
465 \r
466 void CShellExt::InsertGitMenu(BOOL istop, HMENU menu, UINT pos, UINT_PTR id, UINT stringid, UINT icon, UINT idCmdFirst, GitCommands com, UINT uFlags)\r
467 {\r
468         TCHAR menutextbuffer[512] = {0};\r
469         TCHAR verbsbuffer[255] = {0};\r
470         MAKESTRING(stringid);\r
471 \r
472         if (istop)\r
473         {\r
474                 //menu entry for the top context menu, so append an "Git " before\r
475                 //the menu text to indicate where the entry comes from\r
476                 _tcscpy_s(menutextbuffer, 255, _T("Git "));\r
477         }\r
478         _tcscat_s(menutextbuffer, 255, stringtablebuffer);\r
479 #if 1\r
480         // insert branch name into "Git Commit..." entry, so it looks like "Git Commit "master"..."\r
481         // so we have an easy and fast way to check the current branch\r
482         // (the other alternative is using a separate disabled menu entry, the code is already done but commented out)\r
483         if (com == ShellMenuCommit)\r
484         {\r
485                 // get branch name\r
486                 CTGitPath path(folder_.empty() ? files_.front().c_str() : folder_.c_str());\r
487                 CString sProjectRoot;\r
488                 CString sBranchName;\r
489 \r
490                 if (path.HasAdminDir(&sProjectRoot) && !g_Git.GetCurrentBranchFromFile(sProjectRoot, sBranchName))\r
491                 {\r
492                         if (sBranchName.GetLength() == 40)\r
493                         {\r
494                                 // if SHA1 only show 4 first bytes\r
495                                 BOOL bIsSha1 = TRUE;\r
496                                 for (int i=0; i<40; i++)\r
497                                         if ( !iswxdigit(sBranchName[i]) )\r
498                                         {\r
499                                                 bIsSha1 = FALSE;\r
500                                                 break;\r
501                                         }\r
502                                 if (bIsSha1)\r
503                                         sBranchName = sBranchName.Left(8) + _T("....");\r
504                         }\r
505 \r
506                         // sanity check\r
507                         if (sBranchName.GetLength() > 64)\r
508                                 sBranchName = sBranchName.Left(64) + _T("...");\r
509 \r
510                         // scan to before "..."\r
511                         LPTSTR s = menutextbuffer + _tcslen(menutextbuffer)-1;\r
512                         if (s > menutextbuffer)\r
513                         {\r
514                                 while (s > menutextbuffer)\r
515                                 {\r
516                                         if (*s != _T('.'))\r
517                                         {\r
518                                                 s++;\r
519                                                 break;\r
520                                         }\r
521                                         s--;\r
522                                 }\r
523                         }\r
524                         else\r
525                         {\r
526                                 s = menutextbuffer;\r
527                         }\r
528 \r
529                         // append branch name and end with ...\r
530                         _tcscpy(s, _T(" -> \"") + sBranchName + _T("\"..."));\r
531                 }\r
532         }\r
533 #endif\r
534         if ((fullver < 0x500)||(fullver == 0x500 && !(uFlags&~(CMF_RESERVED|CMF_EXPLORE|CMF_EXTENDEDVERBS))))\r
535         {\r
536                 // on win2k, the context menu does not work properly if we use\r
537                 // icon bitmaps. At least the menu text is empty in the context menu\r
538                 // for folder backgrounds (seems like a win2k bug).\r
539                 // the workaround is to use the check/unchecked bitmaps, which are drawn\r
540                 // with AND raster op, but it's better than nothing at all\r
541                 InsertMenu(menu, pos, MF_BYPOSITION | MF_STRING , id, menutextbuffer);\r
542                 if (icon)\r
543                 {\r
544                         HBITMAP bmp = IconToBitmap(icon); \r
545                         SetMenuItemBitmaps(menu, pos, MF_BYPOSITION, bmp, bmp);\r
546                 }\r
547         }\r
548         else\r
549         {\r
550                 MENUITEMINFO menuiteminfo;\r
551                 SecureZeroMemory(&menuiteminfo, sizeof(menuiteminfo));\r
552                 menuiteminfo.cbSize = sizeof(menuiteminfo);\r
553                 menuiteminfo.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING;\r
554                 menuiteminfo.fType = MFT_STRING;\r
555                 menuiteminfo.dwTypeData = menutextbuffer;\r
556                 if (icon)\r
557                 {\r
558                         menuiteminfo.fMask |= MIIM_BITMAP;\r
559                         menuiteminfo.hbmpItem = (fullver >= 0x600) ? IconToBitmapPARGB32(icon) : HBMMENU_CALLBACK;\r
560                 }\r
561                 menuiteminfo.wID = id;\r
562                 InsertMenuItem(menu, pos, TRUE, &menuiteminfo);\r
563         }\r
564 \r
565         if (istop)\r
566         {\r
567                 //menu entry for the top context menu, so append an "Git " before\r
568                 //the menu text to indicate where the entry comes from\r
569                 _tcscpy_s(menutextbuffer, 255, _T("Git "));\r
570         }\r
571         LoadString(g_hResInst, stringid, verbsbuffer, sizeof(verbsbuffer));\r
572         _tcscat_s(menutextbuffer, 255, verbsbuffer);\r
573         stdstring verb = stdstring(menutextbuffer);\r
574         if (verb.find('&') != -1)\r
575         {\r
576                 verb.erase(verb.find('&'),1);\r
577         }\r
578         myVerbsMap[verb] = id - idCmdFirst;\r
579         myVerbsMap[verb] = id;\r
580         myVerbsIDMap[id - idCmdFirst] = verb;\r
581         myVerbsIDMap[id] = verb;\r
582         // We store the relative and absolute diameter\r
583         // (drawitem callback uses absolute, others relative)\r
584         myIDMap[id - idCmdFirst] = com;\r
585         myIDMap[id] = com;\r
586         if (!istop)\r
587                 mySubMenuMap[pos] = com;\r
588 }\r
589 \r
590 HBITMAP CShellExt::IconToBitmap(UINT uIcon)\r
591 {\r
592         std::map<UINT, HBITMAP>::iterator bitmap_it = bitmaps.lower_bound(uIcon);\r
593         if (bitmap_it != bitmaps.end() && bitmap_it->first == uIcon)\r
594                 return bitmap_it->second;\r
595 \r
596         HICON hIcon = (HICON)LoadImage(g_hResInst, MAKEINTRESOURCE(uIcon), IMAGE_ICON, 12, 12, LR_DEFAULTCOLOR);\r
597         if (!hIcon)\r
598                 return NULL;\r
599 \r
600         RECT rect;\r
601 \r
602         rect.right = ::GetSystemMetrics(SM_CXMENUCHECK);\r
603         rect.bottom = ::GetSystemMetrics(SM_CYMENUCHECK);\r
604 \r
605         rect.left = rect.top = 0;\r
606 \r
607         HWND desktop = ::GetDesktopWindow();\r
608         if (desktop == NULL)\r
609         {\r
610                 DestroyIcon(hIcon);\r
611                 return NULL;\r
612         }\r
613 \r
614         HDC screen_dev = ::GetDC(desktop);\r
615         if (screen_dev == NULL)\r
616         {\r
617                 DestroyIcon(hIcon);\r
618                 return NULL;\r
619         }\r
620 \r
621         // Create a compatible DC\r
622         HDC dst_hdc = ::CreateCompatibleDC(screen_dev);\r
623         if (dst_hdc == NULL)\r
624         {\r
625                 DestroyIcon(hIcon);\r
626                 ::ReleaseDC(desktop, screen_dev); \r
627                 return NULL;\r
628         }\r
629 \r
630         // Create a new bitmap of icon size\r
631         HBITMAP bmp = ::CreateCompatibleBitmap(screen_dev, rect.right, rect.bottom);\r
632         if (bmp == NULL)\r
633         {\r
634                 DestroyIcon(hIcon);\r
635                 ::DeleteDC(dst_hdc);\r
636                 ::ReleaseDC(desktop, screen_dev); \r
637                 return NULL;\r
638         }\r
639 \r
640         // Select it into the compatible DC\r
641         HBITMAP old_dst_bmp = (HBITMAP)::SelectObject(dst_hdc, bmp);\r
642         if (old_dst_bmp == NULL)\r
643         {\r
644                 DestroyIcon(hIcon);\r
645                 return NULL;\r
646         }\r
647 \r
648         // Fill the background of the compatible DC with the white color\r
649         // that is taken by menu routines as transparent\r
650         ::SetBkColor(dst_hdc, RGB(255, 255, 255));\r
651         ::ExtTextOut(dst_hdc, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL);\r
652 \r
653         // Draw the icon into the compatible DC\r
654         ::DrawIconEx(dst_hdc, 0, 0, hIcon, rect.right, rect.bottom, 0, NULL, DI_NORMAL);\r
655 \r
656         // Restore settings\r
657         ::SelectObject(dst_hdc, old_dst_bmp);\r
658         ::DeleteDC(dst_hdc);\r
659         ::ReleaseDC(desktop, screen_dev); \r
660         DestroyIcon(hIcon);\r
661         if (bmp)\r
662                 bitmaps.insert(bitmap_it, std::make_pair(uIcon, bmp));\r
663         return bmp;\r
664 }\r
665 \r
666 bool CShellExt::WriteClipboardPathsToTempFile(stdstring& tempfile)\r
667 {\r
668         bool bRet = true;\r
669         tempfile = stdstring();\r
670         //write all selected files and paths to a temporary file\r
671         //for TortoiseProc.exe to read out again.\r
672         DWORD written = 0;\r
673         DWORD pathlength = GetTempPath(0, NULL);\r
674         TCHAR * path = new TCHAR[pathlength+1];\r
675         TCHAR * tempFile = new TCHAR[pathlength + 100];\r
676         GetTempPath (pathlength+1, path);\r
677         GetTempFileName (path, _T("git"), 0, tempFile);\r
678         tempfile = stdstring(tempFile);\r
679 \r
680         HANDLE file = ::CreateFile (tempFile,\r
681                 GENERIC_WRITE, \r
682                 FILE_SHARE_READ, \r
683                 0, \r
684                 CREATE_ALWAYS, \r
685                 FILE_ATTRIBUTE_TEMPORARY,\r
686                 0);\r
687 \r
688         delete [] path;\r
689         delete [] tempFile;\r
690         if (file == INVALID_HANDLE_VALUE)\r
691                 return false;\r
692 \r
693         if (!IsClipboardFormatAvailable(CF_HDROP))\r
694                 return false;\r
695         if (!OpenClipboard(NULL))\r
696                 return false;\r
697 \r
698         stdstring sClipboardText;\r
699         HGLOBAL hglb = GetClipboardData(CF_HDROP);\r
700         HDROP hDrop = (HDROP)GlobalLock(hglb);\r
701         if(hDrop != NULL)\r
702         {\r
703                 TCHAR szFileName[MAX_PATH];\r
704                 UINT cFiles = DragQueryFile(hDrop, 0xFFFFFFFF, NULL, 0); \r
705                 for(UINT i = 0; i < cFiles; ++i)\r
706                 {\r
707                         DragQueryFile(hDrop, i, szFileName, sizeof(szFileName));\r
708                         stdstring filename = szFileName;\r
709                         ::WriteFile (file, filename.c_str(), filename.size()*sizeof(TCHAR), &written, 0);\r
710                         ::WriteFile (file, _T("\n"), 2, &written, 0);\r
711                 }\r
712                 GlobalUnlock(hDrop);\r
713         }\r
714         else bRet = false;\r
715         GlobalUnlock(hglb);\r
716 \r
717         CloseClipboard();\r
718         ::CloseHandle(file);\r
719 \r
720         return bRet;\r
721 }\r
722 \r
723 stdstring CShellExt::WriteFileListToTempFile()\r
724 {\r
725         //write all selected files and paths to a temporary file\r
726         //for TortoiseProc.exe to read out again.\r
727         DWORD pathlength = GetTempPath(0, NULL);\r
728         TCHAR * path = new TCHAR[pathlength+1];\r
729         TCHAR * tempFile = new TCHAR[pathlength + 100];\r
730         GetTempPath (pathlength+1, path);\r
731         GetTempFileName (path, _T("git"), 0, tempFile);\r
732         stdstring retFilePath = stdstring(tempFile);\r
733         \r
734         HANDLE file = ::CreateFile (tempFile,\r
735                                                                 GENERIC_WRITE, \r
736                                                                 FILE_SHARE_READ, \r
737                                                                 0, \r
738                                                                 CREATE_ALWAYS, \r
739                                                                 FILE_ATTRIBUTE_TEMPORARY,\r
740                                                                 0);\r
741 \r
742         delete [] path;\r
743         delete [] tempFile;\r
744         if (file == INVALID_HANDLE_VALUE)\r
745                 return stdstring();\r
746                 \r
747         DWORD written = 0;\r
748         if (files_.empty())\r
749         {\r
750                 ::WriteFile (file, folder_.c_str(), folder_.size()*sizeof(TCHAR), &written, 0);\r
751                 ::WriteFile (file, _T("\n"), 2, &written, 0);\r
752         }\r
753 \r
754         for (std::vector<stdstring>::iterator I = files_.begin(); I != files_.end(); ++I)\r
755         {\r
756                 ::WriteFile (file, I->c_str(), I->size()*sizeof(TCHAR), &written, 0);\r
757                 ::WriteFile (file, _T("\n"), 2, &written, 0);\r
758         }\r
759         ::CloseHandle(file);\r
760         return retFilePath;\r
761 }\r
762 \r
763 STDMETHODIMP CShellExt::QueryDropContext(UINT uFlags, UINT idCmdFirst, HMENU hMenu, UINT &indexMenu)\r
764 {\r
765         PreserveChdir preserveChdir;\r
766         LoadLangDll();\r
767 \r
768         if ((uFlags & CMF_DEFAULTONLY)!=0)\r
769                 return NOERROR;                                 //we don't change the default action\r
770 \r
771         if ((files_.size() == 0)||(folder_.size() == 0))\r
772                 return NOERROR;\r
773 \r
774         if (((uFlags & 0x000f)!=CMF_NORMAL)&&(!(uFlags & CMF_EXPLORE))&&(!(uFlags & CMF_VERBSONLY)))\r
775                 return NOERROR;\r
776 \r
777         bool bSourceAndTargetFromSameRepository = (uuidSource.compare(uuidTarget) == 0) || uuidSource.empty() || uuidTarget.empty();\r
778 \r
779         //the drop handler only has eight commands, but not all are visible at the same time:\r
780         //if the source file(s) are under version control then those files can be moved\r
781         //to the new location or they can be moved with a rename, \r
782         //if they are unversioned then they can be added to the working copy\r
783         //if they are versioned, they also can be exported to an unversioned location\r
784         UINT idCmd = idCmdFirst;\r
785 \r
786         // Git move here\r
787         // available if source is versioned but not added, target is versioned, source and target from same repository or target folder is added\r
788         if ((bSourceAndTargetFromSameRepository||(itemStatesFolder & ITEMIS_ADDED))&&(itemStatesFolder & ITEMIS_FOLDERINSVN)&&((itemStates & ITEMIS_INSVN)&&((~itemStates) & ITEMIS_ADDED)))\r
789                 InsertGitMenu(FALSE, hMenu, indexMenu++, idCmd++, IDS_DROPMOVEMENU, 0, idCmdFirst, ShellMenuDropMove, uFlags);\r
790 \r
791         // Git move and rename here\r
792         // available if source is a single, versioned but not added item, target is versioned, source and target from same repository or target folder is added\r
793         if ((bSourceAndTargetFromSameRepository||(itemStatesFolder & ITEMIS_ADDED))&&(itemStatesFolder & ITEMIS_FOLDERINSVN)&&(itemStates & ITEMIS_INSVN)&&(itemStates & ITEMIS_ONLYONE)&&((~itemStates) & ITEMIS_ADDED))\r
794                 InsertGitMenu(FALSE, hMenu, indexMenu++, idCmd++, IDS_DROPMOVERENAMEMENU, 0, idCmdFirst, ShellMenuDropMoveRename, uFlags);\r
795 \r
796         // Git copy here\r
797         // available if source is versioned but not added, target is versioned, source and target from same repository or target folder is added\r
798         if ((bSourceAndTargetFromSameRepository||(itemStatesFolder & ITEMIS_ADDED))&&(itemStatesFolder & ITEMIS_FOLDERINSVN)&&(itemStates & ITEMIS_INSVN)&&((~itemStates) & ITEMIS_ADDED))\r
799                 InsertGitMenu(FALSE, hMenu, indexMenu++, idCmd++, IDS_DROPCOPYMENU, 0, idCmdFirst, ShellMenuDropCopy, uFlags);\r
800 \r
801         // Git copy and rename here, source and target from same repository\r
802         // available if source is a single, versioned but not added item, target is versioned or target folder is added\r
803         if ((bSourceAndTargetFromSameRepository||(itemStatesFolder & ITEMIS_ADDED))&&(itemStatesFolder & ITEMIS_FOLDERINSVN)&&(itemStates & ITEMIS_INSVN)&&(itemStates & ITEMIS_ONLYONE)&&((~itemStates) & ITEMIS_ADDED))\r
804                 InsertGitMenu(FALSE, hMenu, indexMenu++, idCmd++, IDS_DROPCOPYRENAMEMENU, 0, idCmdFirst, ShellMenuDropCopyRename, uFlags);\r
805 \r
806         // Git add here\r
807         // available if target is versioned and source is either unversioned or from another repository\r
808         if ((itemStatesFolder & ITEMIS_FOLDERINSVN)&&(((~itemStates) & ITEMIS_INSVN)||!bSourceAndTargetFromSameRepository))\r
809                 InsertGitMenu(FALSE, hMenu, indexMenu++, idCmd++, IDS_DROPCOPYADDMENU, 0, idCmdFirst, ShellMenuDropCopyAdd, uFlags);\r
810 \r
811         // Git export here\r
812         // available if source is versioned and a folder\r
813         if ((itemStates & ITEMIS_INSVN)&&(itemStates & ITEMIS_FOLDER))\r
814                 InsertGitMenu(FALSE, hMenu, indexMenu++, idCmd++, IDS_DROPEXPORTMENU, 0, idCmdFirst, ShellMenuDropExport, uFlags);\r
815 \r
816         // Git export all here\r
817         // available if source is versioned and a folder\r
818         if ((itemStates & ITEMIS_INSVN)&&(itemStates & ITEMIS_FOLDER))\r
819                 InsertGitMenu(FALSE, hMenu, indexMenu++, idCmd++, IDS_DROPEXPORTEXTENDEDMENU, 0, idCmdFirst, ShellMenuDropExportExtended, uFlags);\r
820 \r
821         // apply patch\r
822         // available if source is a patchfile\r
823         if (itemStates & ITEMIS_PATCHFILE)\r
824                 InsertGitMenu(FALSE, hMenu, indexMenu++, idCmd++, IDS_MENUAPPLYPATCH, 0, idCmdFirst, ShellMenuApplyPatch, uFlags);\r
825 \r
826         // separator\r
827         if (idCmd != idCmdFirst)\r
828                 InsertMenu(hMenu, indexMenu++, MF_SEPARATOR|MF_BYPOSITION, 0, NULL); \r
829 \r
830         return ResultFromScode(MAKE_SCODE(SEVERITY_SUCCESS, 0, (USHORT)(idCmd - idCmdFirst)));\r
831 }\r
832 \r
833 STDMETHODIMP CShellExt::QueryContextMenu(HMENU hMenu,\r
834                                          UINT indexMenu,\r
835                                          UINT idCmdFirst,\r
836                                          UINT /*idCmdLast*/,\r
837                                          UINT uFlags)\r
838 {\r
839         ATLTRACE("Shell :: QueryContextMenu\n");\r
840         PreserveChdir preserveChdir;\r
841         \r
842         //first check if our drop handler is called\r
843         //and then (if true) provide the context menu for the\r
844         //drop handler\r
845         if (m_State == FileStateDropHandler)\r
846         {\r
847                 return QueryDropContext(uFlags, idCmdFirst, hMenu, indexMenu);\r
848         }\r
849 \r
850         if ((uFlags & CMF_DEFAULTONLY)!=0)\r
851                 return NOERROR;                                 //we don't change the default action\r
852 \r
853         if ((files_.size() == 0)&&(folder_.size() == 0))\r
854                 return NOERROR;\r
855 \r
856         if (((uFlags & 0x000f)!=CMF_NORMAL)&&(!(uFlags & CMF_EXPLORE))&&(!(uFlags & CMF_VERBSONLY)))\r
857                 return NOERROR;\r
858 \r
859         int csidlarray[] = \r
860         {\r
861                 CSIDL_BITBUCKET,\r
862                 CSIDL_CDBURN_AREA,\r
863                 CSIDL_COMMON_FAVORITES,\r
864                 CSIDL_COMMON_STARTMENU,\r
865                 CSIDL_COMPUTERSNEARME,\r
866                 CSIDL_CONNECTIONS,\r
867                 CSIDL_CONTROLS,\r
868                 CSIDL_COOKIES,\r
869                 CSIDL_FAVORITES,\r
870                 CSIDL_FONTS,\r
871                 CSIDL_HISTORY,\r
872                 CSIDL_INTERNET,\r
873                 CSIDL_INTERNET_CACHE,\r
874                 CSIDL_NETHOOD,\r
875                 CSIDL_NETWORK,\r
876                 CSIDL_PRINTERS,\r
877                 CSIDL_PRINTHOOD,\r
878                 CSIDL_RECENT,\r
879                 CSIDL_SENDTO,\r
880                 CSIDL_STARTMENU,\r
881                 0\r
882         };\r
883         if (IsIllegalFolder(folder_, csidlarray))\r
884                 return NOERROR;\r
885 \r
886         if (folder_.empty())\r
887         {\r
888                 // folder is empty, but maybe files are selected\r
889                 if (files_.size() == 0)\r
890                         return NOERROR; // nothing selected - we don't have a menu to show\r
891                 // check whether a selected entry is an UID - those are namespace extensions\r
892                 // which we can't handle\r
893                 for (std::vector<stdstring>::const_iterator it = files_.begin(); it != files_.end(); ++it)\r
894                 {\r
895                         if (_tcsncmp(it->c_str(), _T("::{"), 3)==0)\r
896                                 return NOERROR;\r
897                 }\r
898         }\r
899 \r
900         //check if our menu is requested for a subversion admin directory\r
901         if (g_GitAdminDir.IsAdminDirPath(folder_.c_str()))\r
902                 return NOERROR;\r
903 \r
904         if (uFlags & CMF_EXTENDEDVERBS)\r
905                 itemStates |= ITEMIS_EXTENDED;\r
906 \r
907         const BOOL bShortcut = !!(uFlags & CMF_VERBSONLY);\r
908         if ( bShortcut && (files_.size()==1))\r
909         {\r
910                 // Don't show the context menu for a link if the\r
911                 // destination is not part of a working copy.\r
912                 // It would only show the standard menu items\r
913                 // which are already shown for the lnk-file.\r
914                 CString path = files_.front().c_str();\r
915                 if ( !g_GitAdminDir.HasAdminDir(path) )\r
916                 {\r
917                         return NOERROR;\r
918                 }\r
919         }\r
920 \r
921         //check if we already added our menu entry for a folder.\r
922         //we check that by iterating through all menu entries and check if \r
923         //the dwItemData member points to our global ID string. That string is set\r
924         //by our shell extension when the folder menu is inserted.\r
925         TCHAR menubuf[MAX_PATH];\r
926         int count = GetMenuItemCount(hMenu);\r
927         for (int i=0; i<count; ++i)\r
928         {\r
929                 MENUITEMINFO miif;\r
930                 SecureZeroMemory(&miif, sizeof(MENUITEMINFO));\r
931                 miif.cbSize = sizeof(MENUITEMINFO);\r
932                 miif.fMask = MIIM_DATA;\r
933                 miif.dwTypeData = menubuf;\r
934                 miif.cch = sizeof(menubuf)/sizeof(TCHAR);\r
935                 GetMenuItemInfo(hMenu, i, TRUE, &miif);\r
936                 if (miif.dwItemData == (ULONG_PTR)g_MenuIDString)\r
937                         return NOERROR;\r
938         }\r
939 \r
940         LoadLangDll();\r
941         UINT idCmd = idCmdFirst;\r
942 \r
943         //create the sub menu\r
944         HMENU subMenu = CreateMenu();\r
945         int indexSubMenu = 0;\r
946 \r
947         unsigned __int64 topmenu = g_ShellCache.GetMenuLayout();\r
948         unsigned __int64 menumask = g_ShellCache.GetMenuMask();\r
949         unsigned __int64 menuex = g_ShellCache.GetMenuExt();\r
950 \r
951         int menuIndex = 0;\r
952         bool bAddSeparator = false;\r
953         bool bMenuEntryAdded = false;\r
954         bool bMenuEmpty = true;\r
955         // insert separator at start\r
956         InsertMenu(hMenu, indexMenu++, MF_SEPARATOR|MF_BYPOSITION, 0, NULL); idCmd++;\r
957         bool bShowIcons = !!DWORD(CRegStdWORD(_T("Software\\TortoiseGit\\ShowContextMenuIcons"), TRUE));\r
958         // ?? TSV disabled icons for win2k and earlier, but they work for win2k and should work for win95 and up\r
959         /*if (fullver <= 0x500)\r
960                 bShowIcons = false;*/\r
961 \r
962 #if 0\r
963         if (itemStates & (ITEMIS_INSVN|ITEMIS_FOLDERINSVN))\r
964         {\r
965                 // show current branch name (as a "read-only" menu entry)\r
966 \r
967                 CTGitPath path(folder_.empty() ? files_.front().c_str() : folder_.c_str());\r
968                 CString sProjectRoot;\r
969                 CString sBranchName;\r
970 \r
971                 if (path.HasAdminDir(&sProjectRoot) && !g_Git.GetCurrentBranchFromFile(sProjectRoot, sBranchName))\r
972                 {\r
973                         if (sBranchName.GetLength() == 40)\r
974                         {\r
975                                 // if SHA1 only show 4 first bytes\r
976                                 BOOL bIsSha1 = TRUE;\r
977                                 for (int i=0; i<40; i++)\r
978                                         if ( !iswxdigit(sBranchName[i]) )\r
979                                         {\r
980                                                 bIsSha1 = FALSE;\r
981                                                 break;\r
982                                         }\r
983                                 if (bIsSha1)\r
984                                         sBranchName = sBranchName.Left(8) + _T("....");\r
985                         }\r
986 \r
987                         sBranchName = _T('"') + sBranchName + _T('"');\r
988 \r
989                         const int icon = IDI_COPY;\r
990                         const int pos = indexMenu++;\r
991                         const int id = idCmd++;\r
992 \r
993                         if ((fullver < 0x500)||(fullver == 0x500 && !(uFlags&~(CMF_RESERVED|CMF_EXPLORE|CMF_EXTENDEDVERBS))))\r
994                         {\r
995                                 InsertMenu(hMenu, pos, MF_DISABLED|MF_GRAYED|MF_BYPOSITION|MF_STRING, id, sBranchName);\r
996                                 HBITMAP bmp = IconToBitmap(icon); \r
997                                 SetMenuItemBitmaps(hMenu, pos, MF_BYPOSITION, bmp, bmp);\r
998                         }\r
999                         else\r
1000                         {\r
1001                                 MENUITEMINFO menuiteminfo;\r
1002                                 SecureZeroMemory(&menuiteminfo, sizeof(menuiteminfo));\r
1003                                 menuiteminfo.cbSize = sizeof(menuiteminfo);\r
1004                                 menuiteminfo.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING | MIIM_STATE;\r
1005                                 menuiteminfo.fState = MFS_DISABLED;\r
1006                                 menuiteminfo.fType = MFT_STRING;\r
1007                                 menuiteminfo.dwTypeData = (LPWSTR)sBranchName.GetString();\r
1008                                 if (icon)\r
1009                                 {\r
1010                                         menuiteminfo.fMask |= MIIM_BITMAP;\r
1011                                         menuiteminfo.hbmpItem = (fullver >= 0x600) ? IconToBitmapPARGB32(icon) : HBMMENU_CALLBACK;\r
1012 \r
1013                                         if (menuiteminfo.hbmpItem == HBMMENU_CALLBACK)\r
1014                                         {\r
1015                                                 // WM_DRAWITEM uses myIDMap to get icon, we use the same icon as create branch\r
1016                                                 myIDMap[id - idCmdFirst] = ShellMenuBranch;\r
1017                                                 myIDMap[id] = ShellMenuBranch;\r
1018                                         }\r
1019                                 }\r
1020                                 menuiteminfo.wID = id;\r
1021                                 InsertMenuItem(hMenu, pos, TRUE, &menuiteminfo);\r
1022                         }\r
1023                 }\r
1024         }\r
1025 #endif\r
1026 \r
1027         while (menuInfo[menuIndex].command != ShellMenuLastEntry)\r
1028         {\r
1029                 if (menuInfo[menuIndex].command == ShellSeparator)\r
1030                 {\r
1031                         // we don't add a separator immediately. Because there might not be\r
1032                         // another 'normal' menu entry after we insert a separator.\r
1033                         // we simply set a flag here, indicating that before the next\r
1034                         // 'normal' menu entry, a separator should be added.\r
1035                         if (!bMenuEmpty)\r
1036                                 bAddSeparator = true;\r
1037                 }\r
1038                 else\r
1039                 {\r
1040                         // check the conditions whether to show the menu entry or not\r
1041                         bool bInsertMenu = false;\r
1042 \r
1043                         if (menuInfo[menuIndex].firstyes && menuInfo[menuIndex].firstno)\r
1044                         {\r
1045                                 if (((menuInfo[menuIndex].firstyes & itemStates) == menuInfo[menuIndex].firstyes)\r
1046                                         &&\r
1047                                         ((menuInfo[menuIndex].firstno & (~itemStates)) == menuInfo[menuIndex].firstno))\r
1048                                         bInsertMenu = true;\r
1049                         }\r
1050                         else if ((menuInfo[menuIndex].firstyes)&&((menuInfo[menuIndex].firstyes & itemStates) == menuInfo[menuIndex].firstyes))\r
1051                                 bInsertMenu = true;\r
1052                         else if ((menuInfo[menuIndex].firstno)&&((menuInfo[menuIndex].firstno & (~itemStates)) == menuInfo[menuIndex].firstno))\r
1053                                 bInsertMenu = true;\r
1054 \r
1055                         if (menuInfo[menuIndex].secondyes && menuInfo[menuIndex].secondno)\r
1056                         {\r
1057                                 if (((menuInfo[menuIndex].secondyes & itemStates) == menuInfo[menuIndex].secondyes)\r
1058                                         &&\r
1059                                         ((menuInfo[menuIndex].secondno & (~itemStates)) == menuInfo[menuIndex].secondno))\r
1060                                         bInsertMenu = true;\r
1061                         }\r
1062                         else if ((menuInfo[menuIndex].secondyes)&&((menuInfo[menuIndex].secondyes & itemStates) == menuInfo[menuIndex].secondyes))\r
1063                                 bInsertMenu = true;\r
1064                         else if ((menuInfo[menuIndex].secondno)&&((menuInfo[menuIndex].secondno & (~itemStates)) == menuInfo[menuIndex].secondno))\r
1065                                 bInsertMenu = true;\r
1066 \r
1067                         if (menuInfo[menuIndex].thirdyes && menuInfo[menuIndex].thirdno)\r
1068                         {\r
1069                                 if (((menuInfo[menuIndex].thirdyes & itemStates) == menuInfo[menuIndex].thirdyes)\r
1070                                         &&\r
1071                                         ((menuInfo[menuIndex].thirdno & (~itemStates)) == menuInfo[menuIndex].thirdno))\r
1072                                         bInsertMenu = true;\r
1073                         }\r
1074                         else if ((menuInfo[menuIndex].thirdyes)&&((menuInfo[menuIndex].thirdyes & itemStates) == menuInfo[menuIndex].thirdyes))\r
1075                                 bInsertMenu = true;\r
1076                         else if ((menuInfo[menuIndex].thirdno)&&((menuInfo[menuIndex].thirdno & (~itemStates)) == menuInfo[menuIndex].thirdno))\r
1077                                 bInsertMenu = true;\r
1078 \r
1079                         if (menuInfo[menuIndex].fourthyes && menuInfo[menuIndex].fourthno)\r
1080                         {\r
1081                                 if (((menuInfo[menuIndex].fourthyes & itemStates) == menuInfo[menuIndex].fourthyes)\r
1082                                         &&\r
1083                                         ((menuInfo[menuIndex].fourthno & (~itemStates)) == menuInfo[menuIndex].fourthno))\r
1084                                         bInsertMenu = true;\r
1085                         }\r
1086                         else if ((menuInfo[menuIndex].fourthyes)&&((menuInfo[menuIndex].fourthyes & itemStates) == menuInfo[menuIndex].fourthyes))\r
1087                                 bInsertMenu = true;\r
1088                         else if ((menuInfo[menuIndex].fourthno)&&((menuInfo[menuIndex].fourthno & (~itemStates)) == menuInfo[menuIndex].fourthno))\r
1089                                 bInsertMenu = true;\r
1090 \r
1091                         if (menuInfo[menuIndex].menuID & menuex)\r
1092                         {\r
1093                                 if( !(itemStates & ITEMIS_EXTENDED) )\r
1094                                 {\r
1095                                         bInsertMenu = false;\r
1096                                 }\r
1097                         }\r
1098 \r
1099                         if (menuInfo[menuIndex].menuID & (~menumask))\r
1100                         {\r
1101                                 if (bInsertMenu)\r
1102                                 {\r
1103                                         bool bIsTop = ((topmenu & menuInfo[menuIndex].menuID) != 0);\r
1104                                         // insert a separator\r
1105                                         if ((bMenuEntryAdded)&&(bAddSeparator)&&(!bIsTop))\r
1106                                         {\r
1107                                                 bAddSeparator = false;\r
1108                                                 bMenuEntryAdded = false;\r
1109                                                 InsertMenu(subMenu, indexSubMenu++, MF_SEPARATOR|MF_BYPOSITION, 0, NULL); \r
1110                                                 idCmd++;\r
1111                                         }\r
1112                                         \r
1113                                         // handle special cases (sub menus)\r
1114                                         if ((menuInfo[menuIndex].command == ShellMenuIgnoreSub)||(menuInfo[menuIndex].command == ShellMenuUnIgnoreSub))\r
1115                                         {\r
1116                                                 InsertIgnoreSubmenus(idCmd, idCmdFirst, hMenu, subMenu, indexMenu, indexSubMenu, topmenu, bShowIcons, uFlags);\r
1117                                                 bMenuEntryAdded = true;\r
1118                                                 bMenuEmpty = false;\r
1119                                         }\r
1120                                         else\r
1121                                         {\r
1122                                                 bool bIsTop = ((topmenu & menuInfo[menuIndex].menuID) != 0);\r
1123 \r
1124                                                 // insert the menu entry\r
1125                                                 InsertGitMenu(  bIsTop,\r
1126                                                                                 bIsTop ? hMenu : subMenu,\r
1127                                                                                 bIsTop ? indexMenu++ : indexSubMenu++,\r
1128                                                                                 idCmd++,\r
1129                                                                                 menuInfo[menuIndex].menuTextID,\r
1130                                                                                 bShowIcons ? menuInfo[menuIndex].iconID : 0,\r
1131                                                                                 idCmdFirst,\r
1132                                                                                 menuInfo[menuIndex].command,\r
1133                                                                                 uFlags);\r
1134                                                 if (!bIsTop)\r
1135                                                 {\r
1136                                                         bMenuEntryAdded = true;\r
1137                                                         bMenuEmpty = false;\r
1138                                                 }\r
1139                                         }\r
1140                                 }\r
1141                         }\r
1142                 }\r
1143                 menuIndex++;\r
1144         }\r
1145 \r
1146         //add sub menu to main context menu\r
1147         //don't use InsertMenu because this will lead to multiple menu entries in the explorer file menu.\r
1148         //see http://support.microsoft.com/default.aspx?scid=kb;en-us;214477 for details of that.\r
1149         MAKESTRING(IDS_MENUSUBMENU);\r
1150         MENUITEMINFO menuiteminfo;\r
1151         SecureZeroMemory(&menuiteminfo, sizeof(menuiteminfo));\r
1152         menuiteminfo.cbSize = sizeof(menuiteminfo);\r
1153         menuiteminfo.fType = MFT_STRING;\r
1154         menuiteminfo.dwTypeData = stringtablebuffer;\r
1155 \r
1156         UINT uIcon = bShowIcons ? IDI_APP : 0;\r
1157         if (folder_.size())\r
1158         {\r
1159                 uIcon = bShowIcons ? IDI_MENUFOLDER : 0;\r
1160                 myIDMap[idCmd - idCmdFirst] = ShellSubMenuFolder;\r
1161                 myIDMap[idCmd] = ShellSubMenuFolder;\r
1162                 menuiteminfo.dwItemData = (ULONG_PTR)g_MenuIDString;\r
1163         }\r
1164         else if (!bShortcut && (files_.size()==1))\r
1165         {\r
1166                 uIcon = bShowIcons ? IDI_MENUFILE : 0;\r
1167                 myIDMap[idCmd - idCmdFirst] = ShellSubMenuFile;\r
1168                 myIDMap[idCmd] = ShellSubMenuFile;\r
1169         }\r
1170         else if (bShortcut && (files_.size()==1))\r
1171         {\r
1172                 uIcon = bShowIcons ? IDI_MENULINK : 0;\r
1173                 myIDMap[idCmd - idCmdFirst] = ShellSubMenuLink;\r
1174                 myIDMap[idCmd] = ShellSubMenuLink;\r
1175         }\r
1176         else if (files_.size() > 1)\r
1177         {\r
1178                 uIcon = bShowIcons ? IDI_MENUMULTIPLE : 0;\r
1179                 myIDMap[idCmd - idCmdFirst] = ShellSubMenuMultiple;\r
1180                 myIDMap[idCmd] = ShellSubMenuMultiple;\r
1181         }\r
1182         else\r
1183         {\r
1184                 myIDMap[idCmd - idCmdFirst] = ShellSubMenu;\r
1185                 myIDMap[idCmd] = ShellSubMenu;\r
1186         }\r
1187         HBITMAP bmp = NULL;\r
1188         if ((fullver < 0x500)||(fullver == 0x500 && !(uFlags&~(CMF_RESERVED|CMF_EXPLORE|CMF_EXTENDEDVERBS))))\r
1189         {\r
1190                 menuiteminfo.fMask = MIIM_STRING | MIIM_ID | MIIM_SUBMENU | MIIM_DATA;\r
1191                 if (uIcon)\r
1192                 {\r
1193                         menuiteminfo.fMask |= MIIM_CHECKMARKS;\r
1194                         bmp = IconToBitmap(uIcon);\r
1195                         menuiteminfo.hbmpChecked = bmp;\r
1196                         menuiteminfo.hbmpUnchecked = bmp;\r
1197                 }\r
1198         }\r
1199         else\r
1200         {\r
1201                 menuiteminfo.fMask = MIIM_FTYPE | MIIM_ID | MIIM_SUBMENU | MIIM_DATA | MIIM_STRING;\r
1202                 if (uIcon)\r
1203                 {\r
1204                         menuiteminfo.fMask |= MIIM_BITMAP;\r
1205                         menuiteminfo.hbmpItem = (fullver >= 0x600) ? IconToBitmapPARGB32(uIcon) : HBMMENU_CALLBACK;\r
1206                 }\r
1207         }\r
1208         menuiteminfo.hSubMenu = subMenu;\r
1209         menuiteminfo.wID = idCmd++;\r
1210         InsertMenuItem(hMenu, indexMenu++, TRUE, &menuiteminfo);\r
1211 \r
1212         //separator after\r
1213         InsertMenu(hMenu, indexMenu++, MF_SEPARATOR|MF_BYPOSITION, 0, NULL); idCmd++;\r
1214 \r
1215         //return number of menu items added\r
1216         return ResultFromScode(MAKE_SCODE(SEVERITY_SUCCESS, 0, (USHORT)(idCmd - idCmdFirst)));\r
1217 }\r
1218 \r
1219 \r
1220 // This is called when you invoke a command on the menu:\r
1221 STDMETHODIMP CShellExt::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi)\r
1222 {\r
1223         PreserveChdir preserveChdir;\r
1224         HRESULT hr = E_INVALIDARG;\r
1225         if (lpcmi == NULL)\r
1226                 return hr;\r
1227 \r
1228         std::string command;\r
1229         std::string parent;\r
1230         std::string file;\r
1231 \r
1232         if ((files_.size() > 0)||(folder_.size() > 0))\r
1233         {\r
1234                 UINT idCmd = LOWORD(lpcmi->lpVerb);\r
1235 \r
1236                 if (HIWORD(lpcmi->lpVerb))\r
1237                 {\r
1238                         stdstring verb = stdstring(MultibyteToWide(lpcmi->lpVerb));\r
1239                         std::map<stdstring, UINT_PTR>::const_iterator verb_it = myVerbsMap.lower_bound(verb);\r
1240                         if (verb_it != myVerbsMap.end() && verb_it->first == verb)\r
1241                                 idCmd = verb_it->second;\r
1242                         else\r
1243                                 return hr;\r
1244                 }\r
1245 \r
1246                 // See if we have a handler interface for this id\r
1247                 std::map<UINT_PTR, UINT_PTR>::const_iterator id_it = myIDMap.lower_bound(idCmd);\r
1248                 if (id_it != myIDMap.end() && id_it->first == idCmd)\r
1249                 {\r
1250                         STARTUPINFO startup;\r
1251                         PROCESS_INFORMATION process;\r
1252                         memset(&startup, 0, sizeof(startup));\r
1253                         startup.cb = sizeof(startup);\r
1254                         memset(&process, 0, sizeof(process));\r
1255                         CRegStdString tortoiseProcPath(_T("Software\\TortoiseGit\\ProcPath"), _T("TortoiseProc.exe"), false, HKEY_LOCAL_MACHINE);\r
1256                         CRegStdString tortoiseMergePath(_T("Software\\TortoiseGit\\TMergePath"), _T("TortoiseMerge.exe"), false, HKEY_LOCAL_MACHINE);\r
1257 \r
1258                         //TortoiseProc expects a command line of the form:\r
1259                         //"/command:<commandname> /pathfile:<path> /startrev:<startrevision> /endrev:<endrevision> /deletepathfile\r
1260                         // or\r
1261                         //"/command:<commandname> /path:<path> /startrev:<startrevision> /endrev:<endrevision>\r
1262                         //\r
1263                         //* path is a path to a single file/directory for commands which only act on single items (log, checkout, ...)\r
1264                         //* pathfile is a path to a temporary file which contains a list of file paths\r
1265                         stdstring svnCmd = _T(" /command:");\r
1266                         stdstring tempfile;\r
1267                         switch (id_it->second)\r
1268                         {\r
1269                                 //#region case\r
1270                         case ShellMenuSync:\r
1271                                 svnCmd += _T("sync /path:\"");\r
1272                                 svnCmd += folder_;\r
1273                                 svnCmd += _T("\"");\r
1274                                 break;\r
1275                         case ShellMenuCheckout:\r
1276                                 svnCmd += _T("checkout /path:\"");\r
1277                                 svnCmd += folder_;\r
1278                                 svnCmd += _T("\"");\r
1279                                 break;\r
1280                         case ShellMenuUpdate:\r
1281                                 tempfile = WriteFileListToTempFile();\r
1282                                 svnCmd += _T("update /pathfile:\"");\r
1283                                 svnCmd += tempfile;\r
1284                                 svnCmd += _T("\"");\r
1285                                 svnCmd += _T(" /deletepathfile");\r
1286                                 break;\r
1287                         case ShellMenuSubSync:\r
1288                                 tempfile = WriteFileListToTempFile();\r
1289                                 svnCmd += _T("subsync /pathfile:\"");\r
1290                                 svnCmd += tempfile;\r
1291                                 svnCmd += _T("\"");\r
1292                                 svnCmd += _T(" /deletepathfile");\r
1293                                 if(itemStatesFolder&ITEMIS_SUBMODULE)\r
1294                                 {\r
1295                                         svnCmd += _T(" /bkpath:\"");\r
1296                                         svnCmd += folder_;\r
1297                                         svnCmd += _T("\"");\r
1298                                 }\r
1299                                 break;\r
1300                         case ShellMenuUpdateExt:\r
1301                                 tempfile = WriteFileListToTempFile();\r
1302                                 svnCmd += _T("subupdate /pathfile:\"");\r
1303                                 svnCmd += tempfile;\r
1304                                 svnCmd += _T("\"");\r
1305                                 svnCmd += _T(" /deletepathfile");\r
1306                                 if(itemStatesFolder&ITEMIS_SUBMODULE)\r
1307                                 {\r
1308                                         svnCmd += _T(" /bkpath:\"");\r
1309                                         svnCmd += folder_;\r
1310                                         svnCmd += _T("\"");\r
1311                                 }\r
1312                                 break;\r
1313                         case ShellMenuCommit:\r
1314                                 tempfile = WriteFileListToTempFile();\r
1315                                 svnCmd += _T("commit /pathfile:\"");\r
1316                                 svnCmd += tempfile;\r
1317                                 svnCmd += _T("\"");\r
1318                                 svnCmd += _T(" /deletepathfile");\r
1319                                 break;\r
1320                         case ShellMenuAdd:\r
1321                         case ShellMenuAddAsReplacement:\r
1322                                 tempfile = WriteFileListToTempFile();\r
1323                                 svnCmd += _T("add /pathfile:\"");\r
1324                                 svnCmd += tempfile;\r
1325                                 svnCmd += _T("\"");\r
1326                                 svnCmd += _T(" /deletepathfile");\r
1327                                 break;\r
1328                         case ShellMenuIgnore:\r
1329                                 tempfile = WriteFileListToTempFile();\r
1330                                 svnCmd += _T("ignore /pathfile:\"");\r
1331                                 svnCmd += tempfile;\r
1332                                 svnCmd += _T("\"");\r
1333                                 svnCmd += _T(" /deletepathfile");\r
1334                                 break;\r
1335                         case ShellMenuIgnoreCaseSensitive:\r
1336                                 tempfile = WriteFileListToTempFile();\r
1337                                 svnCmd += _T("ignore /pathfile:\"");\r
1338                                 svnCmd += tempfile;\r
1339                                 svnCmd += _T("\"");\r
1340                                 svnCmd += _T(" /deletepathfile");\r
1341                                 svnCmd += _T(" /onlymask");\r
1342                                 break;\r
1343                         case ShellMenuUnIgnore:\r
1344                                 tempfile = WriteFileListToTempFile();\r
1345                                 svnCmd += _T("unignore /pathfile:\"");\r
1346                                 svnCmd += tempfile;\r
1347                                 svnCmd += _T("\"");\r
1348                                 svnCmd += _T(" /deletepathfile");\r
1349                                 break;\r
1350                         case ShellMenuUnIgnoreCaseSensitive:\r
1351                                 tempfile = WriteFileListToTempFile();\r
1352                                 svnCmd += _T("unignore /pathfile:\"");\r
1353                                 svnCmd += tempfile;\r
1354                                 svnCmd += _T("\"");\r
1355                                 svnCmd += _T(" /deletepathfile");\r
1356                                 svnCmd += _T(" /onlymask");\r
1357                                 break;\r
1358                         case ShellMenuRevert:\r
1359                                 tempfile = WriteFileListToTempFile();\r
1360                                 svnCmd += _T("revert /pathfile:\"");\r
1361                                 svnCmd += tempfile;\r
1362                                 svnCmd += _T("\"");\r
1363                                 svnCmd += _T(" /deletepathfile");\r
1364                                 break;\r
1365                         case ShellMenuDelUnversioned:\r
1366                                 svnCmd += _T("delunversioned /path:\"");\r
1367                                 svnCmd += folder_;\r
1368                                 svnCmd += _T("\"");\r
1369                                 break;\r
1370                         case ShellMenuCleanup:\r
1371                                 tempfile = WriteFileListToTempFile();\r
1372                                 svnCmd += _T("cleanup /pathfile:\"");\r
1373                                 svnCmd += tempfile;\r
1374                                 svnCmd += _T("\"");\r
1375                                 svnCmd += _T(" /deletepathfile");\r
1376                                 break;\r
1377                         case ShellMenuSendMail:\r
1378                                 tempfile = WriteFileListToTempFile();\r
1379                                 svnCmd += _T("sendmail /pathfile:\"");\r
1380                                 svnCmd += tempfile;\r
1381                                 svnCmd += _T("\"");\r
1382                                 svnCmd += _T(" /deletepathfile");\r
1383                                 break;\r
1384                         case ShellMenuResolve:\r
1385                                 tempfile = WriteFileListToTempFile();\r
1386                                 svnCmd += _T("resolve /pathfile:\"");\r
1387                                 svnCmd += tempfile;\r
1388                                 svnCmd += _T("\"");\r
1389                                 svnCmd += _T(" /deletepathfile");\r
1390                                 break;\r
1391                         case ShellMenuSwitch:\r
1392                                 svnCmd += _T("switch /path:\"");\r
1393                                 if (files_.size() > 0)\r
1394                                         svnCmd += files_.front();\r
1395                                 else\r
1396                                         svnCmd += folder_;\r
1397                                 svnCmd += _T("\"");\r
1398                                 break;\r
1399                         case ShellMenuImport:\r
1400                                 svnCmd += _T("import /path:\"");\r
1401                                 svnCmd += folder_;\r
1402                                 svnCmd += _T("\"");\r
1403                                 break;\r
1404                         case ShellMenuExport:\r
1405                                 svnCmd += _T("export /path:\"");\r
1406                                 svnCmd += folder_;\r
1407                                 svnCmd += _T("\"");\r
1408                                 break;\r
1409                         case ShellMenuAbout:\r
1410                                 svnCmd += _T("about");\r
1411                                 break;\r
1412                         case ShellMenuCreateRepos:\r
1413                                 svnCmd += _T("repocreate /path:\"");\r
1414                                 svnCmd += folder_;\r
1415                                 svnCmd += _T("\"");\r
1416                                 break;\r
1417                         case ShellMenuMerge:\r
1418                                 svnCmd += _T("merge /path:\"");\r
1419                                 if (files_.size() > 0)\r
1420                                         svnCmd += files_.front();\r
1421                                 else\r
1422                                         svnCmd += folder_;\r
1423                                 svnCmd += _T("\"");\r
1424                                 break;\r
1425                         case ShellMenuMergeAll:\r
1426                                 svnCmd += _T("mergeall /path:\"");\r
1427                                 if (files_.size() > 0)\r
1428                                         svnCmd += files_.front();\r
1429                                 else\r
1430                                         svnCmd += folder_;\r
1431                                 svnCmd += _T("\"");\r
1432                                 break;\r
1433                         case ShellMenuCopy:\r
1434                                 svnCmd += _T("copy /path:\"");\r
1435                                 if (files_.size() > 0)\r
1436                                         svnCmd += files_.front();\r
1437                                 else\r
1438                                         svnCmd += folder_;\r
1439                                 svnCmd += _T("\"");\r
1440                                 break;\r
1441                         case ShellMenuSettings:\r
1442                                 svnCmd += _T("settings /path:\"");\r
1443                                 if (files_.size() > 0)\r
1444                                         svnCmd += files_.front();\r
1445                                 else\r
1446                                         svnCmd += folder_;\r
1447                                 svnCmd += _T("\"");\r
1448                                 break;\r
1449                         case ShellMenuHelp:\r
1450                                 svnCmd += _T("help");\r
1451                                 break;\r
1452                         case ShellMenuRename:\r
1453                                 svnCmd += _T("rename /path:\"");\r
1454                                 if (files_.size() > 0)\r
1455                                         svnCmd += files_.front();\r
1456                                 else\r
1457                                         svnCmd += folder_;\r
1458                                 svnCmd += _T("\"");\r
1459                                 break;\r
1460                         case ShellMenuRemove:\r
1461                                 tempfile = WriteFileListToTempFile();\r
1462                                 svnCmd += _T("remove /pathfile:\"");\r
1463                                 svnCmd += tempfile;\r
1464                                 svnCmd += _T("\"");\r
1465                                 svnCmd += _T(" /deletepathfile");\r
1466                                 break;\r
1467                         case ShellMenuRemoveKeep:\r
1468                                 tempfile = WriteFileListToTempFile();\r
1469                                 svnCmd += _T("remove /pathfile:\"");\r
1470                                 svnCmd += tempfile;\r
1471                                 svnCmd += _T("\"");\r
1472                                 svnCmd += _T(" /deletepathfile");\r
1473                                 svnCmd += _T(" /keep");\r
1474                                 break;\r
1475                         case ShellMenuDiff:\r
1476                                 svnCmd += _T("diff /path:\"");\r
1477                                 if (files_.size() == 1)\r
1478                                         svnCmd += files_.front();\r
1479                                 else if (files_.size() == 2)\r
1480                                 {\r
1481                                         std::vector<stdstring>::iterator I = files_.begin();\r
1482                                         svnCmd += *I;\r
1483                                         I++;\r
1484                                         svnCmd += _T("\" /path2:\"");\r
1485                                         svnCmd += *I;\r
1486                                 }\r
1487                                 else\r
1488                                         svnCmd += folder_;\r
1489                                 svnCmd += _T("\"");\r
1490                                 if (GetAsyncKeyState(VK_SHIFT) & 0x8000)\r
1491                                         svnCmd += _T(" /alternative");\r
1492                                 break;\r
1493                         case ShellMenuPrevDiff:\r
1494                                 svnCmd += _T("prevdiff /path:\"");\r
1495                                 if (files_.size() == 1)\r
1496                                         svnCmd += files_.front();\r
1497                                 else\r
1498                                         svnCmd += folder_;\r
1499                                 svnCmd += _T("\"");\r
1500                                 if (GetAsyncKeyState(VK_SHIFT) & 0x8000)\r
1501                                         svnCmd += _T(" /alternative");\r
1502                                 break;\r
1503                         case ShellMenuUrlDiff:\r
1504                                 svnCmd += _T("urldiff /path:\"");\r
1505                                 if (files_.size() == 1)\r
1506                                         svnCmd += files_.front();\r
1507                                 else\r
1508                                         svnCmd += folder_;\r
1509                                 svnCmd += _T("\"");\r
1510                                 break;\r
1511                         case ShellMenuDiffTwo:\r
1512                                 svnCmd += _T("diffcommits /path:\"");\r
1513                                 if (files_.size() == 1)\r
1514                                         svnCmd += files_.front();\r
1515                                 else\r
1516                                         svnCmd += folder_;\r
1517                                 svnCmd += _T("\"");\r
1518                                 break;\r
1519                         case ShellMenuDropCopyAdd:\r
1520                                 tempfile = WriteFileListToTempFile();\r
1521                                 svnCmd += _T("dropcopyadd /pathfile:\"");\r
1522                                 svnCmd += tempfile;\r
1523                                 svnCmd += _T("\"");\r
1524                                 svnCmd += _T(" /deletepathfile");\r
1525                                 svnCmd += _T(" /droptarget:\"");\r
1526                                 svnCmd += folder_;\r
1527                                 svnCmd += _T("\"";)\r
1528                                         break;\r
1529                         case ShellMenuDropCopy:\r
1530                                 tempfile = WriteFileListToTempFile();\r
1531                                 svnCmd += _T("dropcopy /pathfile:\"");\r
1532                                 svnCmd += tempfile;\r
1533                                 svnCmd += _T("\"");\r
1534                                 svnCmd += _T(" /deletepathfile");\r
1535                                 svnCmd += _T(" /droptarget:\"");\r
1536                                 svnCmd += folder_;\r
1537                                 svnCmd += _T("\"";)\r
1538                                         break;\r
1539                         case ShellMenuDropCopyRename:\r
1540                                 tempfile = WriteFileListToTempFile();\r
1541                                 svnCmd += _T("dropcopy /pathfile:\"");\r
1542                                 svnCmd += tempfile;\r
1543                                 svnCmd += _T("\"");\r
1544                                 svnCmd += _T(" /deletepathfile");\r
1545                                 svnCmd += _T(" /droptarget:\"");\r
1546                                 svnCmd += folder_;\r
1547                                 svnCmd += _T("\" /rename";)\r
1548                                         break;\r
1549                         case ShellMenuDropMove:\r
1550                                 tempfile = WriteFileListToTempFile();\r
1551                                 svnCmd += _T("dropmove /pathfile:\"");\r
1552                                 svnCmd += tempfile;\r
1553                                 svnCmd += _T("\"");\r
1554                                 svnCmd += _T(" /deletepathfile");\r
1555                                 svnCmd += _T(" /droptarget:\"");\r
1556                                 svnCmd += folder_;\r
1557                                 svnCmd += _T("\"");\r
1558                                 break;\r
1559                         case ShellMenuDropMoveRename:\r
1560                                 tempfile = WriteFileListToTempFile();\r
1561                                 svnCmd += _T("dropmove /pathfile:\"");\r
1562                                 svnCmd += tempfile;\r
1563                                 svnCmd += _T("\"");\r
1564                                 svnCmd += _T(" /deletepathfile");\r
1565                                 svnCmd += _T(" /droptarget:\"");\r
1566                                 svnCmd += folder_;\r
1567                                 svnCmd += _T("\" /rename";)\r
1568                                 break;\r
1569                         case ShellMenuDropExport:\r
1570                                 tempfile = WriteFileListToTempFile();\r
1571                                 svnCmd += _T("dropexport /pathfile:\"");\r
1572                                 svnCmd += tempfile;\r
1573                                 svnCmd += _T("\"");\r
1574                                 svnCmd += _T(" /deletepathfile");\r
1575                                 svnCmd += _T(" /droptarget:\"");\r
1576                                 svnCmd += folder_;\r
1577                                 svnCmd += _T("\"");\r
1578                                 break;\r
1579                         case ShellMenuDropExportExtended:\r
1580                                 tempfile = WriteFileListToTempFile();\r
1581                                 svnCmd += _T("dropexport /pathfile:\"");\r
1582                                 svnCmd += tempfile;\r
1583                                 svnCmd += _T("\"");\r
1584                                 svnCmd += _T(" /deletepathfile");\r
1585                                 svnCmd += _T(" /droptarget:\"");\r
1586                                 svnCmd += folder_;\r
1587                                 svnCmd += _T("\"");\r
1588                                 svnCmd += _T(" /extended");\r
1589                                 break;\r
1590                         case ShellMenuLog:\r
1591                                 svnCmd += _T("log /path:\"");\r
1592                                 if (files_.size() > 0)\r
1593                                         svnCmd += files_.front();\r
1594                                 else\r
1595                                         svnCmd += folder_;\r
1596                                 svnCmd += _T("\"");\r
1597                                 break;\r
1598                         case ShellMenuConflictEditor:\r
1599                                 svnCmd += _T("conflicteditor /path:\"");\r
1600                                 if (files_.size() > 0)\r
1601                                         svnCmd += files_.front();\r
1602                                 else\r
1603                                         svnCmd += folder_;\r
1604                                 svnCmd += _T("\"");\r
1605                                 break;\r
1606                         case ShellMenuRelocate:\r
1607                                 svnCmd += _T("relocate /path:\"");\r
1608                                 if (files_.size() > 0)\r
1609                                         svnCmd += files_.front();\r
1610                                 else\r
1611                                         svnCmd += folder_;\r
1612                                 svnCmd += _T("\"");\r
1613                                 break;\r
1614                         case ShellMenuGitSVNRebase:\r
1615                                 svnCmd += _T("svnrebase /path:\"");\r
1616                                 if (files_.size() > 0)\r
1617                                         svnCmd += files_.front();\r
1618                                 else\r
1619                                         svnCmd += folder_;\r
1620                                 svnCmd += _T("\"");\r
1621                                 break;\r
1622                         case ShellMenuGitSVNDCommit:\r
1623                                 svnCmd += _T("svndcommit /path:\"");\r
1624                                 if (files_.size() > 0)\r
1625                                         svnCmd += files_.front();\r
1626                                 else\r
1627                                         svnCmd += folder_;\r
1628                                 svnCmd += _T("\"");\r
1629                                 break;\r
1630                         case ShellMenuGitSVNIgnore:\r
1631                                 svnCmd += _T("svnignore /path:\"");\r
1632                                 if (files_.size() > 0)\r
1633                                         svnCmd += files_.front();\r
1634                                 else\r
1635                                         svnCmd += folder_;\r
1636                                 svnCmd += _T("\"");\r
1637                                 break;  \r
1638                         case ShellMenuRebase:\r
1639                                 svnCmd += _T("rebase /path:\"");\r
1640                                 if (files_.size() > 0)\r
1641                                         svnCmd += files_.front();\r
1642                                 else\r
1643                                         svnCmd += folder_;\r
1644                                 svnCmd += _T("\"");\r
1645                                 break;\r
1646                         case ShellMenuShowChanged:\r
1647                                 if (files_.size() > 1)\r
1648                 {\r
1649                                     tempfile = WriteFileListToTempFile();\r
1650                                     svnCmd += _T("repostatus /pathfile:\"");\r
1651                                     svnCmd += tempfile;\r
1652                                 svnCmd += _T("\"");\r
1653                                 svnCmd += _T(" /deletepathfile");\r
1654                 }\r
1655                 else\r
1656                 {\r
1657                     svnCmd += _T("repostatus /path:\"");\r
1658                                     if (files_.size() > 0)\r
1659                                             svnCmd += files_.front();\r
1660                                     else\r
1661                                             svnCmd += folder_;\r
1662                                 svnCmd += _T("\"");\r
1663                 }\r
1664                                 break;\r
1665                         case ShellMenuRefBrowse:\r
1666                                 svnCmd += _T("refbrowse /path:\"");\r
1667                                 if (files_.size() > 0)\r
1668                                         svnCmd += files_.front();\r
1669                                 else\r
1670                                         svnCmd += folder_;\r
1671                                 svnCmd += _T("\"");\r
1672                                 break;\r
1673                         case ShellMenuRefLog:\r
1674                                 svnCmd += _T("reflog /path:\"");\r
1675                                 if (files_.size() > 0)\r
1676                                         svnCmd += files_.front();\r
1677                                 else\r
1678                                         svnCmd += folder_;\r
1679                                 svnCmd += _T("\"");\r
1680                                 break;\r
1681 \r
1682                         case ShellMenuStashSave:\r
1683                                 svnCmd += _T("stashsave /path:\"");\r
1684                                 if (files_.size() > 0)\r
1685                                         svnCmd += files_.front();\r
1686                                 else\r
1687                                         svnCmd += folder_;\r
1688                                 svnCmd += _T("\"");\r
1689                                 break;\r
1690 \r
1691                         case ShellMenuStashApply:\r
1692                                 svnCmd += _T("stashapply /path:\"");\r
1693                                 if (files_.size() > 0)\r
1694                                         svnCmd += files_.front();\r
1695                                 else\r
1696                                         svnCmd += folder_;\r
1697                                 svnCmd += _T("\"");\r
1698                                 break;\r
1699 \r
1700                         case ShellMenuStashPop:\r
1701                                 svnCmd += _T("stashpop /path:\"");\r
1702                                 if (files_.size() > 0)\r
1703                                         svnCmd += files_.front();\r
1704                                 else\r
1705                                         svnCmd += folder_;\r
1706                                 svnCmd += _T("\"");\r
1707                                 break;\r
1708 \r
1709 \r
1710                         case ShellMenuStashList:\r
1711                                 svnCmd += _T("reflog /path:\"");\r
1712                                 if (files_.size() > 0)\r
1713                                         svnCmd += files_.front();\r
1714                                 else\r
1715                                         svnCmd += folder_;\r
1716                                 svnCmd += _T("\" /ref:refs/stash");\r
1717                                 break;\r
1718 \r
1719                         case ShellMenuSubAdd:\r
1720                                 svnCmd += _T("subadd /path:\"");\r
1721                                 if (files_.size() > 0)\r
1722                                         svnCmd += files_.front();\r
1723                                 else\r
1724                                         svnCmd += folder_;\r
1725                                 svnCmd += _T("\"");\r
1726                                 break;\r
1727 \r
1728                         case ShellMenuBlame:\r
1729                                 svnCmd += _T("blame /path:\"");\r
1730                                 if (files_.size() > 0)\r
1731                                         svnCmd += files_.front();\r
1732                                 else\r
1733                                         svnCmd += folder_;\r
1734                                 svnCmd += _T("\"");\r
1735                                 break;\r
1736                         case ShellMenuCreatePatch:\r
1737                                 tempfile = WriteFileListToTempFile();\r
1738                                 svnCmd += _T("createpatch /pathfile:\"");\r
1739                                 svnCmd += tempfile;\r
1740                                 svnCmd += _T("\"");\r
1741                                 svnCmd += _T(" /deletepathfile");\r
1742                                 break;\r
1743                         case ShellMenuApplyPatch:\r
1744                                 if ((itemStates & ITEMIS_PATCHINCLIPBOARD) && ((~itemStates) & ITEMIS_PATCHFILE))\r
1745                                 {\r
1746                                         // if there's a patch file in the clipboard, we save it\r
1747                                         // to a temporary file and tell TortoiseMerge to use that one\r
1748                                         UINT cFormat = RegisterClipboardFormat(_T("TGIT_UNIFIEDDIFF"));\r
1749                                         if ((cFormat)&&(OpenClipboard(NULL)))\r
1750                                         { \r
1751                                                 HGLOBAL hglb = GetClipboardData(cFormat); \r
1752                                                 LPCSTR lpstr = (LPCSTR)GlobalLock(hglb); \r
1753 \r
1754                                                 DWORD len = GetTempPath(0, NULL);\r
1755                                                 TCHAR * path = new TCHAR[len+1];\r
1756                                                 TCHAR * tempF = new TCHAR[len+100];\r
1757                                                 GetTempPath (len+1, path);\r
1758                                                 GetTempFileName (path, TEXT("git"), 0, tempF);\r
1759                                                 std::wstring sTempFile = std::wstring(tempF);\r
1760                                                 delete [] path;\r
1761                                                 delete [] tempF;\r
1762 \r
1763                                                 FILE * outFile;\r
1764                                                 size_t patchlen = strlen(lpstr);\r
1765                                                 _tfopen_s(&outFile, sTempFile.c_str(), _T("wb"));\r
1766                                                 if(outFile)\r
1767                                                 {\r
1768                                                         size_t size = fwrite(lpstr, sizeof(char), patchlen, outFile);\r
1769                                                         if (size == patchlen)\r
1770                                                         {\r
1771                                                                 itemStates |= ITEMIS_PATCHFILE;\r
1772                                                                 files_.clear();\r
1773                                                                 files_.push_back(sTempFile);\r
1774                                                         }\r
1775                                                         fclose(outFile);\r
1776                                                 }\r
1777                                                 GlobalUnlock(hglb); \r
1778                                                 CloseClipboard(); \r
1779                                         } \r
1780                                 }\r
1781                                 if (itemStates & ITEMIS_PATCHFILE)\r
1782                                 {\r
1783                                         svnCmd = _T(" /diff:\"");\r
1784                                         if (files_.size() > 0)\r
1785                                         {\r
1786                                                 svnCmd += files_.front();\r
1787                                                 if (itemStatesFolder & ITEMIS_FOLDERINSVN)\r
1788                                                 {\r
1789                                                         svnCmd += _T("\" /patchpath:\"");\r
1790                                                         svnCmd += folder_;\r
1791                                                 }\r
1792                                         }\r
1793                                         else\r
1794                                                 svnCmd += folder_;\r
1795                                         if (itemStates & ITEMIS_INVERSIONEDFOLDER)\r
1796                                                 svnCmd += _T("\" /wc");\r
1797                                         else\r
1798                                                 svnCmd += _T("\"");\r
1799                                 }\r
1800                                 else\r
1801                                 {\r
1802                                         svnCmd = _T(" /patchpath:\"");\r
1803                                         if (files_.size() > 0)\r
1804                                                 svnCmd += files_.front();\r
1805                                         else\r
1806                                                 svnCmd += folder_;\r
1807                                         svnCmd += _T("\"");\r
1808                                 }\r
1809                                 myIDMap.clear();\r
1810                                 myVerbsIDMap.clear();\r
1811                                 myVerbsMap.clear();\r
1812                                 if (CreateProcess(((stdstring)tortoiseMergePath).c_str(), const_cast<TCHAR*>(svnCmd.c_str()), NULL, NULL, FALSE, 0, 0, 0, &startup, &process)==0)\r
1813                                 {\r
1814                                         LPVOID lpMsgBuf;\r
1815                                         FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | \r
1816                                                 FORMAT_MESSAGE_FROM_SYSTEM | \r
1817                                                 FORMAT_MESSAGE_IGNORE_INSERTS,\r
1818                                                 NULL,\r
1819                                                 GetLastError(),\r
1820                                                 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language\r
1821                                                 (LPTSTR) &lpMsgBuf,\r
1822                                                 0,\r
1823                                                 NULL \r
1824                                                 );\r
1825                                         MessageBox( NULL, (LPCTSTR)lpMsgBuf, _T("TortoiseMerge launch failed"), MB_OK | MB_ICONINFORMATION );\r
1826                                         LocalFree( lpMsgBuf );\r
1827                                 }\r
1828                                 CloseHandle(process.hThread);\r
1829                                 CloseHandle(process.hProcess);\r
1830                                 return NOERROR;\r
1831                                 break;\r
1832                         case ShellMenuRevisionGraph:\r
1833                                 svnCmd += _T("revisiongraph /path:\"");\r
1834                                 if (files_.size() > 0)\r
1835                                         svnCmd += files_.front();\r
1836                                 else\r
1837                                         svnCmd += folder_;\r
1838                                 svnCmd += _T("\"");\r
1839                                 break;\r
1840                         case ShellMenuProperties:\r
1841                                 tempfile = WriteFileListToTempFile();\r
1842                                 svnCmd += _T("properties /pathfile:\"");\r
1843                                 svnCmd += tempfile;\r
1844                                 svnCmd += _T("\"");\r
1845                                 svnCmd += _T(" /deletepathfile");\r
1846                                 break;\r
1847                         case ShellMenuClipPaste:\r
1848                                 if (WriteClipboardPathsToTempFile(tempfile))\r
1849                                 {\r
1850                                         bool bCopy = true;\r
1851                                         UINT cPrefDropFormat = RegisterClipboardFormat(_T("Preferred DropEffect"));\r
1852                                         if (cPrefDropFormat)\r
1853                                         {\r
1854                                                 if (OpenClipboard(lpcmi->hwnd))\r
1855                                                 {\r
1856                                                         HGLOBAL hglb = GetClipboardData(cPrefDropFormat);\r
1857                                                         if (hglb)\r
1858                                                         {\r
1859                                                                 DWORD* effect = (DWORD*) GlobalLock(hglb);\r
1860                                                                 if (*effect == DROPEFFECT_MOVE)\r
1861                                                                         bCopy = false;\r
1862                                                                 GlobalUnlock(hglb);\r
1863                                                         }\r
1864                                                         CloseClipboard();\r
1865                                                 }\r
1866                                         }\r
1867 \r
1868                                         if (bCopy)\r
1869                                                 svnCmd += _T("pastecopy /pathfile:\"");\r
1870                                         else\r
1871                                                 svnCmd += _T("pastemove /pathfile:\"");\r
1872                                         svnCmd += tempfile;\r
1873                                         svnCmd += _T("\"");\r
1874                                         svnCmd += _T(" /deletepathfile");\r
1875                                         svnCmd += _T(" /droptarget:\"");\r
1876                                         svnCmd += folder_;\r
1877                                         svnCmd += _T("\"");\r
1878                                 }\r
1879                                 else return NOERROR;\r
1880                                 break;\r
1881                         case ShellMenuClone:\r
1882                                 svnCmd += _T("clone /path:\"");\r
1883                                 svnCmd += folder_;\r
1884                                 svnCmd += _T("\"");\r
1885                                 break;\r
1886                         case ShellMenuPull:\r
1887                                 svnCmd += _T("pull /path:\"");\r
1888                                 if (files_.size() > 0)\r
1889                                         svnCmd += files_.front();\r
1890                                 else\r
1891                                         svnCmd += folder_;\r
1892                                 svnCmd += _T("\"");\r
1893                                 break;\r
1894                         case ShellMenuPush:\r
1895                                 svnCmd += _T("push /path:\"");\r
1896                                 if (files_.size() > 0)\r
1897                                         svnCmd += files_.front();\r
1898                                 else\r
1899                                         svnCmd += folder_;\r
1900                                 svnCmd += _T("\"");\r
1901                                 break;\r
1902                         case ShellMenuBranch:\r
1903                                 svnCmd += _T("branch /path:\"");\r
1904                                 if (files_.size() > 0)\r
1905                                         svnCmd += files_.front();\r
1906                                 else\r
1907                                         svnCmd += folder_;\r
1908                                 svnCmd += _T("\"");\r
1909                                 break;\r
1910                         \r
1911                         case ShellMenuTag:\r
1912                                 svnCmd += _T("tag /path:\"");\r
1913                                 if (files_.size() > 0)\r
1914                                         svnCmd += files_.front();\r
1915                                 else\r
1916                                         svnCmd += folder_;\r
1917                                 svnCmd += _T("\"");\r
1918                                 break;\r
1919 \r
1920                         case ShellMenuFormatPatch:\r
1921                                 svnCmd += _T("formatpatch /path:\"");\r
1922                                 if (files_.size() > 0)\r
1923                                         svnCmd += files_.front();\r
1924                                 else\r
1925                                         svnCmd += folder_;\r
1926                                 svnCmd += _T("\"");\r
1927                                 break;\r
1928 \r
1929                         case ShellMenuImportPatch:\r
1930                                 tempfile = WriteFileListToTempFile();\r
1931                                 svnCmd += _T("importpatch /pathfile:\"");\r
1932                                 svnCmd += tempfile;\r
1933                                 svnCmd += _T("\"");\r
1934                                 svnCmd += _T(" /deletepathfile");\r
1935                                 break;\r
1936 \r
1937                         case ShellMenuCherryPick:\r
1938                                 svnCmd += _T("cherrypick /path:\"");\r
1939                                 if (files_.size() > 0)\r
1940                                         svnCmd += files_.front();\r
1941                                 else\r
1942                                         svnCmd += folder_;\r
1943                                 svnCmd += _T("\"");\r
1944                                 break;\r
1945                         case ShellMenuFetch:\r
1946                                 svnCmd += _T("fetch /path:\"");\r
1947                                 if (files_.size() > 0)\r
1948                                         svnCmd += files_.front();\r
1949                                 else\r
1950                                         svnCmd += folder_;\r
1951                                 svnCmd += _T("\"");\r
1952                                 break;\r
1953 \r
1954                         default:\r
1955                                 break;\r
1956                                 //#endregion\r
1957                         } // switch (id_it->second) \r
1958                         svnCmd += _T(" /hwnd:");\r
1959                         TCHAR buf[30];\r
1960                         _stprintf_s(buf, 30, _T("%d"), lpcmi->hwnd);\r
1961                         svnCmd += buf;\r
1962                         myIDMap.clear();\r
1963                         myVerbsIDMap.clear();\r
1964                         myVerbsMap.clear();\r
1965                         if (CreateProcess(((stdstring)tortoiseProcPath).c_str(), const_cast<TCHAR*>(svnCmd.c_str()), NULL, NULL, FALSE, 0, 0, 0, &startup, &process)==0)\r
1966                         {\r
1967                                 LPVOID lpMsgBuf;\r
1968                                 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | \r
1969                                         FORMAT_MESSAGE_FROM_SYSTEM | \r
1970                                         FORMAT_MESSAGE_IGNORE_INSERTS,\r
1971                                         NULL,\r
1972                                         GetLastError(),\r
1973                                         MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language\r
1974                                         (LPTSTR) &lpMsgBuf,\r
1975                                         0,\r
1976                                         NULL \r
1977                                         );\r
1978                                 MessageBox( NULL, (LPCTSTR)lpMsgBuf, _T("TortoiseProc Launch failed"), MB_OK | MB_ICONINFORMATION );\r
1979                                 LocalFree( lpMsgBuf );\r
1980                         }\r
1981                         CloseHandle(process.hThread);\r
1982                         CloseHandle(process.hProcess);\r
1983                         hr = NOERROR;\r
1984                 } // if (id_it != myIDMap.end() && id_it->first == idCmd) \r
1985         } // if ((files_.size() > 0)||(folder_.size() > 0)) \r
1986         return hr;\r
1987 \r
1988 }\r
1989 \r
1990 // This is for the status bar and things like that:\r
1991 STDMETHODIMP CShellExt::GetCommandString(UINT_PTR idCmd,\r
1992                                          UINT uFlags,\r
1993                                          UINT FAR * /*reserved*/,\r
1994                                          LPSTR pszName,\r
1995                                          UINT cchMax)\r
1996 {\r
1997         PreserveChdir preserveChdir;\r
1998         //do we know the id?\r
1999         std::map<UINT_PTR, UINT_PTR>::const_iterator id_it = myIDMap.lower_bound(idCmd);\r
2000         if (id_it == myIDMap.end() || id_it->first != idCmd)\r
2001         {\r
2002                 return E_INVALIDARG;            //no, we don't\r
2003         }\r
2004 \r
2005         LoadLangDll();\r
2006         HRESULT hr = E_INVALIDARG;\r
2007 \r
2008         MAKESTRING(IDS_MENUDESCDEFAULT);\r
2009         int menuIndex = 0;\r
2010         while (menuInfo[menuIndex].command != ShellMenuLastEntry)\r
2011         {\r
2012                 if (menuInfo[menuIndex].command == (GitCommands)id_it->second)\r
2013                 {\r
2014                         MAKESTRING(menuInfo[menuIndex].menuDescID);\r
2015                         break;\r
2016                 }\r
2017                 menuIndex++;\r
2018         }\r
2019 \r
2020         const TCHAR * desc = stringtablebuffer;\r
2021         switch(uFlags)\r
2022         {\r
2023         case GCS_HELPTEXTA:\r
2024                 {\r
2025                         std::string help = WideToMultibyte(desc);\r
2026                         lstrcpynA(pszName, help.c_str(), cchMax);\r
2027                         hr = S_OK;\r
2028                         break; \r
2029                 }\r
2030         case GCS_HELPTEXTW: \r
2031                 {\r
2032                         wide_string help = desc;\r
2033                         lstrcpynW((LPWSTR)pszName, help.c_str(), cchMax); \r
2034                         hr = S_OK;\r
2035                         break; \r
2036                 }\r
2037         case GCS_VERBA:\r
2038                 {\r
2039                         std::map<UINT_PTR, stdstring>::const_iterator verb_id_it = myVerbsIDMap.lower_bound(idCmd);\r
2040                         if (verb_id_it != myVerbsIDMap.end() && verb_id_it->first == idCmd)\r
2041                         {\r
2042                                 std::string help = WideToMultibyte(verb_id_it->second);\r
2043                                 lstrcpynA(pszName, help.c_str(), cchMax);\r
2044                                 hr = S_OK;\r
2045                         }\r
2046                 }\r
2047                 break;\r
2048         case GCS_VERBW:\r
2049                 {\r
2050                         std::map<UINT_PTR, stdstring>::const_iterator verb_id_it = myVerbsIDMap.lower_bound(idCmd);\r
2051                         if (verb_id_it != myVerbsIDMap.end() && verb_id_it->first == idCmd)\r
2052                         {\r
2053                                 wide_string help = verb_id_it->second;\r
2054                                 ATLTRACE("verb : %ws\n", help.c_str());\r
2055                                 lstrcpynW((LPWSTR)pszName, help.c_str(), cchMax); \r
2056                                 hr = S_OK;\r
2057                         }\r
2058                 }\r
2059                 break;\r
2060         }\r
2061         return hr;\r
2062 }\r
2063 \r
2064 STDMETHODIMP CShellExt::HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam)\r
2065 {\r
2066         LRESULT res;\r
2067         return HandleMenuMsg2(uMsg, wParam, lParam, &res);\r
2068 }\r
2069 \r
2070 STDMETHODIMP CShellExt::HandleMenuMsg2(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *pResult)\r
2071 {\r
2072         PreserveChdir preserveChdir;\r
2073 \r
2074         LRESULT res;\r
2075         if (pResult == NULL)\r
2076                 pResult = &res;\r
2077         *pResult = FALSE;\r
2078 \r
2079         LoadLangDll();\r
2080         switch (uMsg)\r
2081         {\r
2082         case WM_MEASUREITEM:\r
2083                 {\r
2084                         MEASUREITEMSTRUCT* lpmis = (MEASUREITEMSTRUCT*)lParam;\r
2085                         if (lpmis==NULL||lpmis->CtlType!=ODT_MENU)\r
2086                                 break;\r
2087                         lpmis->itemWidth += 2;\r
2088                         if (lpmis->itemHeight < 16)\r
2089                                 lpmis->itemHeight = 16;\r
2090                         *pResult = TRUE;\r
2091                 }\r
2092                 break;\r
2093         case WM_DRAWITEM:\r
2094                 {\r
2095                         LPCTSTR resource;\r
2096                         DRAWITEMSTRUCT* lpdis = (DRAWITEMSTRUCT*)lParam;\r
2097                         if ((lpdis==NULL)||(lpdis->CtlType != ODT_MENU))\r
2098                                 return S_OK;            //not for a menu\r
2099                         resource = GetMenuTextFromResource(myIDMap[lpdis->itemID]);\r
2100                         if (resource == NULL)\r
2101                                 return S_OK;\r
2102                         HICON hIcon = (HICON)LoadImage(g_hResInst, resource, IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);\r
2103                         if (hIcon == NULL)\r
2104                                 return S_OK;\r
2105                         DrawIconEx(lpdis->hDC,\r
2106                                 lpdis->rcItem.left - 16,\r
2107                                 lpdis->rcItem.top + (lpdis->rcItem.bottom - lpdis->rcItem.top - 16) / 2,\r
2108                                 hIcon, 16, 16,\r
2109                                 0, NULL, DI_NORMAL);\r
2110                         DestroyIcon(hIcon);\r
2111                         *pResult = TRUE;\r
2112                 }\r
2113                 break;\r
2114         case WM_MENUCHAR:\r
2115                 {\r
2116                         LPCTSTR resource;\r
2117                         TCHAR *szItem;\r
2118                         if (HIWORD(wParam) != MF_POPUP)\r
2119                                 return NOERROR;\r
2120                         int nChar = LOWORD(wParam);\r
2121                         if (_istascii((wint_t)nChar) && _istupper((wint_t)nChar))\r
2122                                 nChar = tolower(nChar);\r
2123                         // we have the char the user pressed, now search that char in all our\r
2124                         // menu items\r
2125                         std::vector<int> accmenus;\r
2126                         for (std::map<UINT_PTR, UINT_PTR>::iterator It = mySubMenuMap.begin(); It != mySubMenuMap.end(); ++It)\r
2127                         {\r
2128                                 resource = GetMenuTextFromResource(mySubMenuMap[It->first]);\r
2129                                 if (resource == NULL)\r
2130                                         continue;\r
2131                                 szItem = stringtablebuffer;\r
2132                                 TCHAR * amp = _tcschr(szItem, '&');\r
2133                                 if (amp == NULL)\r
2134                                         continue;\r
2135                                 amp++;\r
2136                                 int ampChar = LOWORD(*amp);\r
2137                                 if (_istascii((wint_t)ampChar) && _istupper((wint_t)ampChar))\r
2138                                         ampChar = tolower(ampChar);\r
2139                                 if (ampChar == nChar)\r
2140                                 {\r
2141                                         // yep, we found a menu which has the pressed key\r
2142                                         // as an accelerator. Add that menu to the list to\r
2143                                         // process later.\r
2144                                         accmenus.push_back(It->first);\r
2145                                 }\r
2146                         }\r
2147                         if (accmenus.size() == 0)\r
2148                         {\r
2149                                 // no menu with that accelerator key.\r
2150                                 *pResult = MAKELONG(0, MNC_IGNORE);\r
2151                                 return NOERROR;\r
2152                         }\r
2153                         if (accmenus.size() == 1)\r
2154                         {\r
2155                                 // Only one menu with that accelerator key. We're lucky!\r
2156                                 // So just execute that menu entry.\r
2157                                 *pResult = MAKELONG(accmenus[0], MNC_EXECUTE);\r
2158                                 return NOERROR;\r
2159                         }\r
2160                         if (accmenus.size() > 1)\r
2161                         {\r
2162                                 // we have more than one menu item with this accelerator key!\r
2163                                 MENUITEMINFO mif;\r
2164                                 mif.cbSize = sizeof(MENUITEMINFO);\r
2165                                 mif.fMask = MIIM_STATE;\r
2166                                 for (std::vector<int>::iterator it = accmenus.begin(); it != accmenus.end(); ++it)\r
2167                                 {\r
2168                                         GetMenuItemInfo((HMENU)lParam, *it, TRUE, &mif);\r
2169                                         if (mif.fState == MFS_HILITE)\r
2170                                         {\r
2171                                                 // this is the selected item, so select the next one\r
2172                                                 ++it;\r
2173                                                 if (it == accmenus.end())\r
2174                                                         *pResult = MAKELONG(accmenus[0], MNC_SELECT);\r
2175                                                 else\r
2176                                                         *pResult = MAKELONG(*it, MNC_SELECT);\r
2177                                                 return NOERROR;\r
2178                                         }\r
2179                                 }\r
2180                                 *pResult = MAKELONG(accmenus[0], MNC_SELECT);\r
2181                         }\r
2182                 }\r
2183                 break;\r
2184         default:\r
2185                 return NOERROR;\r
2186         }\r
2187 \r
2188         return NOERROR;\r
2189 }\r
2190 \r
2191 LPCTSTR CShellExt::GetMenuTextFromResource(int id)\r
2192 {\r
2193         TCHAR textbuf[255];\r
2194         LPCTSTR resource = NULL;\r
2195         unsigned __int64 layout = g_ShellCache.GetMenuLayout();\r
2196         space = 6;\r
2197 \r
2198         int menuIndex = 0;\r
2199         while (menuInfo[menuIndex].command != ShellMenuLastEntry)\r
2200         {\r
2201                 if (menuInfo[menuIndex].command == id)\r
2202                 {\r
2203                         MAKESTRING(menuInfo[menuIndex].menuTextID);\r
2204                         resource = MAKEINTRESOURCE(menuInfo[menuIndex].iconID);\r
2205                         switch (id)\r
2206                         {\r
2207                         case ShellSubMenuMultiple:\r
2208                         case ShellSubMenuLink:\r
2209                         case ShellSubMenuFolder:\r
2210                         case ShellSubMenuFile:\r
2211                         case ShellSubMenu:\r
2212                                 space = 0;\r
2213                                 break;\r
2214                         default:\r
2215                                 space = layout & menuInfo[menuIndex].menuID ? 0 : 6;\r
2216                                 if (layout & (menuInfo[menuIndex].menuID)) \r
2217                                 {\r
2218                                         _tcscpy_s(textbuf, 255, _T("Git "));\r
2219                                         _tcscat_s(textbuf, 255, stringtablebuffer);\r
2220                                         _tcscpy_s(stringtablebuffer, 255, textbuf);\r
2221                                 }\r
2222                                 break;\r
2223                         }\r
2224                         return resource;\r
2225                 }\r
2226                 menuIndex++;\r
2227         }\r
2228         return NULL;\r
2229 }\r
2230 \r
2231 bool CShellExt::IsIllegalFolder(std::wstring folder, int * cslidarray)\r
2232 {\r
2233         int i=0;\r
2234         TCHAR buf[MAX_PATH];    //MAX_PATH ok, since SHGetSpecialFolderPath doesn't return the required buffer length!\r
2235         LPITEMIDLIST pidl = NULL;\r
2236         while (cslidarray[i])\r
2237         {\r
2238                 ++i;\r
2239                 pidl = NULL;\r
2240                 if (SHGetFolderLocation(NULL, cslidarray[i-1], NULL, 0, &pidl)!=S_OK)\r
2241                         continue;\r
2242                 if (!SHGetPathFromIDList(pidl, buf))\r
2243                 {\r
2244                         // not a file system path, definitely illegal for our use\r
2245                         CoTaskMemFree(pidl);\r
2246                         continue;\r
2247                 }\r
2248                 CoTaskMemFree(pidl);\r
2249                 if (_tcslen(buf)==0)\r
2250                         continue;\r
2251                 if (_tcscmp(buf, folder.c_str())==0)\r
2252                         return true;\r
2253         }\r
2254         return false;\r
2255 }\r
2256 \r
2257 void CShellExt::InsertIgnoreSubmenus(UINT &idCmd, UINT idCmdFirst, HMENU hMenu, HMENU subMenu, UINT &indexMenu, int &indexSubMenu, unsigned __int64 topmenu, bool bShowIcons, UINT uFlags)\r
2258 {\r
2259         HMENU ignoresubmenu = NULL;\r
2260         int indexignoresub = 0;\r
2261         bool bShowIgnoreMenu = false;\r
2262         TCHAR maskbuf[MAX_PATH];                // MAX_PATH is ok, since this only holds a filename\r
2263         TCHAR ignorepath[MAX_PATH];             // MAX_PATH is ok, since this only holds a filename\r
2264         if (files_.size() == 0)\r
2265                 return;\r
2266         UINT icon = bShowIcons ? IDI_IGNORE : 0;\r
2267 \r
2268         std::vector<stdstring>::iterator I = files_.begin();\r
2269         if (_tcsrchr(I->c_str(), '\\'))\r
2270                 _tcscpy_s(ignorepath, MAX_PATH, _tcsrchr(I->c_str(), '\\')+1);\r
2271         else\r
2272                 _tcscpy_s(ignorepath, MAX_PATH, I->c_str());\r
2273         if ((itemStates & ITEMIS_IGNORED)&&(ignoredprops.size() > 0))\r
2274         {\r
2275                 // check if the item name is ignored or the mask\r
2276                 size_t p = 0;\r
2277                 while ( (p=ignoredprops.find( ignorepath,p )) != -1 )\r
2278                 {\r
2279                         if ( (p==0 || ignoredprops[p-1]==TCHAR('\n'))\r
2280                                 && (p+_tcslen(ignorepath)==ignoredprops.length() || ignoredprops[p+_tcslen(ignorepath)+1]==TCHAR('\n')) )\r
2281                         {\r
2282                                 break;\r
2283                         }\r
2284                         p++;\r
2285                 }\r
2286                 if (p!=-1)\r
2287                 {\r
2288                         ignoresubmenu = CreateMenu();\r
2289                         InsertMenu(ignoresubmenu, indexignoresub++, MF_BYPOSITION | MF_STRING , idCmd, ignorepath);\r
2290                         stdstring verb = stdstring(ignorepath);\r
2291                         myVerbsMap[verb] = idCmd - idCmdFirst;\r
2292                         myVerbsMap[verb] = idCmd;\r
2293                         myVerbsIDMap[idCmd - idCmdFirst] = verb;\r
2294                         myVerbsIDMap[idCmd] = verb;\r
2295                         myIDMap[idCmd - idCmdFirst] = ShellMenuUnIgnore;\r
2296                         myIDMap[idCmd++] = ShellMenuUnIgnore;\r
2297                         bShowIgnoreMenu = true;\r
2298                 }\r
2299                 _tcscpy_s(maskbuf, MAX_PATH, _T("*"));\r
2300                 if (_tcsrchr(ignorepath, '.'))\r
2301                 {\r
2302                         _tcscat_s(maskbuf, MAX_PATH, _tcsrchr(ignorepath, '.'));\r
2303                         p = ignoredprops.find(maskbuf);\r
2304                         if ((p!=-1) &&\r
2305                                 ((ignoredprops.compare(maskbuf)==0) || (ignoredprops.find('\n', p)==p+_tcslen(maskbuf)+1) || (ignoredprops.rfind('\n', p)==p-1)))\r
2306                         {\r
2307                                 if (ignoresubmenu==NULL)\r
2308                                         ignoresubmenu = CreateMenu();\r
2309 \r
2310                                 InsertMenu(ignoresubmenu, indexignoresub++, MF_BYPOSITION | MF_STRING , idCmd, maskbuf);\r
2311                                 stdstring verb = stdstring(maskbuf);\r
2312                                 myVerbsMap[verb] = idCmd - idCmdFirst;\r
2313                                 myVerbsMap[verb] = idCmd;\r
2314                                 myVerbsIDMap[idCmd - idCmdFirst] = verb;\r
2315                                 myVerbsIDMap[idCmd] = verb;\r
2316                                 myIDMap[idCmd - idCmdFirst] = ShellMenuUnIgnoreCaseSensitive;\r
2317                                 myIDMap[idCmd++] = ShellMenuUnIgnoreCaseSensitive;\r
2318                                 bShowIgnoreMenu = true;\r
2319                         }\r
2320                 }\r
2321         }\r
2322         else if ((itemStates & ITEMIS_IGNORED) == 0)\r
2323         {\r
2324                 bShowIgnoreMenu = true;\r
2325                 ignoresubmenu = CreateMenu();\r
2326                 if (itemStates & ITEMIS_ONLYONE)\r
2327                 {\r
2328                         InsertMenu(ignoresubmenu, indexignoresub++, MF_BYPOSITION | MF_STRING , idCmd, ignorepath);\r
2329                         myIDMap[idCmd - idCmdFirst] = ShellMenuIgnore;\r
2330                         myIDMap[idCmd++] = ShellMenuIgnore;\r
2331 \r
2332                         _tcscpy_s(maskbuf, MAX_PATH, _T("*"));\r
2333                         if (_tcsrchr(ignorepath, '.'))\r
2334                         {\r
2335                                 _tcscat_s(maskbuf, MAX_PATH, _tcsrchr(ignorepath, '.'));\r
2336                                 InsertMenu(ignoresubmenu, indexignoresub++, MF_BYPOSITION | MF_STRING , idCmd, maskbuf);\r
2337                                 stdstring verb = stdstring(maskbuf);\r
2338                                 myVerbsMap[verb] = idCmd - idCmdFirst;\r
2339                                 myVerbsMap[verb] = idCmd;\r
2340                                 myVerbsIDMap[idCmd - idCmdFirst] = verb;\r
2341                                 myVerbsIDMap[idCmd] = verb;\r
2342                                 myIDMap[idCmd - idCmdFirst] = ShellMenuIgnoreCaseSensitive;\r
2343                                 myIDMap[idCmd++] = ShellMenuIgnoreCaseSensitive;\r
2344                         }\r
2345                 }\r
2346                 else\r
2347                 {\r
2348                         MAKESTRING(IDS_MENUIGNOREMULTIPLE);\r
2349                         _stprintf_s(ignorepath, MAX_PATH, stringtablebuffer, files_.size());\r
2350                         InsertMenu(ignoresubmenu, indexignoresub++, MF_BYPOSITION | MF_STRING , idCmd, ignorepath);\r
2351                         stdstring verb = stdstring(ignorepath);\r
2352                         myVerbsMap[verb] = idCmd - idCmdFirst;\r
2353                         myVerbsMap[verb] = idCmd;\r
2354                         myVerbsIDMap[idCmd - idCmdFirst] = verb;\r
2355                         myVerbsIDMap[idCmd] = verb;\r
2356                         myIDMap[idCmd - idCmdFirst] = ShellMenuIgnore;\r
2357                         myIDMap[idCmd++] = ShellMenuIgnore;\r
2358 \r
2359                         MAKESTRING(IDS_MENUIGNOREMULTIPLEMASK);\r
2360                         _stprintf_s(ignorepath, MAX_PATH, stringtablebuffer, files_.size());\r
2361                         InsertMenu(ignoresubmenu, indexignoresub++, MF_BYPOSITION | MF_STRING , idCmd, ignorepath);\r
2362                         verb = stdstring(ignorepath);\r
2363                         myVerbsMap[verb] = idCmd - idCmdFirst;\r
2364                         myVerbsMap[verb] = idCmd;\r
2365                         myVerbsIDMap[idCmd - idCmdFirst] = verb;\r
2366                         myVerbsIDMap[idCmd] = verb;\r
2367                         myIDMap[idCmd - idCmdFirst] = ShellMenuIgnoreCaseSensitive;\r
2368                         myIDMap[idCmd++] = ShellMenuIgnoreCaseSensitive;\r
2369                 }\r
2370         }\r
2371 \r
2372         if (bShowIgnoreMenu)\r
2373         {\r
2374                 MENUITEMINFO menuiteminfo;\r
2375                 SecureZeroMemory(&menuiteminfo, sizeof(menuiteminfo));\r
2376                 menuiteminfo.cbSize = sizeof(menuiteminfo);\r
2377                 if (fullver < 0x500 || (fullver == 0x500 && !(uFlags&~(CMF_RESERVED|CMF_EXPLORE|CMF_EXTENDEDVERBS))))\r
2378                 {\r
2379                         menuiteminfo.fMask = MIIM_STRING | MIIM_ID | MIIM_SUBMENU | MIIM_DATA;\r
2380                         if (icon)\r
2381                         {\r
2382                                 HBITMAP bmp = IconToBitmap(icon);\r
2383                                 menuiteminfo.fMask |= MIIM_CHECKMARKS;\r
2384                                 menuiteminfo.hbmpChecked = bmp;\r
2385                                 menuiteminfo.hbmpUnchecked = bmp;\r
2386                         }\r
2387                 }\r
2388                 else\r
2389                 {\r
2390                         menuiteminfo.fMask = MIIM_FTYPE | MIIM_ID | MIIM_SUBMENU | MIIM_DATA | MIIM_STRING;\r
2391                         if (icon)\r
2392                         {\r
2393                                 menuiteminfo.fMask |= MIIM_BITMAP;\r
2394                                 menuiteminfo.hbmpItem = (fullver >= 0x600) ? IconToBitmapPARGB32(icon) : HBMMENU_CALLBACK;\r
2395                         }\r
2396                 }\r
2397                 menuiteminfo.fType = MFT_STRING;\r
2398                 menuiteminfo.hSubMenu = ignoresubmenu;\r
2399                 menuiteminfo.wID = idCmd;\r
2400                 SecureZeroMemory(stringtablebuffer, sizeof(stringtablebuffer));\r
2401                 if (itemStates & ITEMIS_IGNORED)\r
2402                         GetMenuTextFromResource(ShellMenuUnIgnoreSub);\r
2403                 else\r
2404                         GetMenuTextFromResource(ShellMenuIgnoreSub);\r
2405                 menuiteminfo.dwTypeData = stringtablebuffer;\r
2406                 menuiteminfo.cch = (UINT)min(_tcslen(menuiteminfo.dwTypeData), UINT_MAX);\r
2407 \r
2408                 InsertMenuItem((topmenu & MENUIGNORE) ? hMenu : subMenu, (topmenu & MENUIGNORE) ? indexMenu++ : indexSubMenu++, TRUE, &menuiteminfo);\r
2409                 if (itemStates & ITEMIS_IGNORED)\r
2410                 {\r
2411                         myIDMap[idCmd - idCmdFirst] = ShellMenuUnIgnoreSub;\r
2412                         myIDMap[idCmd++] = ShellMenuUnIgnoreSub;\r
2413                 }\r
2414                 else\r
2415                 {\r
2416                         myIDMap[idCmd - idCmdFirst] = ShellMenuIgnoreSub;\r
2417                         myIDMap[idCmd++] = ShellMenuIgnoreSub;\r
2418                 }\r
2419         }\r
2420 }\r
2421 \r
2422 HBITMAP CShellExt::IconToBitmapPARGB32(UINT uIcon)\r
2423 {\r
2424         std::map<UINT, HBITMAP>::iterator bitmap_it = bitmaps.lower_bound(uIcon);\r
2425         if (bitmap_it != bitmaps.end() && bitmap_it->first == uIcon)\r
2426                 return bitmap_it->second;\r
2427 \r
2428         HICON hIcon = (HICON)LoadImage(g_hResInst, MAKEINTRESOURCE(uIcon), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);\r
2429         if (!hIcon)\r
2430                 return NULL;\r
2431 \r
2432         if (pfnBeginBufferedPaint == NULL || pfnEndBufferedPaint == NULL || pfnGetBufferedPaintBits == NULL)\r
2433                 return NULL;\r
2434 \r
2435         SIZE sizIcon;\r
2436         sizIcon.cx = GetSystemMetrics(SM_CXSMICON);\r
2437         sizIcon.cy = GetSystemMetrics(SM_CYSMICON);\r
2438 \r
2439         RECT rcIcon;\r
2440         SetRect(&rcIcon, 0, 0, sizIcon.cx, sizIcon.cy);\r
2441         HBITMAP hBmp = NULL;\r
2442 \r
2443         HDC hdcDest = CreateCompatibleDC(NULL);\r
2444         if (hdcDest)\r
2445         {\r
2446                 if (SUCCEEDED(Create32BitHBITMAP(hdcDest, &sizIcon, NULL, &hBmp)))\r
2447                 {\r
2448                         HBITMAP hbmpOld = (HBITMAP)SelectObject(hdcDest, hBmp);\r
2449                         if (hbmpOld)\r
2450                         {\r
2451                                 BLENDFUNCTION bfAlpha = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };\r
2452                                 BP_PAINTPARAMS paintParams = {0};\r
2453                                 paintParams.cbSize = sizeof(paintParams);\r
2454                                 paintParams.dwFlags = BPPF_ERASE;\r
2455                                 paintParams.pBlendFunction = &bfAlpha;\r
2456 \r
2457                                 HDC hdcBuffer;\r
2458                                 HPAINTBUFFER hPaintBuffer = pfnBeginBufferedPaint(hdcDest, &rcIcon, BPBF_DIB, &paintParams, &hdcBuffer);\r
2459                                 if (hPaintBuffer)\r
2460                                 {\r
2461                                         if (DrawIconEx(hdcBuffer, 0, 0, hIcon, sizIcon.cx, sizIcon.cy, 0, NULL, DI_NORMAL))\r
2462                                         {\r
2463                                                 // If icon did not have an alpha channel we need to convert buffer to PARGB\r
2464                                                 ConvertBufferToPARGB32(hPaintBuffer, hdcDest, hIcon, sizIcon);\r
2465                                         }\r
2466 \r
2467                                         // This will write the buffer contents to the destination bitmap\r
2468                                         pfnEndBufferedPaint(hPaintBuffer, TRUE);\r
2469                                 }\r
2470 \r
2471                                 SelectObject(hdcDest, hbmpOld);\r
2472                         }\r
2473                 }\r
2474 \r
2475                 DeleteDC(hdcDest);\r
2476         }\r
2477 \r
2478         DestroyIcon(hIcon);\r
2479 \r
2480         if(hBmp)\r
2481                 bitmaps.insert(bitmap_it, std::make_pair(uIcon, hBmp));\r
2482         return hBmp;\r
2483 }\r
2484 \r
2485 HRESULT CShellExt::Create32BitHBITMAP(HDC hdc, const SIZE *psize, __deref_opt_out void **ppvBits, __out HBITMAP* phBmp)\r
2486 {\r
2487         *phBmp = NULL;\r
2488 \r
2489         BITMAPINFO bmi;\r
2490         ZeroMemory(&bmi, sizeof(bmi));\r
2491         bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);\r
2492         bmi.bmiHeader.biPlanes = 1;\r
2493         bmi.bmiHeader.biCompression = BI_RGB;\r
2494 \r
2495         bmi.bmiHeader.biWidth = psize->cx;\r
2496         bmi.bmiHeader.biHeight = psize->cy;\r
2497         bmi.bmiHeader.biBitCount = 32;\r
2498 \r
2499         HDC hdcUsed = hdc ? hdc : GetDC(NULL);\r
2500         if (hdcUsed)\r
2501         {\r
2502                 *phBmp = CreateDIBSection(hdcUsed, &bmi, DIB_RGB_COLORS, ppvBits, NULL, 0);\r
2503                 if (hdc != hdcUsed)\r
2504                 {\r
2505                         ReleaseDC(NULL, hdcUsed);\r
2506                 }\r
2507         }\r
2508         return (NULL == *phBmp) ? E_OUTOFMEMORY : S_OK;\r
2509 }\r
2510 \r
2511 HRESULT CShellExt::ConvertBufferToPARGB32(HPAINTBUFFER hPaintBuffer, HDC hdc, HICON hicon, SIZE& sizIcon)\r
2512 {\r
2513         RGBQUAD *prgbQuad;\r
2514         int cxRow;\r
2515         HRESULT hr = pfnGetBufferedPaintBits(hPaintBuffer, &prgbQuad, &cxRow);\r
2516         if (SUCCEEDED(hr))\r
2517         {\r
2518                 ARGB *pargb = reinterpret_cast<ARGB *>(prgbQuad);\r
2519                 if (!HasAlpha(pargb, sizIcon, cxRow))\r
2520                 {\r
2521                         ICONINFO info;\r
2522                         if (GetIconInfo(hicon, &info))\r
2523                         {\r
2524                                 if (info.hbmMask)\r
2525                                 {\r
2526                                         hr = ConvertToPARGB32(hdc, pargb, info.hbmMask, sizIcon, cxRow);\r
2527                                 }\r
2528 \r
2529                                 DeleteObject(info.hbmColor);\r
2530                                 DeleteObject(info.hbmMask);\r
2531                         }\r
2532                 }\r
2533         }\r
2534 \r
2535         return hr;\r
2536 }\r
2537 \r
2538 bool CShellExt::HasAlpha(__in ARGB *pargb, SIZE& sizImage, int cxRow)\r
2539 {\r
2540         ULONG cxDelta = cxRow - sizImage.cx;\r
2541         for (ULONG y = sizImage.cy; y; --y)\r
2542         {\r
2543                 for (ULONG x = sizImage.cx; x; --x)\r
2544                 {\r
2545                         if (*pargb++ & 0xFF000000)\r
2546                         {\r
2547                                 return true;\r
2548                         }\r
2549                 }\r
2550 \r
2551                 pargb += cxDelta;\r
2552         }\r
2553 \r
2554         return false;\r
2555 }\r
2556 \r
2557 HRESULT CShellExt::ConvertToPARGB32(HDC hdc, __inout ARGB *pargb, HBITMAP hbmp, SIZE& sizImage, int cxRow)\r
2558 {\r
2559         BITMAPINFO bmi;\r
2560         ZeroMemory(&bmi, sizeof(bmi));\r
2561         bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);\r
2562         bmi.bmiHeader.biPlanes = 1;\r
2563         bmi.bmiHeader.biCompression = BI_RGB;\r
2564 \r
2565         bmi.bmiHeader.biWidth = sizImage.cx;\r
2566         bmi.bmiHeader.biHeight = sizImage.cy;\r
2567         bmi.bmiHeader.biBitCount = 32;\r
2568 \r
2569         HRESULT hr = E_OUTOFMEMORY;\r
2570         HANDLE hHeap = GetProcessHeap();\r
2571         void *pvBits = HeapAlloc(hHeap, 0, bmi.bmiHeader.biWidth * 4 * bmi.bmiHeader.biHeight);\r
2572         if (pvBits)\r
2573         {\r
2574                 hr = E_UNEXPECTED;\r
2575                 if (GetDIBits(hdc, hbmp, 0, bmi.bmiHeader.biHeight, pvBits, &bmi, DIB_RGB_COLORS) == bmi.bmiHeader.biHeight)\r
2576                 {\r
2577                         ULONG cxDelta = cxRow - bmi.bmiHeader.biWidth;\r
2578                         ARGB *pargbMask = static_cast<ARGB *>(pvBits);\r
2579 \r
2580                         for (ULONG y = bmi.bmiHeader.biHeight; y; --y)\r
2581                         {\r
2582                                 for (ULONG x = bmi.bmiHeader.biWidth; x; --x)\r
2583                                 {\r
2584                                         if (*pargbMask++)\r
2585                                         {\r
2586                                                 // transparent pixel\r
2587                                                 *pargb++ = 0;\r
2588                                         }\r
2589                                         else\r
2590                                         {\r
2591                                                 // opaque pixel\r
2592                                                 *pargb++ |= 0xFF000000;\r
2593                                         }\r
2594                                 }\r
2595 \r
2596                                 pargb += cxDelta;\r
2597                         }\r
2598 \r
2599                         hr = S_OK;\r
2600                 }\r
2601 \r
2602                 HeapFree(hHeap, 0, pvBits);\r
2603         }\r
2604 \r
2605         return hr;\r
2606 }\r
2607 \r