Update OGDF to the latest stable release (v. 2015.05, Baobab)
[TortoiseGit.git] / src / TortoiseUDiff / MainWindow.cpp
blobb6d9d4f5b7bb9eb818402db1f8d7864259f18fcf
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2012-2018 - TortoiseGit
4 // Copyright (C) 2003-2014 - 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 "TortoiseUDiff.h"
22 #include "MainWindow.h"
23 #include "UnicodeUtils.h"
24 #include "StringUtils.h"
25 #include "TaskbarUUID.h"
26 #include "CreateProcessHelper.h"
27 #include "UDiffColors.h"
28 #include "registry.h"
29 #include "DPIAware.h"
30 #include "LoadIconEx.h"
32 const UINT TaskBarButtonCreated = RegisterWindowMessage(L"TaskbarButtonCreated");
34 CMainWindow::CMainWindow(HINSTANCE hInst, const WNDCLASSEX* wcx /* = nullptr*/)
35 : CWindow(hInst, wcx)
36 , m_bShowFindBar(false)
37 , m_directFunction(0)
38 , m_directPointer(0)
39 , m_hWndEdit(nullptr)
40 , m_bMatchCase(false)
42 SetWindowTitle(L"TortoiseGitUDiff");
45 CMainWindow::~CMainWindow(void)
49 bool CMainWindow::RegisterAndCreateWindow()
51 WNDCLASSEX wcx;
53 // Fill in the window class structure with default parameters
54 wcx.cbSize = sizeof(WNDCLASSEX);
55 wcx.style = CS_HREDRAW | CS_VREDRAW;
56 wcx.lpfnWndProc = CWindow::stWinMsgHandler;
57 wcx.cbClsExtra = 0;
58 wcx.cbWndExtra = 0;
59 wcx.hInstance = hResource;
60 wcx.hCursor = nullptr;
61 ResString clsname(hResource, IDS_APP_TITLE);
62 wcx.lpszClassName = clsname;
63 wcx.hIcon = LoadIconEx(hResource, MAKEINTRESOURCE(IDI_TORTOISEUDIFF), GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON));
64 wcx.hbrBackground = (HBRUSH)(COLOR_3DFACE+1);
65 wcx.lpszMenuName = MAKEINTRESOURCE(IDC_TORTOISEUDIFF);
66 wcx.hIconSm = LoadIconEx(wcx.hInstance, MAKEINTRESOURCE(IDI_TORTOISEUDIFF));
67 if (RegisterWindow(&wcx))
69 if (Create(WS_CAPTION | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_SIZEBOX | WS_SYSMENU | WS_CLIPCHILDREN, nullptr))
71 m_FindBar.SetParent(*this);
72 m_FindBar.Create(::hResource, IDD_FINDBAR, *this);
73 UpdateWindow(*this);
74 return true;
77 return false;
80 LRESULT CALLBACK CMainWindow::WinMsgHandler(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
82 if (uMsg == TaskBarButtonCreated)
84 SetUUIDOverlayIcon(hwnd);
86 switch (uMsg)
88 case WM_CREATE:
90 m_hwnd = hwnd;
91 Initialize();
93 break;
94 case WM_COMMAND:
96 return DoCommand(LOWORD(wParam));
98 break;
99 case WM_MOUSEWHEEL:
101 if (GET_KEYSTATE_WPARAM(wParam) == MK_SHIFT)
103 // scroll sideways
104 SendEditor(SCI_LINESCROLL, -GET_WHEEL_DELTA_WPARAM(wParam)/40, 0);
106 else
107 return DefWindowProc(hwnd, uMsg, wParam, lParam);
109 break;
110 case WM_SIZE:
112 RECT rect;
113 GetClientRect(*this, &rect);
114 if (m_bShowFindBar)
116 ::SetWindowPos(m_hWndEdit, HWND_TOP,
117 rect.left, rect.top,
118 rect.right - rect.left, rect.bottom - rect.top - int(30 * CDPIAware::Instance().ScaleFactorY()),
119 SWP_SHOWWINDOW);
120 ::SetWindowPos(m_FindBar, HWND_TOP,
121 rect.left, rect.bottom - int(30 * CDPIAware::Instance().ScaleFactorY()),
122 rect.right - rect.left, int(30 * CDPIAware::Instance().ScaleFactorY()),
123 SWP_SHOWWINDOW);
125 else
127 ::SetWindowPos(m_hWndEdit, HWND_TOP,
128 rect.left, rect.top,
129 rect.right-rect.left, rect.bottom-rect.top,
130 SWP_SHOWWINDOW);
131 ::ShowWindow(m_FindBar, SW_HIDE);
134 break;
135 case WM_GETMINMAXINFO:
137 MINMAXINFO * mmi = (MINMAXINFO*)lParam;
138 mmi->ptMinTrackSize.x = 100;
139 mmi->ptMinTrackSize.y = 100;
140 return 0;
142 break;
143 case WM_DESTROY:
144 PostQuitMessage(0);
145 break;
146 case WM_CLOSE:
147 ::DestroyWindow(m_hwnd);
148 break;
149 case WM_SETFOCUS:
150 SetFocus(m_hWndEdit);
151 break;
152 case WM_SYSCOLORCHANGE:
153 SetupColors(true);
154 break;
155 case COMMITMONITOR_FINDMSGNEXT:
157 SendEditor(SCI_CHARRIGHT);
158 SendEditor(SCI_SEARCHANCHOR);
159 m_bMatchCase = !!wParam;
160 m_findtext = (LPCTSTR)lParam;
161 SendEditor(SCI_SEARCHNEXT, m_bMatchCase ? SCFIND_MATCHCASE : 0, (LPARAM)CUnicodeUtils::StdGetUTF8(m_findtext).c_str());
162 SendEditor(SCI_SCROLLCARET);
164 break;
165 case COMMITMONITOR_FINDMSGPREV:
167 SendEditor(SCI_SEARCHANCHOR);
168 m_bMatchCase = !!wParam;
169 m_findtext = (LPCTSTR)lParam;
170 SendEditor(SCI_SEARCHPREV, m_bMatchCase ? SCFIND_MATCHCASE : 0, (LPARAM)CUnicodeUtils::StdGetUTF8(m_findtext).c_str());
171 SendEditor(SCI_SCROLLCARET);
173 break;
174 case COMMITMONITOR_FINDEXIT:
176 RECT rect;
177 GetClientRect(*this, &rect);
178 m_bShowFindBar = false;
179 ::ShowWindow(m_FindBar, SW_HIDE);
180 ::SetWindowPos(m_hWndEdit, HWND_TOP,
181 rect.left, rect.top,
182 rect.right-rect.left, rect.bottom-rect.top,
183 SWP_SHOWWINDOW);
185 break;
186 case COMMITMONITOR_FINDRESET:
187 SendEditor(SCI_SETSELECTIONSTART, 0);
188 SendEditor(SCI_SETSELECTIONEND, 0);
189 SendEditor(SCI_SEARCHANCHOR);
190 break;
191 default:
192 return DefWindowProc(hwnd, uMsg, wParam, lParam);
195 return 0;
198 LRESULT CMainWindow::DoCommand(int id)
200 switch (id)
202 case ID_FILE_OPEN:
203 loadOrSaveFile(true);
204 break;
205 case ID_FILE_SAVEAS:
206 loadOrSaveFile(false);
207 break;
208 case ID_FILE_SAVE:
209 loadOrSaveFile(false, m_filename);
210 break;
211 case ID_FILE_EXIT:
212 ::PostQuitMessage(0);
213 return 0;
214 case IDM_SHOWFINDBAR:
216 m_bShowFindBar = true;
217 ::ShowWindow(m_FindBar, SW_SHOW);
218 RECT rect;
219 GetClientRect(*this, &rect);
220 ::SetWindowPos(m_hWndEdit, HWND_TOP,
221 rect.left, rect.top,
222 rect.right - rect.left, rect.bottom - rect.top - int(30 * CDPIAware::Instance().ScaleFactorY()),
223 SWP_SHOWWINDOW);
224 ::SetWindowPos(m_FindBar, HWND_TOP,
225 rect.left, rect.bottom - int(30 * CDPIAware::Instance().ScaleFactorY()),
226 rect.right - rect.left, int(30 * CDPIAware::Instance().ScaleFactorY()),
227 SWP_SHOWWINDOW);
228 ::SetFocus(m_FindBar);
229 SendEditor(SCI_SETSELECTIONSTART, 0);
230 SendEditor(SCI_SETSELECTIONEND, 0);
231 SendEditor(SCI_SEARCHANCHOR);
233 break;
234 case IDM_FINDNEXT:
235 SendEditor(SCI_SEARCHANCHOR);
236 SendEditor(SCI_SEARCHNEXT, m_bMatchCase ? SCFIND_MATCHCASE : 0, (LPARAM)CUnicodeUtils::StdGetUTF8(m_findtext).c_str());
237 SendEditor(SCI_SCROLLCARET);
238 break;
239 case IDM_FINDPREV:
240 SendEditor(SCI_SEARCHANCHOR);
241 SendEditor(SCI_SEARCHPREV, m_bMatchCase ? SCFIND_MATCHCASE : 0, (LPARAM)CUnicodeUtils::StdGetUTF8(m_findtext).c_str());
242 SendEditor(SCI_SCROLLCARET);
243 break;
244 case IDM_FINDEXIT:
245 if (IsWindowVisible(m_FindBar))
247 RECT rect;
248 GetClientRect(*this, &rect);
249 m_bShowFindBar = false;
250 ::ShowWindow(m_FindBar, SW_HIDE);
251 ::SetWindowPos(m_hWndEdit, HWND_TOP,
252 rect.left, rect.top,
253 rect.right-rect.left, rect.bottom-rect.top,
254 SWP_SHOWWINDOW);
256 else
257 PostQuitMessage(0);
258 break;
259 case ID_FILE_SETTINGS:
261 tstring gitCmd = L" /command:settings /page:udiff";
262 RunCommand(gitCmd);
264 break;
265 case ID_FILE_APPLYPATCH:
267 std::wstring command = L" /diff:\"";
268 command += m_filename;
269 command += L'"';
270 std::wstring tortoiseMergePath = GetAppDirectory() + L"TortoiseGitMerge.exe";
271 CCreateProcessHelper::CreateProcessDetached(tortoiseMergePath.c_str(), command.c_str());
273 break;
274 case ID_FILE_PAGESETUP:
276 TCHAR localeInfo[3] = { 0 };
277 GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IMEASURE, localeInfo, 3);
278 // Metric system. '1' is US System
279 int defaultMargin = localeInfo[0] == '0' ? 2540 : 1000;
281 PAGESETUPDLG pdlg = {0};
282 pdlg.lStructSize = sizeof(PAGESETUPDLG);
283 pdlg.hwndOwner = *this;
284 pdlg.hInstance = nullptr;
285 pdlg.Flags = PSD_DEFAULTMINMARGINS|PSD_MARGINS|PSD_DISABLEPAPER|PSD_DISABLEORIENTATION;
286 if (localeInfo[0] == '0')
287 pdlg.Flags |= PSD_INHUNDREDTHSOFMILLIMETERS;
289 CRegStdDWORD m_regMargLeft = CRegStdDWORD(L"Software\\TortoiseGit\\UDiffpagesetupmarginleft", defaultMargin);
290 CRegStdDWORD m_regMargTop = CRegStdDWORD(L"Software\\TortoiseGit\\UDiffpagesetupmargintop", defaultMargin);
291 CRegStdDWORD m_regMargRight = CRegStdDWORD(L"Software\\TortoiseGit\\UDiffpagesetupmarginright", defaultMargin);
292 CRegStdDWORD m_regMargBottom = CRegStdDWORD(L"Software\\TortoiseGit\\UDiffpagesetupmarginbottom", defaultMargin);
294 pdlg.rtMargin.left = (long)(DWORD)m_regMargLeft;
295 pdlg.rtMargin.top = (long)(DWORD)m_regMargTop;
296 pdlg.rtMargin.right = (long)(DWORD)m_regMargRight;
297 pdlg.rtMargin.bottom = (long)(DWORD)m_regMargBottom;
299 if (!PageSetupDlg(&pdlg))
300 return false;
302 m_regMargLeft = pdlg.rtMargin.left;
303 m_regMargTop = pdlg.rtMargin.top;
304 m_regMargRight = pdlg.rtMargin.right;
305 m_regMargBottom = pdlg.rtMargin.bottom;
307 break;
308 case ID_FILE_PRINT:
310 PRINTDLGEX pdlg = {0};
311 pdlg.lStructSize = sizeof(PRINTDLGEX);
312 pdlg.hwndOwner = *this;
313 pdlg.hInstance = nullptr;
314 pdlg.Flags = PD_USEDEVMODECOPIESANDCOLLATE | PD_ALLPAGES | PD_RETURNDC | PD_NOCURRENTPAGE | PD_NOPAGENUMS;
315 pdlg.nMinPage = 1;
316 pdlg.nMaxPage = 0xffffU; // We do not know how many pages in the document
317 pdlg.nCopies = 1;
318 pdlg.hDC = 0;
319 pdlg.nStartPage = START_PAGE_GENERAL;
321 // See if a range has been selected
322 auto startPos = (Sci_Position)SendEditor(SCI_GETSELECTIONSTART);
323 auto endPos = (Sci_Position)SendEditor(SCI_GETSELECTIONEND);
325 if (startPos == endPos)
326 pdlg.Flags |= PD_NOSELECTION;
327 else
328 pdlg.Flags |= PD_SELECTION;
330 HRESULT hResult = PrintDlgEx(&pdlg);
331 if ((hResult != S_OK) || (pdlg.dwResultAction != PD_RESULT_PRINT))
332 return 0;
334 // reset all indicators
335 auto endpos = (int)SendEditor(SCI_GETLENGTH);
336 for (int i = INDIC_CONTAINER; i <= INDIC_MAX; ++i)
338 SendEditor(SCI_SETINDICATORCURRENT, i);
339 SendEditor(SCI_INDICATORCLEARRANGE, 0, endpos);
341 // store and reset UI settings
342 auto viewws = (int)SendEditor(SCI_GETVIEWWS);
343 SendEditor(SCI_SETVIEWWS, 0);
344 auto edgemode = (int)SendEditor(SCI_GETEDGEMODE);
345 SendEditor(SCI_SETEDGEMODE, EDGE_NONE);
346 SendEditor(SCI_SETWRAPVISUALFLAGS, SC_WRAPVISUALFLAG_END);
348 HDC hdc = pdlg.hDC;
350 RECT rectMargins, rectPhysMargins;
351 POINT ptPage;
352 POINT ptDpi;
354 // Get printer resolution
355 ptDpi.x = GetDeviceCaps(hdc, LOGPIXELSX); // dpi in X direction
356 ptDpi.y = GetDeviceCaps(hdc, LOGPIXELSY); // dpi in Y direction
358 // Start by getting the physical page size (in device units).
359 ptPage.x = GetDeviceCaps(hdc, PHYSICALWIDTH); // device units
360 ptPage.y = GetDeviceCaps(hdc, PHYSICALHEIGHT); // device units
362 // Get the dimensions of the unprintable
363 // part of the page (in device units).
364 rectPhysMargins.left = GetDeviceCaps(hdc, PHYSICALOFFSETX);
365 rectPhysMargins.top = GetDeviceCaps(hdc, PHYSICALOFFSETY);
367 // To get the right and lower unprintable area,
368 // we take the entire width and height of the paper and
369 // subtract everything else.
370 rectPhysMargins.right = ptPage.x // total paper width
371 - GetDeviceCaps(hdc, HORZRES) // printable width
372 - rectPhysMargins.left; // left unprintable margin
374 rectPhysMargins.bottom = ptPage.y // total paper height
375 - GetDeviceCaps(hdc, VERTRES) // printable height
376 - rectPhysMargins.top; // right unprintable margin
378 TCHAR localeInfo[3] = { 0 };
379 GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IMEASURE, localeInfo, 3);
380 // Metric system. '1' is US System
381 int defaultMargin = localeInfo[0] == '0' ? 2540 : 1000;
382 RECT pagesetupMargin;
383 CRegStdDWORD m_regMargLeft = CRegStdDWORD(L"Software\\TortoiseGit\\UDiffpagesetupmarginleft", defaultMargin);
384 CRegStdDWORD m_regMargTop = CRegStdDWORD(L"Software\\TortoiseGit\\UDiffpagesetupmargintop", defaultMargin);
385 CRegStdDWORD m_regMargRight = CRegStdDWORD(L"Software\\TortoiseGit\\UDiffpagesetupmarginright", defaultMargin);
386 CRegStdDWORD m_regMargBottom = CRegStdDWORD(L"Software\\TortoiseGit\\UDiffpagesetupmarginbottom", defaultMargin);
388 pagesetupMargin.left = (long)(DWORD)m_regMargLeft;
389 pagesetupMargin.top = (long)(DWORD)m_regMargTop;
390 pagesetupMargin.right = (long)(DWORD)m_regMargRight;
391 pagesetupMargin.bottom = (long)(DWORD)m_regMargBottom;
393 if (pagesetupMargin.left != 0 || pagesetupMargin.right != 0 ||
394 pagesetupMargin.top != 0 || pagesetupMargin.bottom != 0)
396 RECT rectSetup;
398 // Convert the hundredths of millimeters (HiMetric) or
399 // thousandths of inches (HiEnglish) margin values
400 // from the Page Setup dialog to device units.
401 // (There are 2540 hundredths of a mm in an inch.)
402 if (localeInfo[0] == '0')
404 // Metric system. '1' is US System
405 rectSetup.left = MulDiv (pagesetupMargin.left, ptDpi.x, 2540);
406 rectSetup.top = MulDiv (pagesetupMargin.top, ptDpi.y, 2540);
407 rectSetup.right = MulDiv(pagesetupMargin.right, ptDpi.x, 2540);
408 rectSetup.bottom = MulDiv(pagesetupMargin.bottom, ptDpi.y, 2540);
410 else
412 rectSetup.left = MulDiv(pagesetupMargin.left, ptDpi.x, 1000);
413 rectSetup.top = MulDiv(pagesetupMargin.top, ptDpi.y, 1000);
414 rectSetup.right = MulDiv(pagesetupMargin.right, ptDpi.x, 1000);
415 rectSetup.bottom = MulDiv(pagesetupMargin.bottom, ptDpi.y, 1000);
418 // Don't reduce margins below the minimum printable area
419 rectMargins.left = max(rectPhysMargins.left, rectSetup.left);
420 rectMargins.top = max(rectPhysMargins.top, rectSetup.top);
421 rectMargins.right = max(rectPhysMargins.right, rectSetup.right);
422 rectMargins.bottom = max(rectPhysMargins.bottom, rectSetup.bottom);
424 else
426 rectMargins.left = rectPhysMargins.left;
427 rectMargins.top = rectPhysMargins.top;
428 rectMargins.right = rectPhysMargins.right;
429 rectMargins.bottom = rectPhysMargins.bottom;
432 // rectMargins now contains the values used to shrink the printable
433 // area of the page.
435 // Convert device coordinates into logical coordinates
436 DPtoLP(hdc, (LPPOINT) &rectMargins, 2);
437 DPtoLP(hdc, (LPPOINT)&rectPhysMargins, 2);
439 // Convert page size to logical units and we're done!
440 DPtoLP(hdc, (LPPOINT) &ptPage, 1);
443 DOCINFO di = {sizeof(DOCINFO), 0, 0, 0, 0};
444 di.lpszDocName = m_filename.c_str();
445 di.lpszOutput = 0;
446 di.lpszDatatype = 0;
447 di.fwType = 0;
448 if (::StartDoc(hdc, &di) < 0)
450 ::DeleteDC(hdc);
451 return 0;
454 size_t lengthDoc = (int)SendEditor(SCI_GETLENGTH);
455 size_t lengthDocMax = lengthDoc;
456 size_t lengthPrinted = 0;
458 // Requested to print selection
459 if (pdlg.Flags & PD_SELECTION)
461 if (startPos > endPos)
463 lengthPrinted = endPos;
464 lengthDoc = startPos;
466 else
468 lengthPrinted = startPos;
469 lengthDoc = endPos;
472 if (lengthDoc > lengthDocMax)
473 lengthDoc = lengthDocMax;
476 // We must subtract the physical margins from the printable area
477 Sci_RangeToFormat frPrint;
478 frPrint.hdc = hdc;
479 frPrint.hdcTarget = hdc;
480 frPrint.rc.left = rectMargins.left - rectPhysMargins.left;
481 frPrint.rc.top = rectMargins.top - rectPhysMargins.top;
482 frPrint.rc.right = ptPage.x - rectMargins.right - rectPhysMargins.left;
483 frPrint.rc.bottom = ptPage.y - rectMargins.bottom - rectPhysMargins.top;
484 frPrint.rcPage.left = 0;
485 frPrint.rcPage.top = 0;
486 frPrint.rcPage.right = ptPage.x - rectPhysMargins.left - rectPhysMargins.right - 1;
487 frPrint.rcPage.bottom = ptPage.y - rectPhysMargins.top - rectPhysMargins.bottom - 1;
489 // Print each page
490 while (lengthPrinted < lengthDoc)
492 ::StartPage(hdc);
494 frPrint.chrg.cpMin = (long)lengthPrinted;
495 frPrint.chrg.cpMax = (long)lengthDoc;
497 lengthPrinted = SendEditor(SCI_FORMATRANGE, true, reinterpret_cast<LPARAM>(&frPrint));
499 ::EndPage(hdc);
502 SendEditor(SCI_FORMATRANGE, FALSE, 0);
504 ::EndDoc(hdc);
505 ::DeleteDC(hdc);
507 if (pdlg.hDevMode)
508 GlobalFree(pdlg.hDevMode);
509 if (pdlg.hDevNames)
510 GlobalFree(pdlg.hDevNames);
511 if (pdlg.lpPageRanges)
512 GlobalFree(pdlg.lpPageRanges);
514 // reset the UI
515 SendEditor(SCI_SETVIEWWS, viewws);
516 SendEditor(SCI_SETEDGEMODE, edgemode);
517 SendEditor(SCI_SETWRAPVISUALFLAGS, SC_WRAPVISUALFLAG_NONE);
519 break;
520 default:
521 break;
523 return 1;
526 std::wstring CMainWindow::GetAppDirectory()
528 std::wstring path;
529 DWORD len = 0;
530 DWORD bufferlen = MAX_PATH; // MAX_PATH is not the limit here!
533 bufferlen += MAX_PATH; // MAX_PATH is not the limit here!
534 auto pBuf = std::make_unique<TCHAR[]>(bufferlen);
535 len = GetModuleFileName(nullptr, pBuf.get(), bufferlen);
536 path = std::wstring(pBuf.get(), len);
537 } while(len == bufferlen);
538 path = path.substr(0, path.rfind('\\') + 1);
540 return path;
543 void CMainWindow::RunCommand(const std::wstring& command)
545 tstring tortoiseProcPath = GetAppDirectory() + L"TortoiseGitProc.exe";
546 CCreateProcessHelper::CreateProcessDetached(tortoiseProcPath.c_str(), command.c_str());
549 LRESULT CMainWindow::SendEditor(UINT Msg, WPARAM wParam, LPARAM lParam)
551 if (m_directFunction)
553 return ((SciFnDirect) m_directFunction)(m_directPointer, Msg, wParam, lParam);
555 return ::SendMessage(m_hWndEdit, Msg, wParam, lParam);
558 bool CMainWindow::Initialize()
560 m_hWndEdit = ::CreateWindow(
561 L"Scintilla",
562 L"Source",
563 WS_CHILD | WS_VSCROLL | WS_HSCROLL | WS_CLIPCHILDREN,
564 CW_USEDEFAULT, CW_USEDEFAULT,
565 CW_USEDEFAULT, CW_USEDEFAULT,
566 *this,
567 nullptr,
568 hResource,
569 nullptr);
570 if (!m_hWndEdit)
571 return false;
573 RECT rect;
574 GetClientRect(*this, &rect);
575 ::SetWindowPos(m_hWndEdit, HWND_TOP,
576 rect.left, rect.top,
577 rect.right-rect.left, rect.bottom-rect.top,
578 SWP_SHOWWINDOW);
580 m_directFunction = SendMessage(m_hWndEdit, SCI_GETDIRECTFUNCTION, 0, 0);
581 m_directPointer = SendMessage(m_hWndEdit, SCI_GETDIRECTPOINTER, 0, 0);
583 // Set up the global default style. These attributes are used wherever no explicit choices are made.
584 SetAStyle(STYLE_DEFAULT, ::GetSysColor(COLOR_WINDOWTEXT), ::GetSysColor(COLOR_WINDOW),
585 CRegStdDWORD(L"Software\\TortoiseGit\\UDiffFontSize", 10),
586 CUnicodeUtils::StdGetUTF8(CRegStdString(L"Software\\TortoiseGit\\UDiffFontName", L"Consolas")).c_str());
587 SendEditor(SCI_SETTABWIDTH, CRegStdDWORD(L"Software\\TortoiseGit\\UDiffTabSize", 4));
588 SendEditor(SCI_SETREADONLY, TRUE);
589 LRESULT pix = SendEditor(SCI_TEXTWIDTH, STYLE_LINENUMBER, (LPARAM)"_99999");
590 SendEditor(SCI_SETMARGINWIDTHN, 0, pix);
591 SendEditor(SCI_SETMARGINWIDTHN, 1);
592 SendEditor(SCI_SETMARGINWIDTHN, 2);
593 //Set the default windows colors for edit controls
594 SetupColors(false);
595 if (CRegStdDWORD(L"Software\\TortoiseGit\\ScintillaDirect2D", FALSE) != FALSE)
597 SendEditor(SCI_SETTECHNOLOGY, SC_TECHNOLOGY_DIRECTWRITERETAIN);
598 SendEditor(SCI_SETBUFFEREDDRAW, 0);
600 SendEditor(SCI_SETVIEWWS, 1);
601 SendEditor(SCI_SETWHITESPACESIZE, 2);
602 SendEditor(SCI_STYLESETVISIBLE, STYLE_CONTROLCHAR, TRUE);
604 return true;
607 bool CMainWindow::LoadFile(HANDLE hFile)
609 InitEditor();
610 char data[4096] = { 0 };
611 DWORD dwRead = 0;
613 BOOL bRet = ReadFile(hFile, data, sizeof(data), &dwRead, nullptr);
614 bool bUTF8 = IsUTF8(data, dwRead);
615 while ((dwRead > 0) && (bRet))
617 SendEditor(SCI_ADDTEXT, dwRead,
618 reinterpret_cast<LPARAM>(static_cast<char *>(data)));
619 bRet = ReadFile(hFile, data, sizeof(data), &dwRead, nullptr);
621 SetupWindow(bUTF8);
622 return true;
625 bool CMainWindow::LoadFile(LPCTSTR filename)
627 InitEditor();
628 FILE* fp = nullptr;
629 _wfopen_s(&fp, filename, L"rb");
630 if (!fp)
631 return false;
633 //SetTitle();
634 char data[4096] = { 0 };
635 size_t lenFile = fread(data, 1, sizeof(data), fp);
636 bool bUTF8 = IsUTF8(data, lenFile);
637 while (lenFile > 0)
639 SendEditor(SCI_ADDTEXT, lenFile,
640 reinterpret_cast<LPARAM>(static_cast<char *>(data)));
641 lenFile = fread(data, 1, sizeof(data), fp);
643 fclose(fp);
644 SetupWindow(bUTF8);
645 m_filename = filename;
646 return true;
649 void CMainWindow::InitEditor()
651 SendEditor(SCI_SETREADONLY, FALSE);
652 SendEditor(SCI_CLEARALL);
653 SendEditor(EM_EMPTYUNDOBUFFER);
654 SendEditor(SCI_SETSAVEPOINT);
655 SendEditor(SCI_CANCEL);
656 SendEditor(SCI_SETUNDOCOLLECTION, 0);
659 void CMainWindow::SetupWindow(bool bUTF8)
661 SendEditor(SCI_SETCODEPAGE, bUTF8 ? SC_CP_UTF8 : GetACP());
663 SendEditor(SCI_SETUNDOCOLLECTION, 1);
664 ::SetFocus(m_hWndEdit);
665 SendEditor(EM_EMPTYUNDOBUFFER);
666 SendEditor(SCI_SETSAVEPOINT);
667 SendEditor(SCI_GOTOPOS, 0);
669 SetupColors(true);
671 ::ShowWindow(m_hWndEdit, SW_SHOW);
674 void CMainWindow::SetupColors(bool recolorize)
676 SendEditor(SCI_STYLESETFORE, STYLE_DEFAULT, ::GetSysColor(COLOR_WINDOWTEXT));
677 SendEditor(SCI_STYLESETBACK, STYLE_DEFAULT, ::GetSysColor(COLOR_WINDOW));
678 SendEditor(SCI_SETSELFORE, TRUE, ::GetSysColor(COLOR_HIGHLIGHTTEXT));
679 SendEditor(SCI_SETSELBACK, TRUE, ::GetSysColor(COLOR_HIGHLIGHT));
680 SendEditor(SCI_SETCARETFORE, ::GetSysColor(COLOR_WINDOWTEXT));
682 SendEditor(SCI_SETWHITESPACEFORE, true, ::GetSysColor(COLOR_3DSHADOW));
684 SendEditor(SCI_CLEARDOCUMENTSTYLE, 0, 0);
686 HIGHCONTRAST highContrast = { 0 };
687 highContrast.cbSize = sizeof(HIGHCONTRAST);
688 if (SystemParametersInfo(SPI_GETHIGHCONTRAST, 0, &highContrast, 0) == TRUE && (highContrast.dwFlags & HCF_HIGHCONTRASTON))
690 SendEditor(SCI_SETLEXER, SCLEX_NULL);
691 return;
694 //SetAStyle(SCE_DIFF_DEFAULT, RGB(0, 0, 0));
695 SetAStyle(SCE_DIFF_COMMAND,
696 CRegStdDWORD(L"Software\\TortoiseGit\\UDiffForeCommandColor", UDIFF_COLORFORECOMMAND),
697 CRegStdDWORD(L"Software\\TortoiseGit\\UDiffBackCommandColor", UDIFF_COLORBACKCOMMAND));
698 SetAStyle(SCE_DIFF_POSITION,
699 CRegStdDWORD(L"Software\\TortoiseGit\\UDiffForePositionColor", UDIFF_COLORFOREPOSITION),
700 CRegStdDWORD(L"Software\\TortoiseGit\\UDiffBackPositionColor", UDIFF_COLORBACKPOSITION));
701 SetAStyle(SCE_DIFF_HEADER,
702 CRegStdDWORD(L"Software\\TortoiseGit\\UDiffForeHeaderColor", UDIFF_COLORFOREHEADER),
703 CRegStdDWORD(L"Software\\TortoiseGit\\UDiffBackHeaderColor", UDIFF_COLORBACKHEADER));
704 SetAStyle(SCE_DIFF_COMMENT,
705 CRegStdDWORD(L"Software\\TortoiseGit\\UDiffForeCommentColor", UDIFF_COLORFORECOMMENT),
706 CRegStdDWORD(L"Software\\TortoiseGit\\UDiffBackCommentColor", UDIFF_COLORBACKCOMMENT));
707 SendEditor(SCI_STYLESETBOLD, SCE_DIFF_COMMENT, TRUE);
709 SetAStyle(SCE_DIFF_ADDED,
710 CRegStdDWORD(L"Software\\TortoiseGit\\UDiffForeAddedColor", UDIFF_COLORFOREADDED),
711 CRegStdDWORD(L"Software\\TortoiseGit\\UDiffBackAddedColor", UDIFF_COLORBACKADDED));
712 SetAStyle(SCE_DIFF_DELETED,
713 CRegStdDWORD(L"Software\\TortoiseGit\\UDiffForeRemovedColor", UDIFF_COLORFOREREMOVED),
714 CRegStdDWORD(L"Software\\TortoiseGit\\UDiffBackRemovedColor", UDIFF_COLORBACKREMOVED));
716 SendEditor(SCI_SETLEXER, SCLEX_DIFF);
717 SendEditor(SCI_SETKEYWORDS, 0, (LPARAM)"revision");
719 if (recolorize)
720 SendEditor(SCI_COLOURISE, 0, -1);
723 bool CMainWindow::SaveFile(LPCTSTR filename)
725 FILE* fp = nullptr;
726 _wfopen_s(&fp, filename, L"w+b");
727 if (!fp)
728 return false;
730 auto len = (int)SendEditor(SCI_GETTEXT, 0, 0);
731 auto data = std::make_unique<char[]>(len + 1);
732 SendEditor(SCI_GETTEXT, len, reinterpret_cast<LPARAM>(static_cast<char *>(data.get())));
733 fwrite(data.get(), sizeof(char), len-1, fp);
734 fclose(fp);
736 SendEditor(SCI_SETSAVEPOINT);
737 ::ShowWindow(m_hWndEdit, SW_SHOW);
738 return true;
741 void CMainWindow::SetTitle(LPCTSTR title)
743 size_t len = wcslen(title);
744 auto pBuf = std::make_unique<TCHAR[]>(len + 40);
745 swprintf_s(pBuf.get(), len + 40, L"%s - TortoiseGitUDiff", title);
746 SetWindowTitle(std::wstring(pBuf.get()));
749 void CMainWindow::SetAStyle(int style, COLORREF fore, COLORREF back, int size, const char *face)
751 SendEditor(SCI_STYLESETFORE, style, fore);
752 SendEditor(SCI_STYLESETBACK, style, back);
753 if (size >= 1)
754 SendEditor(SCI_STYLESETSIZE, style, size);
755 if (face)
756 SendEditor(SCI_STYLESETFONT, style, reinterpret_cast<LPARAM>(face));
759 bool CMainWindow::IsUTF8(LPVOID pBuffer, size_t cb)
761 if (cb < 2)
762 return true;
763 UINT16 * pVal16 = (UINT16 *)pBuffer;
764 UINT8 * pVal8 = (UINT8 *)(pVal16+1);
765 // scan the whole buffer for a 0x0000 sequence
766 // if found, we assume a binary file
767 for (size_t i=0; i<(cb-2); i=i+2)
769 if (0x0000 == *pVal16++)
770 return false;
772 pVal16 = (UINT16 *)pBuffer;
773 if (*pVal16 == 0xFEFF)
774 return false;
775 if (cb < 3)
776 return false;
777 if (*pVal16 == 0xBBEF)
779 if (*pVal8 == 0xBF)
780 return true;
782 // check for illegal UTF8 chars
783 pVal8 = (UINT8 *)pBuffer;
784 for (size_t i=0; i<cb; ++i)
786 if ((*pVal8 == 0xC0)||(*pVal8 == 0xC1)||(*pVal8 >= 0xF5))
787 return false;
788 pVal8++;
790 pVal8 = (UINT8 *)pBuffer;
791 bool bUTF8 = false;
792 for (size_t i=0; i<(cb-3); ++i)
794 if ((*pVal8 & 0xE0)==0xC0)
796 pVal8++;i++;
797 if ((*pVal8 & 0xC0)!=0x80)
798 return false;
799 bUTF8 = true;
801 else if ((*pVal8 & 0xF0)==0xE0)
803 pVal8++;i++;
804 if ((*pVal8 & 0xC0)!=0x80)
805 return false;
806 pVal8++;i++;
807 if ((*pVal8 & 0xC0)!=0x80)
808 return false;
809 bUTF8 = true;
811 else if ((*pVal8 & 0xF8)==0xF0)
813 pVal8++;i++;
814 if ((*pVal8 & 0xC0)!=0x80)
815 return false;
816 pVal8++;i++;
817 if ((*pVal8 & 0xC0)!=0x80)
818 return false;
819 pVal8++;i++;
820 if ((*pVal8 & 0xC0)!=0x80)
821 return false;
822 bUTF8 = true;
824 else if (*pVal8 >= 0x80)
825 return false;
827 pVal8++;
829 if (bUTF8)
830 return true;
831 return false;
834 void CMainWindow::loadOrSaveFile(bool doLoad, const std::wstring& filename /* = L"" */)
836 OPENFILENAME ofn = {0}; // common dialog box structure
837 TCHAR szFile[MAX_PATH] = {0}; // buffer for file name
838 // Initialize OPENFILENAME
839 ofn.lStructSize = sizeof(OPENFILENAME);
840 ofn.hwndOwner = *this;
841 ofn.lpstrFile = szFile;
842 ofn.nMaxFile = sizeof(szFile)/sizeof(TCHAR);
843 TCHAR filter[1024] = { 0 };
844 LoadString(::hResource, IDS_PATCHFILEFILTER, filter, sizeof(filter)/sizeof(TCHAR));
845 CStringUtils::PipesToNulls(filter);
846 ofn.lpstrFilter = filter;
847 ofn.nFilterIndex = 1;
848 ofn.lpstrFileTitle = nullptr;
849 ofn.lpstrDefExt = L"diff";
850 ofn.nMaxFileTitle = 0;
851 ofn.lpstrInitialDir = nullptr;
852 TCHAR fileTitle[1024] = { 0 };
853 LoadString(::hResource, doLoad ? IDS_OPENPATCH : IDS_SAVEPATCH, fileTitle, sizeof(fileTitle)/sizeof(TCHAR));
854 ofn.lpstrTitle = fileTitle;
855 ofn.Flags = OFN_ENABLESIZING | OFN_EXPLORER;
856 if(doLoad)
857 ofn.Flags |= OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
858 else
859 ofn.Flags |= OFN_OVERWRITEPROMPT;
860 // Display the Open dialog box.
861 if( doLoad )
863 if (GetOpenFileName(&ofn)==TRUE)
865 LoadFile(ofn.lpstrFile);
868 else
870 if (filename.empty())
872 if (GetSaveFileName(&ofn)==TRUE)
874 SaveFile(ofn.lpstrFile);
877 else
878 SaveFile(filename.c_str());