Fix dragging the slider in TortoiseIDiff when only two images are shown
[TortoiseGit.git] / src / TortoiseUDiff / MainWindow.cpp
blobe74b1f4551ba18d30c7e5aa7986cdeed21d7f0c0
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"
31 const UINT TaskBarButtonCreated = RegisterWindowMessage(L"TaskbarButtonCreated");
33 CMainWindow::CMainWindow(HINSTANCE hInst, const WNDCLASSEX* wcx /* = nullptr*/)
34 : CWindow(hInst, wcx)
35 , m_bShowFindBar(false)
36 , m_directFunction(0)
37 , m_directPointer(0)
38 , m_hWndEdit(nullptr)
39 , m_bMatchCase(false)
41 SetWindowTitle(L"TortoiseGitUDiff");
44 CMainWindow::~CMainWindow(void)
48 bool CMainWindow::RegisterAndCreateWindow()
50 WNDCLASSEX wcx;
52 // Fill in the window class structure with default parameters
53 wcx.cbSize = sizeof(WNDCLASSEX);
54 wcx.style = CS_HREDRAW | CS_VREDRAW;
55 wcx.lpfnWndProc = CWindow::stWinMsgHandler;
56 wcx.cbClsExtra = 0;
57 wcx.cbWndExtra = 0;
58 wcx.hInstance = hResource;
59 wcx.hCursor = nullptr;
60 ResString clsname(hResource, IDS_APP_TITLE);
61 wcx.lpszClassName = clsname;
62 wcx.hIcon = LoadIcon(hResource, MAKEINTRESOURCE(IDI_TORTOISEUDIFF));
63 wcx.hbrBackground = (HBRUSH)(COLOR_3DFACE+1);
64 wcx.lpszMenuName = MAKEINTRESOURCE(IDC_TORTOISEUDIFF);
65 wcx.hIconSm = LoadIcon(wcx.hInstance, MAKEINTRESOURCE(IDI_TORTOISEUDIFF));
66 if (RegisterWindow(&wcx))
68 if (Create(WS_CAPTION | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_SIZEBOX | WS_SYSMENU | WS_CLIPCHILDREN, nullptr))
70 m_FindBar.SetParent(*this);
71 m_FindBar.Create(::hResource, IDD_FINDBAR, *this);
72 UpdateWindow(*this);
73 return true;
76 return false;
79 LRESULT CALLBACK CMainWindow::WinMsgHandler(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
81 if (uMsg == TaskBarButtonCreated)
83 SetUUIDOverlayIcon(hwnd);
85 switch (uMsg)
87 case WM_CREATE:
89 m_hwnd = hwnd;
90 Initialize();
92 break;
93 case WM_COMMAND:
95 return DoCommand(LOWORD(wParam));
97 break;
98 case WM_MOUSEWHEEL:
100 if (GET_KEYSTATE_WPARAM(wParam) == MK_SHIFT)
102 // scroll sideways
103 SendEditor(SCI_LINESCROLL, -GET_WHEEL_DELTA_WPARAM(wParam)/40, 0);
105 else
106 return DefWindowProc(hwnd, uMsg, wParam, lParam);
108 break;
109 case WM_SIZE:
111 RECT rect;
112 GetClientRect(*this, &rect);
113 if (m_bShowFindBar)
115 ::SetWindowPos(m_hWndEdit, HWND_TOP,
116 rect.left, rect.top,
117 rect.right - rect.left, rect.bottom - rect.top - int(30 * CDPIAware::Instance().ScaleFactorY()),
118 SWP_SHOWWINDOW);
119 ::SetWindowPos(m_FindBar, HWND_TOP,
120 rect.left, rect.bottom - int(30 * CDPIAware::Instance().ScaleFactorY()),
121 rect.right - rect.left, int(30 * CDPIAware::Instance().ScaleFactorY()),
122 SWP_SHOWWINDOW);
124 else
126 ::SetWindowPos(m_hWndEdit, HWND_TOP,
127 rect.left, rect.top,
128 rect.right-rect.left, rect.bottom-rect.top,
129 SWP_SHOWWINDOW);
130 ::ShowWindow(m_FindBar, SW_HIDE);
133 break;
134 case WM_GETMINMAXINFO:
136 MINMAXINFO * mmi = (MINMAXINFO*)lParam;
137 mmi->ptMinTrackSize.x = 100;
138 mmi->ptMinTrackSize.y = 100;
139 return 0;
141 break;
142 case WM_DESTROY:
143 PostQuitMessage(0);
144 break;
145 case WM_CLOSE:
146 ::DestroyWindow(m_hwnd);
147 break;
148 case WM_SETFOCUS:
149 SetFocus(m_hWndEdit);
150 break;
151 case WM_SYSCOLORCHANGE:
152 SetupColors(true);
153 break;
154 case COMMITMONITOR_FINDMSGNEXT:
156 SendEditor(SCI_CHARRIGHT);
157 SendEditor(SCI_SEARCHANCHOR);
158 m_bMatchCase = !!wParam;
159 m_findtext = (LPCTSTR)lParam;
160 SendEditor(SCI_SEARCHNEXT, m_bMatchCase ? SCFIND_MATCHCASE : 0, (LPARAM)CUnicodeUtils::StdGetUTF8(m_findtext).c_str());
161 SendEditor(SCI_SCROLLCARET);
163 break;
164 case COMMITMONITOR_FINDMSGPREV:
166 SendEditor(SCI_SEARCHANCHOR);
167 m_bMatchCase = !!wParam;
168 m_findtext = (LPCTSTR)lParam;
169 SendEditor(SCI_SEARCHPREV, m_bMatchCase ? SCFIND_MATCHCASE : 0, (LPARAM)CUnicodeUtils::StdGetUTF8(m_findtext).c_str());
170 SendEditor(SCI_SCROLLCARET);
172 break;
173 case COMMITMONITOR_FINDEXIT:
175 RECT rect;
176 GetClientRect(*this, &rect);
177 m_bShowFindBar = false;
178 ::ShowWindow(m_FindBar, SW_HIDE);
179 ::SetWindowPos(m_hWndEdit, HWND_TOP,
180 rect.left, rect.top,
181 rect.right-rect.left, rect.bottom-rect.top,
182 SWP_SHOWWINDOW);
184 break;
185 case COMMITMONITOR_FINDRESET:
186 SendEditor(SCI_SETSELECTIONSTART, 0);
187 SendEditor(SCI_SETSELECTIONEND, 0);
188 SendEditor(SCI_SEARCHANCHOR);
189 break;
190 default:
191 return DefWindowProc(hwnd, uMsg, wParam, lParam);
194 return 0;
197 LRESULT CMainWindow::DoCommand(int id)
199 switch (id)
201 case ID_FILE_OPEN:
202 loadOrSaveFile(true);
203 break;
204 case ID_FILE_SAVEAS:
205 loadOrSaveFile(false);
206 break;
207 case ID_FILE_SAVE:
208 loadOrSaveFile(false, m_filename);
209 break;
210 case ID_FILE_EXIT:
211 ::PostQuitMessage(0);
212 return 0;
213 case IDM_SHOWFINDBAR:
215 m_bShowFindBar = true;
216 ::ShowWindow(m_FindBar, SW_SHOW);
217 RECT rect;
218 GetClientRect(*this, &rect);
219 ::SetWindowPos(m_hWndEdit, HWND_TOP,
220 rect.left, rect.top,
221 rect.right - rect.left, rect.bottom - rect.top - int(30 * CDPIAware::Instance().ScaleFactorY()),
222 SWP_SHOWWINDOW);
223 ::SetWindowPos(m_FindBar, HWND_TOP,
224 rect.left, rect.bottom - int(30 * CDPIAware::Instance().ScaleFactorY()),
225 rect.right - rect.left, int(30 * CDPIAware::Instance().ScaleFactorY()),
226 SWP_SHOWWINDOW);
227 ::SetFocus(m_FindBar);
228 SendEditor(SCI_SETSELECTIONSTART, 0);
229 SendEditor(SCI_SETSELECTIONEND, 0);
230 SendEditor(SCI_SEARCHANCHOR);
232 break;
233 case IDM_FINDNEXT:
234 SendEditor(SCI_SEARCHANCHOR);
235 SendEditor(SCI_SEARCHNEXT, m_bMatchCase ? SCFIND_MATCHCASE : 0, (LPARAM)CUnicodeUtils::StdGetUTF8(m_findtext).c_str());
236 SendEditor(SCI_SCROLLCARET);
237 break;
238 case IDM_FINDPREV:
239 SendEditor(SCI_SEARCHANCHOR);
240 SendEditor(SCI_SEARCHPREV, m_bMatchCase ? SCFIND_MATCHCASE : 0, (LPARAM)CUnicodeUtils::StdGetUTF8(m_findtext).c_str());
241 SendEditor(SCI_SCROLLCARET);
242 break;
243 case IDM_FINDEXIT:
244 if (IsWindowVisible(m_FindBar))
246 RECT rect;
247 GetClientRect(*this, &rect);
248 m_bShowFindBar = false;
249 ::ShowWindow(m_FindBar, SW_HIDE);
250 ::SetWindowPos(m_hWndEdit, HWND_TOP,
251 rect.left, rect.top,
252 rect.right-rect.left, rect.bottom-rect.top,
253 SWP_SHOWWINDOW);
255 else
256 PostQuitMessage(0);
257 break;
258 case ID_FILE_SETTINGS:
260 tstring gitCmd = L" /command:settings /page:udiff";
261 RunCommand(gitCmd);
263 break;
264 case ID_FILE_APPLYPATCH:
266 std::wstring command = L" /diff:\"";
267 command += m_filename;
268 command += L'"';
269 std::wstring tortoiseMergePath = GetAppDirectory() + L"TortoiseGitMerge.exe";
270 CCreateProcessHelper::CreateProcessDetached(tortoiseMergePath.c_str(), command.c_str());
272 break;
273 case ID_FILE_PAGESETUP:
275 TCHAR localeInfo[3] = { 0 };
276 GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IMEASURE, localeInfo, 3);
277 // Metric system. '1' is US System
278 int defaultMargin = localeInfo[0] == '0' ? 2540 : 1000;
280 PAGESETUPDLG pdlg = {0};
281 pdlg.lStructSize = sizeof(PAGESETUPDLG);
282 pdlg.hwndOwner = *this;
283 pdlg.hInstance = nullptr;
284 pdlg.Flags = PSD_DEFAULTMINMARGINS|PSD_MARGINS|PSD_DISABLEPAPER|PSD_DISABLEORIENTATION;
285 if (localeInfo[0] == '0')
286 pdlg.Flags |= PSD_INHUNDREDTHSOFMILLIMETERS;
288 CRegStdDWORD m_regMargLeft = CRegStdDWORD(L"Software\\TortoiseGit\\UDiffpagesetupmarginleft", defaultMargin);
289 CRegStdDWORD m_regMargTop = CRegStdDWORD(L"Software\\TortoiseGit\\UDiffpagesetupmargintop", defaultMargin);
290 CRegStdDWORD m_regMargRight = CRegStdDWORD(L"Software\\TortoiseGit\\UDiffpagesetupmarginright", defaultMargin);
291 CRegStdDWORD m_regMargBottom = CRegStdDWORD(L"Software\\TortoiseGit\\UDiffpagesetupmarginbottom", defaultMargin);
293 pdlg.rtMargin.left = (long)(DWORD)m_regMargLeft;
294 pdlg.rtMargin.top = (long)(DWORD)m_regMargTop;
295 pdlg.rtMargin.right = (long)(DWORD)m_regMargRight;
296 pdlg.rtMargin.bottom = (long)(DWORD)m_regMargBottom;
298 if (!PageSetupDlg(&pdlg))
299 return false;
301 m_regMargLeft = pdlg.rtMargin.left;
302 m_regMargTop = pdlg.rtMargin.top;
303 m_regMargRight = pdlg.rtMargin.right;
304 m_regMargBottom = pdlg.rtMargin.bottom;
306 break;
307 case ID_FILE_PRINT:
309 PRINTDLGEX pdlg = {0};
310 pdlg.lStructSize = sizeof(PRINTDLGEX);
311 pdlg.hwndOwner = *this;
312 pdlg.hInstance = nullptr;
313 pdlg.Flags = PD_USEDEVMODECOPIESANDCOLLATE | PD_ALLPAGES | PD_RETURNDC | PD_NOCURRENTPAGE | PD_NOPAGENUMS;
314 pdlg.nMinPage = 1;
315 pdlg.nMaxPage = 0xffffU; // We do not know how many pages in the document
316 pdlg.nCopies = 1;
317 pdlg.hDC = 0;
318 pdlg.nStartPage = START_PAGE_GENERAL;
320 // See if a range has been selected
321 auto startPos = (Sci_Position)SendEditor(SCI_GETSELECTIONSTART);
322 auto endPos = (Sci_Position)SendEditor(SCI_GETSELECTIONEND);
324 if (startPos == endPos)
325 pdlg.Flags |= PD_NOSELECTION;
326 else
327 pdlg.Flags |= PD_SELECTION;
329 HRESULT hResult = PrintDlgEx(&pdlg);
330 if ((hResult != S_OK) || (pdlg.dwResultAction != PD_RESULT_PRINT))
331 return 0;
333 // reset all indicators
334 auto endpos = (int)SendEditor(SCI_GETLENGTH);
335 for (int i = INDIC_CONTAINER; i <= INDIC_MAX; ++i)
337 SendEditor(SCI_SETINDICATORCURRENT, i);
338 SendEditor(SCI_INDICATORCLEARRANGE, 0, endpos);
340 // store and reset UI settings
341 auto viewws = (int)SendEditor(SCI_GETVIEWWS);
342 SendEditor(SCI_SETVIEWWS, 0);
343 auto edgemode = (int)SendEditor(SCI_GETEDGEMODE);
344 SendEditor(SCI_SETEDGEMODE, EDGE_NONE);
345 SendEditor(SCI_SETWRAPVISUALFLAGS, SC_WRAPVISUALFLAG_END);
347 HDC hdc = pdlg.hDC;
349 RECT rectMargins, rectPhysMargins;
350 POINT ptPage;
351 POINT ptDpi;
353 // Get printer resolution
354 ptDpi.x = GetDeviceCaps(hdc, LOGPIXELSX); // dpi in X direction
355 ptDpi.y = GetDeviceCaps(hdc, LOGPIXELSY); // dpi in Y direction
357 // Start by getting the physical page size (in device units).
358 ptPage.x = GetDeviceCaps(hdc, PHYSICALWIDTH); // device units
359 ptPage.y = GetDeviceCaps(hdc, PHYSICALHEIGHT); // device units
361 // Get the dimensions of the unprintable
362 // part of the page (in device units).
363 rectPhysMargins.left = GetDeviceCaps(hdc, PHYSICALOFFSETX);
364 rectPhysMargins.top = GetDeviceCaps(hdc, PHYSICALOFFSETY);
366 // To get the right and lower unprintable area,
367 // we take the entire width and height of the paper and
368 // subtract everything else.
369 rectPhysMargins.right = ptPage.x // total paper width
370 - GetDeviceCaps(hdc, HORZRES) // printable width
371 - rectPhysMargins.left; // left unprintable margin
373 rectPhysMargins.bottom = ptPage.y // total paper height
374 - GetDeviceCaps(hdc, VERTRES) // printable height
375 - rectPhysMargins.top; // right unprintable margin
377 TCHAR localeInfo[3] = { 0 };
378 GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IMEASURE, localeInfo, 3);
379 // Metric system. '1' is US System
380 int defaultMargin = localeInfo[0] == '0' ? 2540 : 1000;
381 RECT pagesetupMargin;
382 CRegStdDWORD m_regMargLeft = CRegStdDWORD(L"Software\\TortoiseGit\\UDiffpagesetupmarginleft", defaultMargin);
383 CRegStdDWORD m_regMargTop = CRegStdDWORD(L"Software\\TortoiseGit\\UDiffpagesetupmargintop", defaultMargin);
384 CRegStdDWORD m_regMargRight = CRegStdDWORD(L"Software\\TortoiseGit\\UDiffpagesetupmarginright", defaultMargin);
385 CRegStdDWORD m_regMargBottom = CRegStdDWORD(L"Software\\TortoiseGit\\UDiffpagesetupmarginbottom", defaultMargin);
387 pagesetupMargin.left = (long)(DWORD)m_regMargLeft;
388 pagesetupMargin.top = (long)(DWORD)m_regMargTop;
389 pagesetupMargin.right = (long)(DWORD)m_regMargRight;
390 pagesetupMargin.bottom = (long)(DWORD)m_regMargBottom;
392 if (pagesetupMargin.left != 0 || pagesetupMargin.right != 0 ||
393 pagesetupMargin.top != 0 || pagesetupMargin.bottom != 0)
395 RECT rectSetup;
397 // Convert the hundredths of millimeters (HiMetric) or
398 // thousandths of inches (HiEnglish) margin values
399 // from the Page Setup dialog to device units.
400 // (There are 2540 hundredths of a mm in an inch.)
401 if (localeInfo[0] == '0')
403 // Metric system. '1' is US System
404 rectSetup.left = MulDiv (pagesetupMargin.left, ptDpi.x, 2540);
405 rectSetup.top = MulDiv (pagesetupMargin.top, ptDpi.y, 2540);
406 rectSetup.right = MulDiv(pagesetupMargin.right, ptDpi.x, 2540);
407 rectSetup.bottom = MulDiv(pagesetupMargin.bottom, ptDpi.y, 2540);
409 else
411 rectSetup.left = MulDiv(pagesetupMargin.left, ptDpi.x, 1000);
412 rectSetup.top = MulDiv(pagesetupMargin.top, ptDpi.y, 1000);
413 rectSetup.right = MulDiv(pagesetupMargin.right, ptDpi.x, 1000);
414 rectSetup.bottom = MulDiv(pagesetupMargin.bottom, ptDpi.y, 1000);
417 // Don't reduce margins below the minimum printable area
418 rectMargins.left = max(rectPhysMargins.left, rectSetup.left);
419 rectMargins.top = max(rectPhysMargins.top, rectSetup.top);
420 rectMargins.right = max(rectPhysMargins.right, rectSetup.right);
421 rectMargins.bottom = max(rectPhysMargins.bottom, rectSetup.bottom);
423 else
425 rectMargins.left = rectPhysMargins.left;
426 rectMargins.top = rectPhysMargins.top;
427 rectMargins.right = rectPhysMargins.right;
428 rectMargins.bottom = rectPhysMargins.bottom;
431 // rectMargins now contains the values used to shrink the printable
432 // area of the page.
434 // Convert device coordinates into logical coordinates
435 DPtoLP(hdc, (LPPOINT) &rectMargins, 2);
436 DPtoLP(hdc, (LPPOINT)&rectPhysMargins, 2);
438 // Convert page size to logical units and we're done!
439 DPtoLP(hdc, (LPPOINT) &ptPage, 1);
442 DOCINFO di = {sizeof(DOCINFO), 0, 0, 0, 0};
443 di.lpszDocName = m_filename.c_str();
444 di.lpszOutput = 0;
445 di.lpszDatatype = 0;
446 di.fwType = 0;
447 if (::StartDoc(hdc, &di) < 0)
449 ::DeleteDC(hdc);
450 return 0;
453 size_t lengthDoc = (int)SendEditor(SCI_GETLENGTH);
454 size_t lengthDocMax = lengthDoc;
455 size_t lengthPrinted = 0;
457 // Requested to print selection
458 if (pdlg.Flags & PD_SELECTION)
460 if (startPos > endPos)
462 lengthPrinted = endPos;
463 lengthDoc = startPos;
465 else
467 lengthPrinted = startPos;
468 lengthDoc = endPos;
471 if (lengthDoc > lengthDocMax)
472 lengthDoc = lengthDocMax;
475 // We must subtract the physical margins from the printable area
476 Sci_RangeToFormat frPrint;
477 frPrint.hdc = hdc;
478 frPrint.hdcTarget = hdc;
479 frPrint.rc.left = rectMargins.left - rectPhysMargins.left;
480 frPrint.rc.top = rectMargins.top - rectPhysMargins.top;
481 frPrint.rc.right = ptPage.x - rectMargins.right - rectPhysMargins.left;
482 frPrint.rc.bottom = ptPage.y - rectMargins.bottom - rectPhysMargins.top;
483 frPrint.rcPage.left = 0;
484 frPrint.rcPage.top = 0;
485 frPrint.rcPage.right = ptPage.x - rectPhysMargins.left - rectPhysMargins.right - 1;
486 frPrint.rcPage.bottom = ptPage.y - rectPhysMargins.top - rectPhysMargins.bottom - 1;
488 // Print each page
489 while (lengthPrinted < lengthDoc)
491 ::StartPage(hdc);
493 frPrint.chrg.cpMin = (long)lengthPrinted;
494 frPrint.chrg.cpMax = (long)lengthDoc;
496 lengthPrinted = SendEditor(SCI_FORMATRANGE, true, reinterpret_cast<LPARAM>(&frPrint));
498 ::EndPage(hdc);
501 SendEditor(SCI_FORMATRANGE, FALSE, 0);
503 ::EndDoc(hdc);
504 ::DeleteDC(hdc);
506 if (pdlg.hDevMode)
507 GlobalFree(pdlg.hDevMode);
508 if (pdlg.hDevNames)
509 GlobalFree(pdlg.hDevNames);
510 if (pdlg.lpPageRanges)
511 GlobalFree(pdlg.lpPageRanges);
513 // reset the UI
514 SendEditor(SCI_SETVIEWWS, viewws);
515 SendEditor(SCI_SETEDGEMODE, edgemode);
516 SendEditor(SCI_SETWRAPVISUALFLAGS, SC_WRAPVISUALFLAG_NONE);
518 break;
519 default:
520 break;
522 return 1;
525 std::wstring CMainWindow::GetAppDirectory()
527 std::wstring path;
528 DWORD len = 0;
529 DWORD bufferlen = MAX_PATH; // MAX_PATH is not the limit here!
532 bufferlen += MAX_PATH; // MAX_PATH is not the limit here!
533 auto pBuf = std::make_unique<TCHAR[]>(bufferlen);
534 len = GetModuleFileName(nullptr, pBuf.get(), bufferlen);
535 path = std::wstring(pBuf.get(), len);
536 } while(len == bufferlen);
537 path = path.substr(0, path.rfind('\\') + 1);
539 return path;
542 void CMainWindow::RunCommand(const std::wstring& command)
544 tstring tortoiseProcPath = GetAppDirectory() + L"TortoiseGitProc.exe";
545 CCreateProcessHelper::CreateProcessDetached(tortoiseProcPath.c_str(), command.c_str());
548 LRESULT CMainWindow::SendEditor(UINT Msg, WPARAM wParam, LPARAM lParam)
550 if (m_directFunction)
552 return ((SciFnDirect) m_directFunction)(m_directPointer, Msg, wParam, lParam);
554 return ::SendMessage(m_hWndEdit, Msg, wParam, lParam);
557 bool CMainWindow::Initialize()
559 m_hWndEdit = ::CreateWindow(
560 L"Scintilla",
561 L"Source",
562 WS_CHILD | WS_VSCROLL | WS_HSCROLL | WS_CLIPCHILDREN,
563 CW_USEDEFAULT, CW_USEDEFAULT,
564 CW_USEDEFAULT, CW_USEDEFAULT,
565 *this,
566 nullptr,
567 hResource,
568 nullptr);
569 if (!m_hWndEdit)
570 return false;
572 RECT rect;
573 GetClientRect(*this, &rect);
574 ::SetWindowPos(m_hWndEdit, HWND_TOP,
575 rect.left, rect.top,
576 rect.right-rect.left, rect.bottom-rect.top,
577 SWP_SHOWWINDOW);
579 m_directFunction = SendMessage(m_hWndEdit, SCI_GETDIRECTFUNCTION, 0, 0);
580 m_directPointer = SendMessage(m_hWndEdit, SCI_GETDIRECTPOINTER, 0, 0);
582 // Set up the global default style. These attributes are used wherever no explicit choices are made.
583 SetAStyle(STYLE_DEFAULT, ::GetSysColor(COLOR_WINDOWTEXT), ::GetSysColor(COLOR_WINDOW),
584 CRegStdDWORD(L"Software\\TortoiseGit\\UDiffFontSize", 10),
585 CUnicodeUtils::StdGetUTF8(CRegStdString(L"Software\\TortoiseGit\\UDiffFontName", L"Consolas")).c_str());
586 SendEditor(SCI_SETTABWIDTH, CRegStdDWORD(L"Software\\TortoiseGit\\UDiffTabSize", 4));
587 SendEditor(SCI_SETREADONLY, TRUE);
588 LRESULT pix = SendEditor(SCI_TEXTWIDTH, STYLE_LINENUMBER, (LPARAM)"_99999");
589 SendEditor(SCI_SETMARGINWIDTHN, 0, pix);
590 SendEditor(SCI_SETMARGINWIDTHN, 1);
591 SendEditor(SCI_SETMARGINWIDTHN, 2);
592 //Set the default windows colors for edit controls
593 SetupColors(false);
594 if (CRegStdDWORD(L"Software\\TortoiseGit\\ScintillaDirect2D", FALSE) != FALSE)
596 SendEditor(SCI_SETTECHNOLOGY, SC_TECHNOLOGY_DIRECTWRITERETAIN);
597 SendEditor(SCI_SETBUFFEREDDRAW, 0);
599 SendEditor(SCI_SETVIEWWS, 1);
600 SendEditor(SCI_SETWHITESPACESIZE, 2);
601 SendEditor(SCI_STYLESETVISIBLE, STYLE_CONTROLCHAR, TRUE);
603 return true;
606 bool CMainWindow::LoadFile(HANDLE hFile)
608 InitEditor();
609 char data[4096] = { 0 };
610 DWORD dwRead = 0;
612 BOOL bRet = ReadFile(hFile, data, sizeof(data), &dwRead, nullptr);
613 bool bUTF8 = IsUTF8(data, dwRead);
614 while ((dwRead > 0) && (bRet))
616 SendEditor(SCI_ADDTEXT, dwRead,
617 reinterpret_cast<LPARAM>(static_cast<char *>(data)));
618 bRet = ReadFile(hFile, data, sizeof(data), &dwRead, nullptr);
620 SetupWindow(bUTF8);
621 return true;
624 bool CMainWindow::LoadFile(LPCTSTR filename)
626 InitEditor();
627 FILE* fp = nullptr;
628 _wfopen_s(&fp, filename, L"rb");
629 if (!fp)
630 return false;
632 //SetTitle();
633 char data[4096] = { 0 };
634 size_t lenFile = fread(data, 1, sizeof(data), fp);
635 bool bUTF8 = IsUTF8(data, lenFile);
636 while (lenFile > 0)
638 SendEditor(SCI_ADDTEXT, lenFile,
639 reinterpret_cast<LPARAM>(static_cast<char *>(data)));
640 lenFile = fread(data, 1, sizeof(data), fp);
642 fclose(fp);
643 SetupWindow(bUTF8);
644 m_filename = filename;
645 return true;
648 void CMainWindow::InitEditor()
650 SendEditor(SCI_SETREADONLY, FALSE);
651 SendEditor(SCI_CLEARALL);
652 SendEditor(EM_EMPTYUNDOBUFFER);
653 SendEditor(SCI_SETSAVEPOINT);
654 SendEditor(SCI_CANCEL);
655 SendEditor(SCI_SETUNDOCOLLECTION, 0);
658 void CMainWindow::SetupWindow(bool bUTF8)
660 SendEditor(SCI_SETCODEPAGE, bUTF8 ? SC_CP_UTF8 : GetACP());
662 SendEditor(SCI_SETUNDOCOLLECTION, 1);
663 ::SetFocus(m_hWndEdit);
664 SendEditor(EM_EMPTYUNDOBUFFER);
665 SendEditor(SCI_SETSAVEPOINT);
666 SendEditor(SCI_GOTOPOS, 0);
668 SetupColors(true);
670 ::ShowWindow(m_hWndEdit, SW_SHOW);
673 void CMainWindow::SetupColors(bool recolorize)
675 SendEditor(SCI_STYLESETFORE, STYLE_DEFAULT, ::GetSysColor(COLOR_WINDOWTEXT));
676 SendEditor(SCI_STYLESETBACK, STYLE_DEFAULT, ::GetSysColor(COLOR_WINDOW));
677 SendEditor(SCI_SETSELFORE, TRUE, ::GetSysColor(COLOR_HIGHLIGHTTEXT));
678 SendEditor(SCI_SETSELBACK, TRUE, ::GetSysColor(COLOR_HIGHLIGHT));
679 SendEditor(SCI_SETCARETFORE, ::GetSysColor(COLOR_WINDOWTEXT));
681 SendEditor(SCI_SETWHITESPACEFORE, true, ::GetSysColor(COLOR_3DSHADOW));
683 SendEditor(SCI_CLEARDOCUMENTSTYLE, 0, 0);
685 HIGHCONTRAST highContrast = { 0 };
686 highContrast.cbSize = sizeof(HIGHCONTRAST);
687 if (SystemParametersInfo(SPI_GETHIGHCONTRAST, 0, &highContrast, 0) == TRUE && (highContrast.dwFlags & HCF_HIGHCONTRASTON))
689 SendEditor(SCI_SETLEXER, SCLEX_NULL);
690 return;
693 //SetAStyle(SCE_DIFF_DEFAULT, RGB(0, 0, 0));
694 SetAStyle(SCE_DIFF_COMMAND,
695 CRegStdDWORD(L"Software\\TortoiseGit\\UDiffForeCommandColor", UDIFF_COLORFORECOMMAND),
696 CRegStdDWORD(L"Software\\TortoiseGit\\UDiffBackCommandColor", UDIFF_COLORBACKCOMMAND));
697 SetAStyle(SCE_DIFF_POSITION,
698 CRegStdDWORD(L"Software\\TortoiseGit\\UDiffForePositionColor", UDIFF_COLORFOREPOSITION),
699 CRegStdDWORD(L"Software\\TortoiseGit\\UDiffBackPositionColor", UDIFF_COLORBACKPOSITION));
700 SetAStyle(SCE_DIFF_HEADER,
701 CRegStdDWORD(L"Software\\TortoiseGit\\UDiffForeHeaderColor", UDIFF_COLORFOREHEADER),
702 CRegStdDWORD(L"Software\\TortoiseGit\\UDiffBackHeaderColor", UDIFF_COLORBACKHEADER));
703 SetAStyle(SCE_DIFF_COMMENT,
704 CRegStdDWORD(L"Software\\TortoiseGit\\UDiffForeCommentColor", UDIFF_COLORFORECOMMENT),
705 CRegStdDWORD(L"Software\\TortoiseGit\\UDiffBackCommentColor", UDIFF_COLORBACKCOMMENT));
706 SendEditor(SCI_STYLESETBOLD, SCE_DIFF_COMMENT, TRUE);
708 SetAStyle(SCE_DIFF_ADDED,
709 CRegStdDWORD(L"Software\\TortoiseGit\\UDiffForeAddedColor", UDIFF_COLORFOREADDED),
710 CRegStdDWORD(L"Software\\TortoiseGit\\UDiffBackAddedColor", UDIFF_COLORBACKADDED));
711 SetAStyle(SCE_DIFF_DELETED,
712 CRegStdDWORD(L"Software\\TortoiseGit\\UDiffForeRemovedColor", UDIFF_COLORFOREREMOVED),
713 CRegStdDWORD(L"Software\\TortoiseGit\\UDiffBackRemovedColor", UDIFF_COLORBACKREMOVED));
715 SendEditor(SCI_SETLEXER, SCLEX_DIFF);
716 SendEditor(SCI_SETKEYWORDS, 0, (LPARAM)"revision");
718 if (recolorize)
719 SendEditor(SCI_COLOURISE, 0, -1);
722 bool CMainWindow::SaveFile(LPCTSTR filename)
724 FILE* fp = nullptr;
725 _wfopen_s(&fp, filename, L"w+b");
726 if (!fp)
727 return false;
729 auto len = (int)SendEditor(SCI_GETTEXT, 0, 0);
730 auto data = std::make_unique<char[]>(len + 1);
731 SendEditor(SCI_GETTEXT, len, reinterpret_cast<LPARAM>(static_cast<char *>(data.get())));
732 fwrite(data.get(), sizeof(char), len-1, fp);
733 fclose(fp);
735 SendEditor(SCI_SETSAVEPOINT);
736 ::ShowWindow(m_hWndEdit, SW_SHOW);
737 return true;
740 void CMainWindow::SetTitle(LPCTSTR title)
742 size_t len = wcslen(title);
743 auto pBuf = std::make_unique<TCHAR[]>(len + 40);
744 swprintf_s(pBuf.get(), len + 40, L"%s - TortoiseGitUDiff", title);
745 SetWindowTitle(std::wstring(pBuf.get()));
748 void CMainWindow::SetAStyle(int style, COLORREF fore, COLORREF back, int size, const char *face)
750 SendEditor(SCI_STYLESETFORE, style, fore);
751 SendEditor(SCI_STYLESETBACK, style, back);
752 if (size >= 1)
753 SendEditor(SCI_STYLESETSIZE, style, size);
754 if (face)
755 SendEditor(SCI_STYLESETFONT, style, reinterpret_cast<LPARAM>(face));
758 bool CMainWindow::IsUTF8(LPVOID pBuffer, size_t cb)
760 if (cb < 2)
761 return true;
762 UINT16 * pVal16 = (UINT16 *)pBuffer;
763 UINT8 * pVal8 = (UINT8 *)(pVal16+1);
764 // scan the whole buffer for a 0x0000 sequence
765 // if found, we assume a binary file
766 for (size_t i=0; i<(cb-2); i=i+2)
768 if (0x0000 == *pVal16++)
769 return false;
771 pVal16 = (UINT16 *)pBuffer;
772 if (*pVal16 == 0xFEFF)
773 return false;
774 if (cb < 3)
775 return false;
776 if (*pVal16 == 0xBBEF)
778 if (*pVal8 == 0xBF)
779 return true;
781 // check for illegal UTF8 chars
782 pVal8 = (UINT8 *)pBuffer;
783 for (size_t i=0; i<cb; ++i)
785 if ((*pVal8 == 0xC0)||(*pVal8 == 0xC1)||(*pVal8 >= 0xF5))
786 return false;
787 pVal8++;
789 pVal8 = (UINT8 *)pBuffer;
790 bool bUTF8 = false;
791 for (size_t i=0; i<(cb-3); ++i)
793 if ((*pVal8 & 0xE0)==0xC0)
795 pVal8++;i++;
796 if ((*pVal8 & 0xC0)!=0x80)
797 return false;
798 bUTF8 = true;
800 else if ((*pVal8 & 0xF0)==0xE0)
802 pVal8++;i++;
803 if ((*pVal8 & 0xC0)!=0x80)
804 return false;
805 pVal8++;i++;
806 if ((*pVal8 & 0xC0)!=0x80)
807 return false;
808 bUTF8 = true;
810 else if ((*pVal8 & 0xF8)==0xF0)
812 pVal8++;i++;
813 if ((*pVal8 & 0xC0)!=0x80)
814 return false;
815 pVal8++;i++;
816 if ((*pVal8 & 0xC0)!=0x80)
817 return false;
818 pVal8++;i++;
819 if ((*pVal8 & 0xC0)!=0x80)
820 return false;
821 bUTF8 = true;
823 else if (*pVal8 >= 0x80)
824 return false;
826 pVal8++;
828 if (bUTF8)
829 return true;
830 return false;
833 void CMainWindow::loadOrSaveFile(bool doLoad, const std::wstring& filename /* = L"" */)
835 OPENFILENAME ofn = {0}; // common dialog box structure
836 TCHAR szFile[MAX_PATH] = {0}; // buffer for file name
837 // Initialize OPENFILENAME
838 ofn.lStructSize = sizeof(OPENFILENAME);
839 ofn.hwndOwner = *this;
840 ofn.lpstrFile = szFile;
841 ofn.nMaxFile = sizeof(szFile)/sizeof(TCHAR);
842 TCHAR filter[1024] = { 0 };
843 LoadString(::hResource, IDS_PATCHFILEFILTER, filter, sizeof(filter)/sizeof(TCHAR));
844 CStringUtils::PipesToNulls(filter);
845 ofn.lpstrFilter = filter;
846 ofn.nFilterIndex = 1;
847 ofn.lpstrFileTitle = nullptr;
848 ofn.lpstrDefExt = L"diff";
849 ofn.nMaxFileTitle = 0;
850 ofn.lpstrInitialDir = nullptr;
851 TCHAR fileTitle[1024] = { 0 };
852 LoadString(::hResource, doLoad ? IDS_OPENPATCH : IDS_SAVEPATCH, fileTitle, sizeof(fileTitle)/sizeof(TCHAR));
853 ofn.lpstrTitle = fileTitle;
854 ofn.Flags = OFN_ENABLESIZING | OFN_EXPLORER;
855 if(doLoad)
856 ofn.Flags |= OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
857 else
858 ofn.Flags |= OFN_OVERWRITEPROMPT;
859 // Display the Open dialog box.
860 if( doLoad )
862 if (GetOpenFileName(&ofn)==TRUE)
864 LoadFile(ofn.lpstrFile);
867 else
869 if (filename.empty())
871 if (GetSaveFileName(&ofn)==TRUE)
873 SaveFile(ofn.lpstrFile);
876 else
877 SaveFile(filename.c_str());