Fix redundant text before "commit submodule"
[TortoiseGit.git] / src / TortoiseUDiff / MainWindow.cpp
blob36378c6014eff8fbb6599e9aae4e7319e6f488b5
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2012-2014 - 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 "MainWindow.h"
22 #include "UnicodeUtils.h"
23 #include "StringUtils.h"
24 #include "TaskbarUUID.h"
25 #include "CreateProcessHelper.h"
26 #include "SysInfo.h"
27 #include "UDiffColors.h"
28 #include "registry.h"
30 const UINT TaskBarButtonCreated = RegisterWindowMessage(L"TaskbarButtonCreated");
32 CMainWindow::CMainWindow(HINSTANCE hInst, const WNDCLASSEX* wcx /* = NULL*/)
33 : CWindow(hInst, wcx)
34 , m_bShowFindBar(false)
35 , m_directFunction(0)
36 , m_directPointer(0)
37 , m_hWndEdit(NULL)
38 , m_bMatchCase(false)
40 SetWindowTitle(_T("TortoiseGitUDiff"));
43 CMainWindow::~CMainWindow(void)
47 bool CMainWindow::RegisterAndCreateWindow()
49 WNDCLASSEX wcx;
51 // Fill in the window class structure with default parameters
52 wcx.cbSize = sizeof(WNDCLASSEX);
53 wcx.style = CS_HREDRAW | CS_VREDRAW;
54 wcx.lpfnWndProc = CWindow::stWinMsgHandler;
55 wcx.cbClsExtra = 0;
56 wcx.cbWndExtra = 0;
57 wcx.hInstance = hResource;
58 wcx.hCursor = NULL;
59 ResString clsname(hResource, IDS_APP_TITLE);
60 wcx.lpszClassName = clsname;
61 wcx.hIcon = LoadIcon(hResource, MAKEINTRESOURCE(IDI_TORTOISEUDIFF));
62 wcx.hbrBackground = (HBRUSH)(COLOR_3DFACE+1);
63 wcx.lpszMenuName = MAKEINTRESOURCE(IDC_TORTOISEUDIFF);
64 wcx.hIconSm = LoadIcon(wcx.hInstance, MAKEINTRESOURCE(IDI_TORTOISEUDIFF));
65 if (RegisterWindow(&wcx))
67 if (Create(WS_CAPTION | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_SIZEBOX | WS_SYSMENU | WS_CLIPCHILDREN, NULL))
69 m_FindBar.SetParent(*this);
70 m_FindBar.Create(hResource, IDD_FINDBAR, *this);
71 UpdateWindow(*this);
72 return true;
75 return false;
78 LRESULT CALLBACK CMainWindow::WinMsgHandler(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
80 if (uMsg == TaskBarButtonCreated)
82 SetUUIDOverlayIcon(hwnd);
84 switch (uMsg)
86 case WM_CREATE:
88 m_hwnd = hwnd;
89 Initialize();
91 break;
92 case WM_COMMAND:
94 return DoCommand(LOWORD(wParam));
96 break;
97 case WM_MOUSEWHEEL:
99 if (GET_KEYSTATE_WPARAM(wParam) == MK_SHIFT)
101 // scroll sideways
102 SendEditor(SCI_LINESCROLL, -GET_WHEEL_DELTA_WPARAM(wParam)/40, 0);
104 else
105 return DefWindowProc(hwnd, uMsg, wParam, lParam);
107 break;
108 case WM_SIZE:
110 RECT rect;
111 GetClientRect(*this, &rect);
112 if (m_bShowFindBar)
114 ::SetWindowPos(m_hWndEdit, HWND_TOP,
115 rect.left, rect.top,
116 rect.right-rect.left, rect.bottom-rect.top-30,
117 SWP_SHOWWINDOW);
118 ::SetWindowPos(m_FindBar, HWND_TOP,
119 rect.left, rect.bottom-30,
120 rect.right-rect.left, 30,
121 SWP_SHOWWINDOW);
123 else
125 ::SetWindowPos(m_hWndEdit, HWND_TOP,
126 rect.left, rect.top,
127 rect.right-rect.left, rect.bottom-rect.top,
128 SWP_SHOWWINDOW);
129 ::ShowWindow(m_FindBar, SW_HIDE);
132 break;
133 case WM_GETMINMAXINFO:
135 MINMAXINFO * mmi = (MINMAXINFO*)lParam;
136 mmi->ptMinTrackSize.x = 100;
137 mmi->ptMinTrackSize.y = 100;
138 return 0;
140 break;
141 case WM_DESTROY:
142 PostQuitMessage(0);
143 break;
144 case WM_CLOSE:
145 ::DestroyWindow(m_hwnd);
146 break;
147 case WM_SETFOCUS:
148 SetFocus(m_hWndEdit);
149 break;
150 case COMMITMONITOR_FINDMSGNEXT:
152 SendEditor(SCI_CHARRIGHT);
153 SendEditor(SCI_SEARCHANCHOR);
154 m_bMatchCase = !!wParam;
155 m_findtext = (LPCTSTR)lParam;
156 SendEditor(SCI_SEARCHNEXT, m_bMatchCase ? SCFIND_MATCHCASE : 0, (LPARAM)CUnicodeUtils::StdGetUTF8(m_findtext).c_str());
157 SendEditor(SCI_SCROLLCARET);
159 break;
160 case COMMITMONITOR_FINDMSGPREV:
162 SendEditor(SCI_SEARCHANCHOR);
163 m_bMatchCase = !!wParam;
164 m_findtext = (LPCTSTR)lParam;
165 SendEditor(SCI_SEARCHPREV, m_bMatchCase ? SCFIND_MATCHCASE : 0, (LPARAM)CUnicodeUtils::StdGetUTF8(m_findtext).c_str());
166 SendEditor(SCI_SCROLLCARET);
168 break;
169 case COMMITMONITOR_FINDEXIT:
171 RECT rect;
172 GetClientRect(*this, &rect);
173 m_bShowFindBar = false;
174 ::ShowWindow(m_FindBar, SW_HIDE);
175 ::SetWindowPos(m_hWndEdit, HWND_TOP,
176 rect.left, rect.top,
177 rect.right-rect.left, rect.bottom-rect.top,
178 SWP_SHOWWINDOW);
180 break;
181 case COMMITMONITOR_FINDRESET:
182 SendEditor(SCI_SETSELECTIONSTART, 0);
183 SendEditor(SCI_SETSELECTIONEND, 0);
184 SendEditor(SCI_SEARCHANCHOR);
185 break;
186 default:
187 return DefWindowProc(hwnd, uMsg, wParam, lParam);
190 return 0;
193 LRESULT CMainWindow::DoCommand(int id)
195 switch (id)
197 case ID_FILE_OPEN:
198 loadOrSaveFile(true);
199 break;
200 case ID_FILE_SAVEAS:
201 loadOrSaveFile(false);
202 break;
203 case ID_FILE_SAVE:
204 loadOrSaveFile(false, m_filename);
205 break;
206 case ID_FILE_EXIT:
207 ::PostQuitMessage(0);
208 return 0;
209 case IDM_SHOWFINDBAR:
211 m_bShowFindBar = true;
212 ::ShowWindow(m_FindBar, SW_SHOW);
213 RECT rect;
214 GetClientRect(*this, &rect);
215 ::SetWindowPos(m_hWndEdit, HWND_TOP,
216 rect.left, rect.top,
217 rect.right-rect.left, rect.bottom-rect.top-30,
218 SWP_SHOWWINDOW);
219 ::SetWindowPos(m_FindBar, HWND_TOP,
220 rect.left, rect.bottom-30,
221 rect.right-rect.left, 30,
222 SWP_SHOWWINDOW);
223 ::SetFocus(m_FindBar);
224 SendEditor(SCI_SETSELECTIONSTART, 0);
225 SendEditor(SCI_SETSELECTIONEND, 0);
226 SendEditor(SCI_SEARCHANCHOR);
228 break;
229 case IDM_FINDNEXT:
230 SendEditor(SCI_SEARCHANCHOR);
231 SendEditor(SCI_SEARCHNEXT, m_bMatchCase ? SCFIND_MATCHCASE : 0, (LPARAM)CUnicodeUtils::StdGetUTF8(m_findtext).c_str());
232 SendEditor(SCI_SCROLLCARET);
233 break;
234 case IDM_FINDPREV:
235 SendEditor(SCI_SEARCHANCHOR);
236 SendEditor(SCI_SEARCHPREV, m_bMatchCase ? SCFIND_MATCHCASE : 0, (LPARAM)CUnicodeUtils::StdGetUTF8(m_findtext).c_str());
237 SendEditor(SCI_SCROLLCARET);
238 break;
239 case IDM_FINDEXIT:
240 if (IsWindowVisible(m_FindBar))
242 RECT rect;
243 GetClientRect(*this, &rect);
244 m_bShowFindBar = false;
245 ::ShowWindow(m_FindBar, SW_HIDE);
246 ::SetWindowPos(m_hWndEdit, HWND_TOP,
247 rect.left, rect.top,
248 rect.right-rect.left, rect.bottom-rect.top,
249 SWP_SHOWWINDOW);
251 else
252 PostQuitMessage(0);
253 break;
254 case ID_FILE_SETTINGS:
256 tstring gitCmd = _T(" /command:settings /page:udiff");
257 RunCommand(gitCmd);
259 break;
260 case ID_FILE_APPLYPATCH:
262 std::wstring command = L" /diff:\"";
263 command += m_filename;
264 command += L"\"";
265 std::wstring tortoiseMergePath = GetAppDirectory() + _T("TortoiseGitMerge.exe");
266 CCreateProcessHelper::CreateProcessDetached(tortoiseMergePath.c_str(), const_cast<TCHAR*>(command.c_str()));
268 break;
269 case ID_FILE_PAGESETUP:
271 TCHAR localeInfo[3] = { 0 };
272 GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IMEASURE, localeInfo, 3);
273 // Metric system. '1' is US System
274 int defaultMargin = localeInfo[0] == '0' ? 2540 : 1000;
276 PAGESETUPDLG pdlg = {0};
277 pdlg.lStructSize = sizeof(PAGESETUPDLG);
278 pdlg.hwndOwner = *this;
279 pdlg.hInstance = NULL;
280 pdlg.Flags = PSD_DEFAULTMINMARGINS|PSD_MARGINS|PSD_DISABLEPAPER|PSD_DISABLEORIENTATION;
281 if (localeInfo[0] == '0')
282 pdlg.Flags |= PSD_INHUNDREDTHSOFMILLIMETERS;
284 CRegStdDWORD m_regMargLeft = CRegStdDWORD(L"Software\\TortoiseGit\\UDiffpagesetupmarginleft", defaultMargin);
285 CRegStdDWORD m_regMargTop = CRegStdDWORD(L"Software\\TortoiseGit\\UDiffpagesetupmargintop", defaultMargin);
286 CRegStdDWORD m_regMargRight = CRegStdDWORD(L"Software\\TortoiseGit\\UDiffpagesetupmarginright", defaultMargin);
287 CRegStdDWORD m_regMargBottom = CRegStdDWORD(L"Software\\TortoiseGit\\UDiffpagesetupmarginbottom", defaultMargin);
289 pdlg.rtMargin.left = (long)(DWORD)m_regMargLeft;
290 pdlg.rtMargin.top = (long)(DWORD)m_regMargTop;
291 pdlg.rtMargin.right = (long)(DWORD)m_regMargRight;
292 pdlg.rtMargin.bottom = (long)(DWORD)m_regMargBottom;
294 if (!PageSetupDlg(&pdlg))
295 return false;
297 m_regMargLeft = pdlg.rtMargin.left;
298 m_regMargTop = pdlg.rtMargin.top;
299 m_regMargRight = pdlg.rtMargin.right;
300 m_regMargBottom = pdlg.rtMargin.bottom;
302 break;
303 case ID_FILE_PRINT:
305 PRINTDLGEX pdlg = {0};
306 pdlg.lStructSize = sizeof(PRINTDLGEX);
307 pdlg.hwndOwner = *this;
308 pdlg.hInstance = NULL;
309 pdlg.Flags = PD_USEDEVMODECOPIESANDCOLLATE | PD_ALLPAGES | PD_RETURNDC | PD_NOCURRENTPAGE | PD_NOPAGENUMS;
310 pdlg.nMinPage = 1;
311 pdlg.nMaxPage = 0xffffU; // We do not know how many pages in the document
312 pdlg.nCopies = 1;
313 pdlg.hDC = 0;
314 pdlg.nStartPage = START_PAGE_GENERAL;
316 // See if a range has been selected
317 size_t startPos = SendEditor(SCI_GETSELECTIONSTART);
318 size_t endPos = SendEditor(SCI_GETSELECTIONEND);
320 if (startPos == endPos)
321 pdlg.Flags |= PD_NOSELECTION;
322 else
323 pdlg.Flags |= PD_SELECTION;
325 HRESULT hResult = PrintDlgEx(&pdlg);
326 if ((hResult != S_OK) || (pdlg.dwResultAction != PD_RESULT_PRINT))
327 return 0;
329 // reset all indicators
330 size_t endpos = SendEditor(SCI_GETLENGTH);
331 for (int i = INDIC_CONTAINER; i <= INDIC_MAX; ++i)
333 SendEditor(SCI_SETINDICATORCURRENT, i);
334 SendEditor(SCI_INDICATORCLEARRANGE, 0, endpos);
336 // store and reset UI settings
337 int viewws = (int)SendEditor(SCI_GETVIEWWS);
338 SendEditor(SCI_SETVIEWWS, 0);
339 int edgemode = (int)SendEditor(SCI_GETEDGEMODE);
340 SendEditor(SCI_SETEDGEMODE, EDGE_NONE);
341 SendEditor(SCI_SETWRAPVISUALFLAGS, SC_WRAPVISUALFLAG_END);
343 HDC hdc = pdlg.hDC;
345 RECT rectMargins, rectPhysMargins;
346 POINT ptPage;
347 POINT ptDpi;
349 // Get printer resolution
350 ptDpi.x = GetDeviceCaps(hdc, LOGPIXELSX); // dpi in X direction
351 ptDpi.y = GetDeviceCaps(hdc, LOGPIXELSY); // dpi in Y direction
353 // Start by getting the physical page size (in device units).
354 ptPage.x = GetDeviceCaps(hdc, PHYSICALWIDTH); // device units
355 ptPage.y = GetDeviceCaps(hdc, PHYSICALHEIGHT); // device units
357 // Get the dimensions of the unprintable
358 // part of the page (in device units).
359 rectPhysMargins.left = GetDeviceCaps(hdc, PHYSICALOFFSETX);
360 rectPhysMargins.top = GetDeviceCaps(hdc, PHYSICALOFFSETY);
362 // To get the right and lower unprintable area,
363 // we take the entire width and height of the paper and
364 // subtract everything else.
365 rectPhysMargins.right = ptPage.x // total paper width
366 - GetDeviceCaps(hdc, HORZRES) // printable width
367 - rectPhysMargins.left; // left unprintable margin
369 rectPhysMargins.bottom = ptPage.y // total paper height
370 - GetDeviceCaps(hdc, VERTRES) // printable height
371 - rectPhysMargins.top; // right unprintable margin
373 TCHAR localeInfo[3] = { 0 };
374 GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IMEASURE, localeInfo, 3);
375 // Metric system. '1' is US System
376 int defaultMargin = localeInfo[0] == '0' ? 2540 : 1000;
377 RECT pagesetupMargin;
378 CRegStdDWORD m_regMargLeft = CRegStdDWORD(L"Software\\TortoiseGit\\UDiffpagesetupmarginleft", defaultMargin);
379 CRegStdDWORD m_regMargTop = CRegStdDWORD(L"Software\\TortoiseGit\\UDiffpagesetupmargintop", defaultMargin);
380 CRegStdDWORD m_regMargRight = CRegStdDWORD(L"Software\\TortoiseGit\\UDiffpagesetupmarginright", defaultMargin);
381 CRegStdDWORD m_regMargBottom = CRegStdDWORD(L"Software\\TortoiseGit\\UDiffpagesetupmarginbottom", defaultMargin);
383 pagesetupMargin.left = (long)(DWORD)m_regMargLeft;
384 pagesetupMargin.top = (long)(DWORD)m_regMargTop;
385 pagesetupMargin.right = (long)(DWORD)m_regMargRight;
386 pagesetupMargin.bottom = (long)(DWORD)m_regMargBottom;
388 if (pagesetupMargin.left != 0 || pagesetupMargin.right != 0 ||
389 pagesetupMargin.top != 0 || pagesetupMargin.bottom != 0)
391 RECT rectSetup;
393 // Convert the hundredths of millimeters (HiMetric) or
394 // thousandths of inches (HiEnglish) margin values
395 // from the Page Setup dialog to device units.
396 // (There are 2540 hundredths of a mm in an inch.)
397 if (localeInfo[0] == '0')
399 // Metric system. '1' is US System
400 rectSetup.left = MulDiv (pagesetupMargin.left, ptDpi.x, 2540);
401 rectSetup.top = MulDiv (pagesetupMargin.top, ptDpi.y, 2540);
402 rectSetup.right = MulDiv(pagesetupMargin.right, ptDpi.x, 2540);
403 rectSetup.bottom = MulDiv(pagesetupMargin.bottom, ptDpi.y, 2540);
405 else
407 rectSetup.left = MulDiv(pagesetupMargin.left, ptDpi.x, 1000);
408 rectSetup.top = MulDiv(pagesetupMargin.top, ptDpi.y, 1000);
409 rectSetup.right = MulDiv(pagesetupMargin.right, ptDpi.x, 1000);
410 rectSetup.bottom = MulDiv(pagesetupMargin.bottom, ptDpi.y, 1000);
413 // Don't reduce margins below the minimum printable area
414 rectMargins.left = max(rectPhysMargins.left, rectSetup.left);
415 rectMargins.top = max(rectPhysMargins.top, rectSetup.top);
416 rectMargins.right = max(rectPhysMargins.right, rectSetup.right);
417 rectMargins.bottom = max(rectPhysMargins.bottom, rectSetup.bottom);
419 else
421 rectMargins.left = rectPhysMargins.left;
422 rectMargins.top = rectPhysMargins.top;
423 rectMargins.right = rectPhysMargins.right;
424 rectMargins.bottom = rectPhysMargins.bottom;
427 // rectMargins now contains the values used to shrink the printable
428 // area of the page.
430 // Convert device coordinates into logical coordinates
431 DPtoLP(hdc, (LPPOINT) &rectMargins, 2);
432 DPtoLP(hdc, (LPPOINT)&rectPhysMargins, 2);
434 // Convert page size to logical units and we're done!
435 DPtoLP(hdc, (LPPOINT) &ptPage, 1);
438 DOCINFO di = {sizeof(DOCINFO), 0, 0, 0, 0};
439 di.lpszDocName = m_filename.c_str();
440 di.lpszOutput = 0;
441 di.lpszDatatype = 0;
442 di.fwType = 0;
443 if (::StartDoc(hdc, &di) < 0)
445 ::DeleteDC(hdc);
446 return 0;
449 size_t lengthDoc = SendEditor(SCI_GETLENGTH);
450 size_t lengthDocMax = lengthDoc;
451 size_t lengthPrinted = 0;
453 // Requested to print selection
454 if (pdlg.Flags & PD_SELECTION)
456 if (startPos > endPos)
458 lengthPrinted = endPos;
459 lengthDoc = startPos;
461 else
463 lengthPrinted = startPos;
464 lengthDoc = endPos;
467 if (lengthDoc > lengthDocMax)
468 lengthDoc = lengthDocMax;
471 // We must subtract the physical margins from the printable area
472 Sci_RangeToFormat frPrint;
473 frPrint.hdc = hdc;
474 frPrint.hdcTarget = hdc;
475 frPrint.rc.left = rectMargins.left - rectPhysMargins.left;
476 frPrint.rc.top = rectMargins.top - rectPhysMargins.top;
477 frPrint.rc.right = ptPage.x - rectMargins.right - rectPhysMargins.left;
478 frPrint.rc.bottom = ptPage.y - rectMargins.bottom - rectPhysMargins.top;
479 frPrint.rcPage.left = 0;
480 frPrint.rcPage.top = 0;
481 frPrint.rcPage.right = ptPage.x - rectPhysMargins.left - rectPhysMargins.right - 1;
482 frPrint.rcPage.bottom = ptPage.y - rectPhysMargins.top - rectPhysMargins.bottom - 1;
484 // Print each page
485 while (lengthPrinted < lengthDoc)
487 ::StartPage(hdc);
489 frPrint.chrg.cpMin = (long)lengthPrinted;
490 frPrint.chrg.cpMax = (long)lengthDoc;
492 lengthPrinted = SendEditor(SCI_FORMATRANGE, true, reinterpret_cast<LPARAM>(&frPrint));
494 ::EndPage(hdc);
497 SendEditor(SCI_FORMATRANGE, FALSE, 0);
499 ::EndDoc(hdc);
500 ::DeleteDC(hdc);
502 if (pdlg.hDevMode != NULL)
503 GlobalFree(pdlg.hDevMode);
504 if (pdlg.hDevNames != NULL)
505 GlobalFree(pdlg.hDevNames);
506 if (pdlg.lpPageRanges != NULL)
507 GlobalFree(pdlg.lpPageRanges);
509 // reset the UI
510 SendEditor(SCI_SETVIEWWS, viewws);
511 SendEditor(SCI_SETEDGEMODE, edgemode);
512 SendEditor(SCI_SETWRAPVISUALFLAGS, SC_WRAPVISUALFLAG_NONE);
514 break;
515 default:
516 break;
518 return 1;
521 std::wstring CMainWindow::GetAppDirectory()
523 std::wstring path;
524 DWORD len = 0;
525 DWORD bufferlen = MAX_PATH; // MAX_PATH is not the limit here!
528 bufferlen += MAX_PATH; // MAX_PATH is not the limit here!
529 std::unique_ptr<TCHAR[]> pBuf(new TCHAR[bufferlen]);
530 len = GetModuleFileName(NULL, pBuf.get(), bufferlen);
531 path = std::wstring(pBuf.get(), len);
532 } while(len == bufferlen);
533 path = path.substr(0, path.rfind('\\') + 1);
535 return path;
538 void CMainWindow::RunCommand(const std::wstring& command)
540 tstring tortoiseProcPath = GetAppDirectory() + _T("TortoiseGitProc.exe");
541 CCreateProcessHelper::CreateProcessDetached(tortoiseProcPath.c_str(), const_cast<TCHAR*>(command.c_str()));
544 LRESULT CMainWindow::SendEditor(UINT Msg, WPARAM wParam, LPARAM lParam)
546 if (m_directFunction)
548 return ((SciFnDirect) m_directFunction)(m_directPointer, Msg, wParam, lParam);
550 return ::SendMessage(m_hWndEdit, Msg, wParam, lParam);
553 bool CMainWindow::Initialize()
555 m_hWndEdit = ::CreateWindow(
556 _T("Scintilla"),
557 _T("Source"),
558 WS_CHILD | WS_VSCROLL | WS_HSCROLL | WS_CLIPCHILDREN,
559 CW_USEDEFAULT, CW_USEDEFAULT,
560 CW_USEDEFAULT, CW_USEDEFAULT,
561 *this,
563 hResource,
565 if (m_hWndEdit == NULL)
566 return false;
568 RECT rect;
569 GetClientRect(*this, &rect);
570 ::SetWindowPos(m_hWndEdit, HWND_TOP,
571 rect.left, rect.top,
572 rect.right-rect.left, rect.bottom-rect.top,
573 SWP_SHOWWINDOW);
575 m_directFunction = SendMessage(m_hWndEdit, SCI_GETDIRECTFUNCTION, 0, 0);
576 m_directPointer = SendMessage(m_hWndEdit, SCI_GETDIRECTPOINTER, 0, 0);
578 // Set up the global default style. These attributes are used wherever no explicit choices are made.
579 SetAStyle(STYLE_DEFAULT, ::GetSysColor(COLOR_WINDOWTEXT), ::GetSysColor(COLOR_WINDOW),
580 CRegStdDWORD(L"Software\\TortoiseGit\\UDiffFontSize", 10),
581 CUnicodeUtils::StdGetUTF8(CRegStdString(L"Software\\TortoiseGit\\UDiffFontName", L"Courier New")).c_str());
582 SendEditor(SCI_SETTABWIDTH, CRegStdDWORD(L"Software\\TortoiseGit\\UDiffTabSize", 4));
583 SendEditor(SCI_SETREADONLY, TRUE);
584 LRESULT pix = SendEditor(SCI_TEXTWIDTH, STYLE_LINENUMBER, (LPARAM)"_99999");
585 SendEditor(SCI_SETMARGINWIDTHN, 0, pix);
586 SendEditor(SCI_SETMARGINWIDTHN, 1);
587 SendEditor(SCI_SETMARGINWIDTHN, 2);
588 //Set the default windows colors for edit controls
589 SendEditor(SCI_STYLESETFORE, STYLE_DEFAULT, ::GetSysColor(COLOR_WINDOWTEXT));
590 SendEditor(SCI_STYLESETBACK, STYLE_DEFAULT, ::GetSysColor(COLOR_WINDOW));
591 SendEditor(SCI_SETSELFORE, TRUE, ::GetSysColor(COLOR_HIGHLIGHTTEXT));
592 SendEditor(SCI_SETSELBACK, TRUE, ::GetSysColor(COLOR_HIGHLIGHT));
593 SendEditor(SCI_SETCARETFORE, ::GetSysColor(COLOR_WINDOWTEXT));
594 CRegStdDWORD used2d(L"Software\\TortoiseGit\\ScintillaDirect2D", FALSE);
595 if (SysInfo::Instance().IsWin7OrLater() && DWORD(used2d))
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_SETWHITESPACEFORE, true, ::GetSysColor(COLOR_3DSHADOW));
603 SendEditor(SCI_STYLESETVISIBLE, STYLE_CONTROLCHAR, TRUE);
605 return true;
608 bool CMainWindow::LoadFile(HANDLE hFile)
610 InitEditor();
611 char data[4096] = { 0 };
612 DWORD dwRead = 0;
614 BOOL bRet = ReadFile(hFile, data, sizeof(data), &dwRead, NULL);
615 bool bUTF8 = IsUTF8(data, dwRead);
616 while ((dwRead > 0) && (bRet))
618 SendEditor(SCI_ADDTEXT, dwRead,
619 reinterpret_cast<LPARAM>(static_cast<char *>(data)));
620 bRet = ReadFile(hFile, data, sizeof(data), &dwRead, NULL);
622 SetupWindow(bUTF8);
623 return true;
626 bool CMainWindow::LoadFile(LPCTSTR filename)
628 InitEditor();
629 FILE *fp = NULL;
630 _tfopen_s(&fp, filename, _T("rb"));
631 if (!fp)
632 return false;
634 //SetTitle();
635 char data[4096] = { 0 };
636 size_t lenFile = fread(data, 1, sizeof(data), fp);
637 bool bUTF8 = IsUTF8(data, lenFile);
638 while (lenFile > 0)
640 SendEditor(SCI_ADDTEXT, lenFile,
641 reinterpret_cast<LPARAM>(static_cast<char *>(data)));
642 lenFile = fread(data, 1, sizeof(data), fp);
644 fclose(fp);
645 SetupWindow(bUTF8);
646 m_filename = filename;
647 return true;
650 void CMainWindow::InitEditor()
652 SendEditor(SCI_SETREADONLY, FALSE);
653 SendEditor(SCI_CLEARALL);
654 SendEditor(EM_EMPTYUNDOBUFFER);
655 SendEditor(SCI_SETSAVEPOINT);
656 SendEditor(SCI_CANCEL);
657 SendEditor(SCI_SETUNDOCOLLECTION, 0);
660 void CMainWindow::SetupWindow(bool bUTF8)
662 SendEditor(SCI_SETCODEPAGE, bUTF8 ? SC_CP_UTF8 : GetACP());
664 SendEditor(SCI_SETUNDOCOLLECTION, 1);
665 ::SetFocus(m_hWndEdit);
666 SendEditor(EM_EMPTYUNDOBUFFER);
667 SendEditor(SCI_SETSAVEPOINT);
668 SendEditor(SCI_GOTOPOS, 0);
670 SendEditor(SCI_CLEARDOCUMENTSTYLE, 0, 0);
671 SendEditor(SCI_SETSTYLEBITS, 5, 0);
673 //SetAStyle(SCE_DIFF_DEFAULT, RGB(0, 0, 0));
674 SetAStyle(SCE_DIFF_COMMAND,
675 CRegStdDWORD(L"Software\\TortoiseGit\\UDiffForeCommandColor", UDIFF_COLORFORECOMMAND),
676 CRegStdDWORD(L"Software\\TortoiseGit\\UDiffBackCommandColor", UDIFF_COLORBACKCOMMAND));
677 SetAStyle(SCE_DIFF_POSITION,
678 CRegStdDWORD(L"Software\\TortoiseGit\\UDiffForePositionColor", UDIFF_COLORFOREPOSITION),
679 CRegStdDWORD(L"Software\\TortoiseGit\\UDiffBackPositionColor", UDIFF_COLORBACKPOSITION));
680 SetAStyle(SCE_DIFF_HEADER,
681 CRegStdDWORD(L"Software\\TortoiseGit\\UDiffForeHeaderColor", UDIFF_COLORFOREHEADER),
682 CRegStdDWORD(L"Software\\TortoiseGit\\UDiffBackHeaderColor", UDIFF_COLORBACKHEADER));
683 SetAStyle(SCE_DIFF_COMMENT,
684 CRegStdDWORD(L"Software\\TortoiseGit\\UDiffForeCommentColor", UDIFF_COLORFORECOMMENT),
685 CRegStdDWORD(L"Software\\TortoiseGit\\UDiffBackCommentColor", UDIFF_COLORBACKCOMMENT));
686 SendEditor(SCI_STYLESETBOLD, SCE_DIFF_COMMENT, TRUE);
688 SetAStyle(SCE_DIFF_ADDED,
689 CRegStdDWORD(L"Software\\TortoiseGit\\UDiffForeAddedColor", UDIFF_COLORFOREADDED),
690 CRegStdDWORD(L"Software\\TortoiseGit\\UDiffBackAddedColor", UDIFF_COLORBACKADDED));
691 SetAStyle(SCE_DIFF_DELETED,
692 CRegStdDWORD(L"Software\\TortoiseGit\\UDiffForeRemovedColor", UDIFF_COLORFOREREMOVED),
693 CRegStdDWORD(L"Software\\TortoiseGit\\UDiffBackRemovedColor", UDIFF_COLORBACKREMOVED));
695 SendEditor(SCI_SETLEXER, SCLEX_DIFF);
696 SendEditor(SCI_SETKEYWORDS, 0, (LPARAM)"revision");
697 SendEditor(SCI_COLOURISE, 0, -1);
698 ::ShowWindow(m_hWndEdit, SW_SHOW);
701 bool CMainWindow::SaveFile(LPCTSTR filename)
703 FILE *fp = NULL;
704 _tfopen_s(&fp, filename, _T("w+b"));
705 if (!fp)
706 return false;
708 LRESULT len = SendEditor(SCI_GETTEXT, 0, 0);
709 std::unique_ptr<char[]> data (new char[len+1]);
710 SendEditor(SCI_GETTEXT, len, reinterpret_cast<LPARAM>(static_cast<char *>(data.get())));
711 fwrite(data.get(), sizeof(char), len-1, fp);
712 fclose(fp);
714 SendEditor(SCI_SETSAVEPOINT);
715 ::ShowWindow(m_hWndEdit, SW_SHOW);
716 return true;
719 void CMainWindow::SetTitle(LPCTSTR title)
721 size_t len = _tcslen(title);
722 std::unique_ptr<TCHAR[]> pBuf(new TCHAR[len + 40]);
723 _stprintf_s(pBuf.get(), len + 40, _T("%s - TortoiseGitUDiff"), title);
724 SetWindowTitle(std::wstring(pBuf.get()));
727 void CMainWindow::SetAStyle(int style, COLORREF fore, COLORREF back, int size, const char *face)
729 SendEditor(SCI_STYLESETFORE, style, fore);
730 SendEditor(SCI_STYLESETBACK, style, back);
731 if (size >= 1)
732 SendEditor(SCI_STYLESETSIZE, style, size);
733 if (face)
734 SendEditor(SCI_STYLESETFONT, style, reinterpret_cast<LPARAM>(face));
737 bool CMainWindow::IsUTF8(LPVOID pBuffer, size_t cb)
739 if (cb < 2)
740 return true;
741 UINT16 * pVal16 = (UINT16 *)pBuffer;
742 UINT8 * pVal8 = (UINT8 *)(pVal16+1);
743 // scan the whole buffer for a 0x0000 sequence
744 // if found, we assume a binary file
745 for (size_t i=0; i<(cb-2); i=i+2)
747 if (0x0000 == *pVal16++)
748 return false;
750 pVal16 = (UINT16 *)pBuffer;
751 if (*pVal16 == 0xFEFF)
752 return false;
753 if (cb < 3)
754 return false;
755 if (*pVal16 == 0xBBEF)
757 if (*pVal8 == 0xBF)
758 return true;
760 // check for illegal UTF8 chars
761 pVal8 = (UINT8 *)pBuffer;
762 for (size_t i=0; i<cb; ++i)
764 if ((*pVal8 == 0xC0)||(*pVal8 == 0xC1)||(*pVal8 >= 0xF5))
765 return false;
766 pVal8++;
768 pVal8 = (UINT8 *)pBuffer;
769 bool bUTF8 = false;
770 for (size_t i=0; i<(cb-3); ++i)
772 if ((*pVal8 & 0xE0)==0xC0)
774 pVal8++;i++;
775 if ((*pVal8 & 0xC0)!=0x80)
776 return false;
777 bUTF8 = true;
779 else if ((*pVal8 & 0xF0)==0xE0)
781 pVal8++;i++;
782 if ((*pVal8 & 0xC0)!=0x80)
783 return false;
784 pVal8++;i++;
785 if ((*pVal8 & 0xC0)!=0x80)
786 return false;
787 bUTF8 = true;
789 else if ((*pVal8 & 0xF8)==0xF0)
791 pVal8++;i++;
792 if ((*pVal8 & 0xC0)!=0x80)
793 return false;
794 pVal8++;i++;
795 if ((*pVal8 & 0xC0)!=0x80)
796 return false;
797 pVal8++;i++;
798 if ((*pVal8 & 0xC0)!=0x80)
799 return false;
800 bUTF8 = true;
802 else if (*pVal8 >= 0x80)
803 return false;
805 pVal8++;
807 if (bUTF8)
808 return true;
809 return false;
812 void CMainWindow::loadOrSaveFile(bool doLoad, const std::wstring& filename /* = L"" */)
814 OPENFILENAME ofn = {0}; // common dialog box structure
815 TCHAR szFile[MAX_PATH] = {0}; // buffer for file name
816 // Initialize OPENFILENAME
817 ofn.lStructSize = sizeof(OPENFILENAME);
818 ofn.hwndOwner = *this;
819 ofn.lpstrFile = szFile;
820 ofn.nMaxFile = sizeof(szFile)/sizeof(TCHAR);
821 TCHAR filter[1024] = { 0 };
822 LoadString(hResource, IDS_PATCHFILEFILTER, filter, sizeof(filter)/sizeof(TCHAR));
823 CStringUtils::PipesToNulls(filter);
824 ofn.lpstrFilter = filter;
825 ofn.nFilterIndex = 1;
826 ofn.lpstrFileTitle = NULL;
827 ofn.nMaxFileTitle = 0;
828 ofn.lpstrInitialDir = NULL;
829 TCHAR fileTitle[1024] = { 0 };
830 LoadString(hResource, doLoad ? IDS_OPENPATCH : IDS_SAVEPATCH, fileTitle, sizeof(fileTitle)/sizeof(TCHAR));
831 ofn.lpstrTitle = fileTitle;
832 ofn.Flags = OFN_ENABLESIZING | OFN_EXPLORER;
833 if(doLoad)
834 ofn.Flags |= OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
835 else
836 ofn.Flags |= OFN_OVERWRITEPROMPT;
837 // Display the Open dialog box.
838 if( doLoad )
840 if (GetOpenFileName(&ofn)==TRUE)
842 LoadFile(ofn.lpstrFile);
845 else
847 if (filename.empty())
849 if (GetSaveFileName(&ofn)==TRUE)
851 SaveFile(ofn.lpstrFile);
854 else
855 SaveFile(filename.c_str());