Fixed issue #3307: Abort Merge on a single file always results in a parameter error...
[TortoiseGit.git] / src / Utils / CommonAppUtils.cpp
blob1c24b5ace1111cfcef6951abd5f2b46ccf41c218
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2013, 2015-2018 - TortoiseGit
4 // Copyright (C) 2003-2008,2010 - TortoiseSVN
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software Foundation,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "stdafx.h"
21 #include "../Resources/LoglistCommonResource.h"
22 #include "CommonAppUtils.h"
23 #include "PathUtils.h"
24 #include "StringUtils.h"
25 #include "FormatMessageWrapper.h"
26 #include "registry.h"
27 #include "SelectFileFilter.h"
28 #include "DPIAware.h"
29 #include "LoadIconEx.h"
31 extern CString sOrigCWD;
32 extern CString g_sGroupingUUID;
34 bool CCommonAppUtils::LaunchApplication(const CString& sCommandLine, UINT idErrMessageFormat, bool bWaitForStartup, CString *cwd, bool uac)
36 CString theCWD = sOrigCWD;
37 if (cwd)
38 theCWD = *cwd;
40 if (uac)
42 CString file, param;
43 SHELLEXECUTEINFO shellinfo = { 0 };
44 shellinfo.cbSize = sizeof(shellinfo);
45 shellinfo.lpVerb = L"runas";
46 shellinfo.nShow = SW_SHOWNORMAL;
47 shellinfo.fMask = SEE_MASK_NOCLOSEPROCESS;
48 shellinfo.lpDirectory = theCWD;
50 int pos = sCommandLine.Find('"');
51 if (pos == 0)
53 pos = sCommandLine.Find('"', 2);
54 if (pos > 1)
56 file = sCommandLine.Mid(1, pos - 1);
57 param = sCommandLine.Mid(pos + 1);
59 else
61 if (idErrMessageFormat != 0)
63 CString temp;
64 temp.Format(idErrMessageFormat, (LPCTSTR)CFormatMessageWrapper());
65 MessageBox(nullptr, temp, L"TortoiseGit", MB_OK | MB_ICONINFORMATION);
67 return false;
70 else
72 pos = sCommandLine.Find(' ', 1);
73 if (pos > 0)
75 file = sCommandLine.Left(pos);
76 param = sCommandLine.Mid(pos + 1);
78 else
79 file = sCommandLine;
82 shellinfo.lpFile = file;
83 shellinfo.lpParameters = param;
85 if (!ShellExecuteEx(&shellinfo))
87 if (idErrMessageFormat != 0)
89 CString temp;
90 temp.Format(idErrMessageFormat, (LPCTSTR)CFormatMessageWrapper());
91 MessageBox(nullptr, temp, L"TortoiseGit", MB_OK | MB_ICONINFORMATION);
93 return false;
96 if (shellinfo.hProcess)
98 if (bWaitForStartup)
99 WaitForInputIdle(shellinfo.hProcess, 10000);
101 CloseHandle(shellinfo.hProcess);
104 return true;
107 STARTUPINFO startup = { 0 };
108 PROCESS_INFORMATION process = { 0 };
109 startup.cb = sizeof(startup);
111 CString cleanCommandLine(sCommandLine);
112 if (CreateProcess(nullptr, cleanCommandLine.GetBuffer(), nullptr, nullptr, FALSE, CREATE_UNICODE_ENVIRONMENT, nullptr, theCWD, &startup, &process) == 0)
114 if (idErrMessageFormat)
116 CString temp;
117 temp.Format(idErrMessageFormat, (LPCTSTR)CFormatMessageWrapper());
118 MessageBox(nullptr, temp, L"TortoiseGit", MB_OK | MB_ICONINFORMATION);
120 return false;
123 AllowSetForegroundWindow(process.dwProcessId);
125 if (bWaitForStartup)
126 WaitForInputIdle(process.hProcess, 10000);
128 CloseHandle(process.hThread);
129 CloseHandle(process.hProcess);
131 return true;
134 bool CCommonAppUtils::RunTortoiseGitProc(const CString& sCommandLine, bool uac, bool includeGroupingUUID)
136 CString pathToExecutable = CPathUtils::GetAppDirectory() + L"TortoiseGitProc.exe";
137 CString sCmd;
138 sCmd.Format(L"\"%s\" %s", (LPCTSTR)pathToExecutable, (LPCTSTR)sCommandLine);
139 if (AfxGetMainWnd()->GetSafeHwnd() && (sCommandLine.Find(L"/hwnd:") < 0))
140 sCmd.AppendFormat(L" /hwnd:%p", (void*)AfxGetMainWnd()->GetSafeHwnd());
141 if (!g_sGroupingUUID.IsEmpty() && includeGroupingUUID)
143 sCmd += L" /groupuuid:\"";
144 sCmd += g_sGroupingUUID;
145 sCmd += L'"';
148 return LaunchApplication(sCmd, NULL, false, nullptr, uac);
151 bool CCommonAppUtils::IsAdminLogin()
153 SID_IDENTIFIER_AUTHORITY ntAuthority = SECURITY_NT_AUTHORITY;
154 PSID administratorsGroup;
155 // Initialize SID.
156 if (!AllocateAndInitializeSid(&ntAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &administratorsGroup))
157 return false;
158 SCOPE_EXIT { FreeSid(administratorsGroup); };
160 // Check whether the token is present in admin group.
161 BOOL isInAdminGroup = FALSE;
162 if (!CheckTokenMembership(nullptr, administratorsGroup, &isInAdminGroup))
163 return false;
165 return !!isInAdminGroup;
168 bool CCommonAppUtils::SetListCtrlBackgroundImage(HWND hListCtrl, UINT nID)
170 return SetListCtrlBackgroundImage(hListCtrl, nID, CDPIAware::Instance().ScaleX(128), CDPIAware::Instance().ScaleY(128));
173 bool CCommonAppUtils::SetListCtrlBackgroundImage(HWND hListCtrl, UINT nID, int width, int height)
175 if ((((DWORD)CRegStdDWORD(L"Software\\TortoiseGit\\ShowListBackgroundImage", TRUE)) == FALSE))
176 return false;
177 ListView_SetTextBkColor(hListCtrl, CLR_NONE);
178 COLORREF bkColor = ListView_GetBkColor(hListCtrl);
179 // create a bitmap from the icon
180 auto hIcon = ::LoadIconEx(AfxGetResourceHandle(), MAKEINTRESOURCE(nID), width, height);
181 if (!hIcon)
182 return false;
183 SCOPE_EXIT { DestroyIcon(hIcon); };
185 RECT rect = {0};
186 rect.right = width;
187 rect.bottom = height;
189 HWND desktop = ::GetDesktopWindow();
190 if (!desktop)
191 return false;
193 HDC screen_dev = ::GetDC(desktop);
194 if (!screen_dev)
195 return false;
196 SCOPE_EXIT { ::ReleaseDC(desktop, screen_dev); };
198 // Create a compatible DC
199 HDC dst_hdc = ::CreateCompatibleDC(screen_dev);
200 if (!dst_hdc)
201 return false;
202 SCOPE_EXIT { ::DeleteDC(dst_hdc); };
204 // Create a new bitmap of icon size
205 HBITMAP bmp = ::CreateCompatibleBitmap(screen_dev, rect.right, rect.bottom);
206 if (!bmp)
207 return false;
209 // Select it into the compatible DC
210 HBITMAP old_dst_bmp = (HBITMAP)::SelectObject(dst_hdc, bmp);
211 // Fill the background of the compatible DC with the given color
212 ::SetBkColor(dst_hdc, bkColor);
213 ::ExtTextOut(dst_hdc, 0, 0, ETO_OPAQUE, &rect, nullptr, 0, nullptr);
215 // Draw the icon into the compatible DC
216 ::DrawIconEx(dst_hdc, 0, 0, hIcon, rect.right, rect.bottom, 0, nullptr, DI_NORMAL);
217 ::SelectObject(dst_hdc, old_dst_bmp);
219 LVBKIMAGE lv;
220 lv.ulFlags = LVBKIF_TYPE_WATERMARK;
221 lv.hbm = bmp;
222 lv.xOffsetPercent = 100;
223 lv.yOffsetPercent = 100;
224 ListView_SetBkImage(hListCtrl, &lv);
225 return true;
228 bool CCommonAppUtils::FileOpenSave(CString& path, int* filterindex, UINT title, UINT filterId, bool bOpen, HWND hwndOwner, LPCTSTR defaultExt, bool handleAsFile)
230 // Create a new common save file dialog
231 CComPtr<IFileDialog> pfd;
233 if (!SUCCEEDED(pfd.CoCreateInstance(bOpen ? CLSID_FileOpenDialog : CLSID_FileSaveDialog, nullptr, CLSCTX_INPROC_SERVER)))
234 return false;
236 // Set the dialog options
237 DWORD dwOptions;
238 if (!SUCCEEDED(pfd->GetOptions(&dwOptions)))
239 return false;
241 if (bOpen)
243 if (!SUCCEEDED(pfd->SetOptions(dwOptions | FOS_FILEMUSTEXIST | FOS_FORCEFILESYSTEM | FOS_PATHMUSTEXIST)))
244 return false;
246 else
248 if (!SUCCEEDED(pfd->SetOptions(dwOptions | FOS_OVERWRITEPROMPT | FOS_FORCEFILESYSTEM | FOS_PATHMUSTEXIST)))
249 return false;
251 if ((!PathIsDirectory(path) || handleAsFile) && !SUCCEEDED(pfd->SetFileName(CPathUtils::GetFileNameFromPath(path))))
252 return false;
254 // Set a title
255 if (title)
257 CString temp;
258 temp.LoadString(title);
259 CStringUtils::RemoveAccelerators(temp);
260 pfd->SetTitle(temp);
262 CComPtr<IShellItem> psiDefaultFolder;
263 if (filterId)
265 CSelectFileFilter fileFilter(filterId);
266 if (!SUCCEEDED(pfd->SetFileTypes(fileFilter.GetCount(), fileFilter)))
267 return false;
268 if (filterId == 1602 || filterId == 2501) // IDS_GITEXEFILEFILTER || IDS_PROGRAMSFILEFILTER
270 pfd->SetClientGuid({ 0x323ca4b0, 0x62df, 0x4a08, { 0xa5, 0x5, 0x58, 0xde, 0xa2, 0xb9, 0x2d, 0xcd } });
271 if (SUCCEEDED(SHCreateItemFromParsingName(CPathUtils::GetProgramsDirectory(), nullptr, IID_PPV_ARGS(&psiDefaultFolder))))
272 pfd->SetDefaultFolder(psiDefaultFolder);
274 else if (filterId == 1120) // IDS_PUTTYKEYFILEFILTER
276 pfd->SetClientGuid({ 0x271dbd3b, 0x50da, 0x4148, { 0x95, 0xfd, 0x64, 0x73, 0x69, 0xd1, 0x74, 0x2 } });
277 if (SUCCEEDED(SHCreateItemFromParsingName(CPathUtils::GetDocumentsDirectory(), nullptr, IID_PPV_ARGS(&psiDefaultFolder))))
278 pfd->SetDefaultFolder(psiDefaultFolder);
282 if (defaultExt && !SUCCEEDED(pfd->SetDefaultExtension(defaultExt)))
283 return false;
285 // set the default folder
286 CComPtr<IShellItem> psiFolder;
287 if (CStringUtils::StartsWith(path, L"\\") || path.Mid(1, 2) == L":\\")
289 CString dir = path;
290 if (!PathIsDirectory(dir) || handleAsFile)
292 if (PathRemoveFileSpec(dir.GetBuffer()))
293 dir.ReleaseBuffer();
294 else
295 dir.Empty();
297 if (!dir.IsEmpty() && SUCCEEDED(SHCreateItemFromParsingName(dir, nullptr, IID_PPV_ARGS(&psiFolder))))
298 pfd->SetFolder(psiFolder);
301 // Show the save/open file dialog
302 if (!SUCCEEDED(pfd->Show(hwndOwner)))
303 return false;
305 // Get the selection from the user
306 CComPtr<IShellItem> psiResult;
307 if (!SUCCEEDED(pfd->GetResult(&psiResult)))
308 return false;
310 PWSTR pszPath = nullptr;
311 if (!SUCCEEDED(psiResult->GetDisplayName(SIGDN_FILESYSPATH, &pszPath)))
312 return false;
314 path = CString(pszPath);
315 if (filterindex)
317 UINT fi = 0;
318 pfd->GetFileTypeIndex(&fi);
319 *filterindex = fi;
321 return true;
324 HICON CCommonAppUtils::LoadIconEx(UINT resourceId, UINT cx, UINT cy)
326 return ::LoadIconEx(AfxGetResourceHandle(), MAKEINTRESOURCE(resourceId), cx, cy);
329 void CCommonAppUtils::SetCharFormat(CWnd* window, DWORD mask , DWORD effects, const std::vector<CHARRANGE>& positions)
331 CHARFORMAT2 format = {};
332 format.cbSize = sizeof(CHARFORMAT2);
333 format.dwMask = mask;
334 format.dwEffects = effects;
335 format.crTextColor = effects;
337 for (const auto& range : positions)
339 window->SendMessage(EM_EXSETSEL, NULL, (LPARAM)&range);
340 window->SendMessage(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&format);
344 void CCommonAppUtils::SetCharFormat(CWnd* window, DWORD mask, DWORD effects )
346 CHARFORMAT2 format = {};
347 format.cbSize = sizeof(CHARFORMAT2);
348 format.dwMask = mask;
349 format.dwEffects = effects;
350 window->SendMessage(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&format);