OSDN Git Service

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