Fixed issue #2064: Broken characters when dragging the horizontal bar in TortoiseGitMerge
[TortoiseGit.git] / src / TortoiseUDiff / MainWindow.cpp
blobb7142b9b513ae15ab17e8fa8e749250b08465954
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2012-2013 - 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"
28 const UINT TaskBarButtonCreated = RegisterWindowMessage(L"TaskbarButtonCreated");
30 CMainWindow::CMainWindow(HINSTANCE hInst, const WNDCLASSEX* wcx /* = NULL*/)
31 : CWindow(hInst, wcx)
32 , m_bShowFindBar(false)
33 , m_directFunction(0)
34 , m_directPointer(0)
35 , m_hWndEdit(NULL)
36 , m_bMatchCase(false)
38 SetWindowTitle(_T("TortoiseGitUDiff"));
41 CMainWindow::~CMainWindow(void)
45 bool CMainWindow::RegisterAndCreateWindow()
47 WNDCLASSEX wcx;
49 // Fill in the window class structure with default parameters
50 wcx.cbSize = sizeof(WNDCLASSEX);
51 wcx.style = CS_HREDRAW | CS_VREDRAW;
52 wcx.lpfnWndProc = CWindow::stWinMsgHandler;
53 wcx.cbClsExtra = 0;
54 wcx.cbWndExtra = 0;
55 wcx.hInstance = hResource;
56 wcx.hCursor = NULL;
57 ResString clsname(hResource, IDS_APP_TITLE);
58 wcx.lpszClassName = clsname;
59 wcx.hIcon = LoadIcon(hResource, MAKEINTRESOURCE(IDI_TORTOISEUDIFF));
60 wcx.hbrBackground = (HBRUSH)(COLOR_3DFACE+1);
61 wcx.lpszMenuName = MAKEINTRESOURCE(IDC_TORTOISEUDIFF);
62 wcx.hIconSm = LoadIcon(wcx.hInstance, MAKEINTRESOURCE(IDI_TORTOISEUDIFF));
63 if (RegisterWindow(&wcx))
65 if (Create(WS_CAPTION | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_SIZEBOX | WS_SYSMENU | WS_CLIPCHILDREN, NULL))
67 m_FindBar.SetParent(*this);
68 m_FindBar.Create(hResource, IDD_FINDBAR, *this);
69 UpdateWindow(*this);
70 return true;
73 return false;
76 LRESULT CALLBACK CMainWindow::WinMsgHandler(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
78 if (uMsg == TaskBarButtonCreated)
80 SetUUIDOverlayIcon(hwnd);
82 switch (uMsg)
84 case WM_CREATE:
86 m_hwnd = hwnd;
87 Initialize();
89 break;
90 case WM_COMMAND:
92 return DoCommand(LOWORD(wParam));
94 break;
95 case WM_MOUSEWHEEL:
97 if (GET_KEYSTATE_WPARAM(wParam) == MK_SHIFT)
99 // scroll sideways
100 SendEditor(SCI_LINESCROLL, -GET_WHEEL_DELTA_WPARAM(wParam)/40, 0);
102 else
103 return DefWindowProc(hwnd, uMsg, wParam, lParam);
105 break;
106 case WM_SIZE:
108 RECT rect;
109 GetClientRect(*this, &rect);
110 if (m_bShowFindBar)
112 ::SetWindowPos(m_hWndEdit, HWND_TOP,
113 rect.left, rect.top,
114 rect.right-rect.left, rect.bottom-rect.top-30,
115 SWP_SHOWWINDOW);
116 ::SetWindowPos(m_FindBar, HWND_TOP,
117 rect.left, rect.bottom-30,
118 rect.right-rect.left, 30,
119 SWP_SHOWWINDOW);
121 else
123 ::SetWindowPos(m_hWndEdit, HWND_TOP,
124 rect.left, rect.top,
125 rect.right-rect.left, rect.bottom-rect.top,
126 SWP_SHOWWINDOW);
127 ::ShowWindow(m_FindBar, SW_HIDE);
130 break;
131 case WM_GETMINMAXINFO:
133 MINMAXINFO * mmi = (MINMAXINFO*)lParam;
134 mmi->ptMinTrackSize.x = 100;
135 mmi->ptMinTrackSize.y = 100;
136 return 0;
138 break;
139 case WM_DESTROY:
140 PostQuitMessage(0);
141 break;
142 case WM_CLOSE:
143 ::DestroyWindow(m_hwnd);
144 break;
145 case WM_SETFOCUS:
146 SetFocus(m_hWndEdit);
147 break;
148 case COMMITMONITOR_FINDMSGNEXT:
150 SendEditor(SCI_CHARRIGHT);
151 SendEditor(SCI_SEARCHANCHOR);
152 m_bMatchCase = !!wParam;
153 m_findtext = (LPCTSTR)lParam;
154 SendEditor(SCI_SEARCHNEXT, m_bMatchCase ? SCFIND_MATCHCASE : 0, (LPARAM)CUnicodeUtils::StdGetUTF8(m_findtext).c_str());
155 SendEditor(SCI_SCROLLCARET);
157 break;
158 case COMMITMONITOR_FINDMSGPREV:
160 SendEditor(SCI_SEARCHANCHOR);
161 m_bMatchCase = !!wParam;
162 m_findtext = (LPCTSTR)lParam;
163 SendEditor(SCI_SEARCHPREV, m_bMatchCase ? SCFIND_MATCHCASE : 0, (LPARAM)CUnicodeUtils::StdGetUTF8(m_findtext).c_str());
164 SendEditor(SCI_SCROLLCARET);
166 break;
167 case COMMITMONITOR_FINDEXIT:
169 RECT rect;
170 GetClientRect(*this, &rect);
171 m_bShowFindBar = false;
172 ::ShowWindow(m_FindBar, SW_HIDE);
173 ::SetWindowPos(m_hWndEdit, HWND_TOP,
174 rect.left, rect.top,
175 rect.right-rect.left, rect.bottom-rect.top,
176 SWP_SHOWWINDOW);
178 break;
179 case COMMITMONITOR_FINDRESET:
180 SendEditor(SCI_SETSELECTIONSTART, 0);
181 SendEditor(SCI_SETSELECTIONEND, 0);
182 SendEditor(SCI_SEARCHANCHOR);
183 break;
184 default:
185 return DefWindowProc(hwnd, uMsg, wParam, lParam);
188 return 0;
191 LRESULT CMainWindow::DoCommand(int id)
193 switch (id)
195 case ID_FILE_OPEN:
196 loadOrSaveFile(true);
197 break;
198 case ID_FILE_SAVEAS:
199 loadOrSaveFile(false);
200 break;
201 case ID_FILE_SAVE:
202 loadOrSaveFile(false, m_filename);
203 break;
204 case ID_FILE_EXIT:
205 ::PostQuitMessage(0);
206 return 0;
207 case IDM_SHOWFINDBAR:
209 m_bShowFindBar = true;
210 ::ShowWindow(m_FindBar, SW_SHOW);
211 RECT rect;
212 GetClientRect(*this, &rect);
213 ::SetWindowPos(m_hWndEdit, HWND_TOP,
214 rect.left, rect.top,
215 rect.right-rect.left, rect.bottom-rect.top-30,
216 SWP_SHOWWINDOW);
217 ::SetWindowPos(m_FindBar, HWND_TOP,
218 rect.left, rect.bottom-30,
219 rect.right-rect.left, 30,
220 SWP_SHOWWINDOW);
221 ::SetFocus(m_FindBar);
222 SendEditor(SCI_SETSELECTIONSTART, 0);
223 SendEditor(SCI_SETSELECTIONEND, 0);
224 SendEditor(SCI_SEARCHANCHOR);
226 break;
227 case IDM_FINDNEXT:
228 SendEditor(SCI_SEARCHANCHOR);
229 SendEditor(SCI_SEARCHNEXT, m_bMatchCase ? SCFIND_MATCHCASE : 0, (LPARAM)CUnicodeUtils::StdGetUTF8(m_findtext).c_str());
230 SendEditor(SCI_SCROLLCARET);
231 break;
232 case IDM_FINDPREV:
233 SendEditor(SCI_SEARCHANCHOR);
234 SendEditor(SCI_SEARCHPREV, m_bMatchCase ? SCFIND_MATCHCASE : 0, (LPARAM)CUnicodeUtils::StdGetUTF8(m_findtext).c_str());
235 SendEditor(SCI_SCROLLCARET);
236 break;
237 case IDM_FINDEXIT:
238 if (IsWindowVisible(m_FindBar))
240 RECT rect;
241 GetClientRect(*this, &rect);
242 m_bShowFindBar = false;
243 ::ShowWindow(m_FindBar, SW_HIDE);
244 ::SetWindowPos(m_hWndEdit, HWND_TOP,
245 rect.left, rect.top,
246 rect.right-rect.left, rect.bottom-rect.top,
247 SWP_SHOWWINDOW);
249 else
250 PostQuitMessage(0);
251 break;
252 case ID_FILE_SETTINGS:
254 tstring gitCmd = _T(" /command:settings /page:blame");
255 RunCommand(gitCmd);
257 break;
258 case ID_FILE_APPLYPATCH:
260 std::wstring command = L" /diff:\"";
261 command += m_filename;
262 command += L"\"";
263 std::wstring tortoiseMergePath = GetAppDirectory() + _T("TortoiseGitMerge.exe");
264 CCreateProcessHelper::CreateProcessDetached(tortoiseMergePath.c_str(), const_cast<TCHAR*>(command.c_str()));
266 break;
267 case ID_FILE_PAGESETUP:
269 TCHAR localeInfo[3] = { 0 };
270 GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IMEASURE, localeInfo, 3);
271 // Metric system. '1' is US System
272 int defaultMargin = localeInfo[0] == '0' ? 2540 : 1000;
274 PAGESETUPDLG pdlg = {0};
275 pdlg.lStructSize = sizeof(PAGESETUPDLG);
276 pdlg.hwndOwner = *this;
277 pdlg.hInstance = NULL;
278 pdlg.Flags = PSD_DEFAULTMINMARGINS|PSD_MARGINS|PSD_DISABLEPAPER|PSD_DISABLEORIENTATION;
279 if (localeInfo[0] == '0')
280 pdlg.Flags |= PSD_INHUNDREDTHSOFMILLIMETERS;
282 CRegStdDWORD m_regMargLeft = CRegStdDWORD(L"Software\\TortoiseGit\\UDiffpagesetupmarginleft", defaultMargin);
283 CRegStdDWORD m_regMargTop = CRegStdDWORD(L"Software\\TortoiseGit\\UDiffpagesetupmargintop", defaultMargin);
284 CRegStdDWORD m_regMargRight = CRegStdDWORD(L"Software\\TortoiseGit\\UDiffpagesetupmarginright", defaultMargin);
285 CRegStdDWORD m_regMargBottom = CRegStdDWORD(L"Software\\TortoiseGit\\UDiffpagesetupmarginbottom", defaultMargin);
287 pdlg.rtMargin.left = (long)(DWORD)m_regMargLeft;
288 pdlg.rtMargin.top = (long)(DWORD)m_regMargTop;
289 pdlg.rtMargin.right = (long)(DWORD)m_regMargRight;
290 pdlg.rtMargin.bottom = (long)(DWORD)m_regMargBottom;
292 if (!PageSetupDlg(&pdlg))
293 return false;
295 m_regMargLeft = pdlg.rtMargin.left;
296 m_regMargTop = pdlg.rtMargin.top;
297 m_regMargRight = pdlg.rtMargin.right;
298 m_regMargBottom = pdlg.rtMargin.bottom;
300 break;
301 case ID_FILE_PRINT:
303 PRINTDLGEX pdlg = {0};
304 pdlg.lStructSize = sizeof(PRINTDLGEX);
305 pdlg.hwndOwner = *this;
306 pdlg.hInstance = NULL;
307 pdlg.Flags = PD_USEDEVMODECOPIESANDCOLLATE | PD_ALLPAGES | PD_RETURNDC | PD_NOCURRENTPAGE | PD_NOPAGENUMS;
308 pdlg.nMinPage = 1;
309 pdlg.nMaxPage = 0xffffU; // We do not know how many pages in the document
310 pdlg.nCopies = 1;
311 pdlg.hDC = 0;
312 pdlg.nStartPage = START_PAGE_GENERAL;
314 // See if a range has been selected
315 size_t startPos = SendEditor(SCI_GETSELECTIONSTART);
316 size_t endPos = SendEditor(SCI_GETSELECTIONEND);
318 if (startPos == endPos)
319 pdlg.Flags |= PD_NOSELECTION;
320 else
321 pdlg.Flags |= PD_SELECTION;
323 HRESULT hResult = PrintDlgEx(&pdlg);
324 if ((hResult != S_OK) || (pdlg.dwResultAction != PD_RESULT_PRINT))
325 return 0;
327 // reset all indicators
328 size_t endpos = SendEditor(SCI_GETLENGTH);
329 for (int i = INDIC_CONTAINER; i <= INDIC_MAX; ++i)
331 SendEditor(SCI_SETINDICATORCURRENT, i);
332 SendEditor(SCI_INDICATORCLEARRANGE, 0, endpos);
334 // store and reset UI settings
335 int viewws = (int)SendEditor(SCI_GETVIEWWS);
336 SendEditor(SCI_SETVIEWWS, 0);
337 int edgemode = (int)SendEditor(SCI_GETEDGEMODE);
338 SendEditor(SCI_SETEDGEMODE, EDGE_NONE);
339 SendEditor(SCI_SETWRAPVISUALFLAGS, SC_WRAPVISUALFLAG_END);
341 HDC hdc = pdlg.hDC;
343 RECT rectMargins, rectPhysMargins;
344 POINT ptPage;
345 POINT ptDpi;
347 // Get printer resolution
348 ptDpi.x = GetDeviceCaps(hdc, LOGPIXELSX); // dpi in X direction
349 ptDpi.y = GetDeviceCaps(hdc, LOGPIXELSY); // dpi in Y direction
351 // Start by getting the physical page size (in device units).
352 ptPage.x = GetDeviceCaps(hdc, PHYSICALWIDTH); // device units
353 ptPage.y = GetDeviceCaps(hdc, PHYSICALHEIGHT); // device units
355 // Get the dimensions of the unprintable
356 // part of the page (in device units).
357 rectPhysMargins.left = GetDeviceCaps(hdc, PHYSICALOFFSETX);
358 rectPhysMargins.top = GetDeviceCaps(hdc, PHYSICALOFFSETY);
360 // To get the right and lower unprintable area,
361 // we take the entire width and height of the paper and
362 // subtract everything else.
363 rectPhysMargins.right = ptPage.x // total paper width
364 - GetDeviceCaps(hdc, HORZRES) // printable width
365 - rectPhysMargins.left; // left unprintable margin
367 rectPhysMargins.bottom = ptPage.y // total paper height
368 - GetDeviceCaps(hdc, VERTRES) // printable height
369 - rectPhysMargins.top; // right unprintable margin
371 TCHAR localeInfo[3] = { 0 };
372 GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IMEASURE, localeInfo, 3);
373 // Metric system. '1' is US System
374 int defaultMargin = localeInfo[0] == '0' ? 2540 : 1000;
375 RECT pagesetupMargin;
376 CRegStdDWORD m_regMargLeft = CRegStdDWORD(L"Software\\TortoiseGit\\UDiffpagesetupmarginleft", defaultMargin);
377 CRegStdDWORD m_regMargTop = CRegStdDWORD(L"Software\\TortoiseGit\\UDiffpagesetupmargintop", defaultMargin);
378 CRegStdDWORD m_regMargRight = CRegStdDWORD(L"Software\\TortoiseGit\\UDiffpagesetupmarginright", defaultMargin);
379 CRegStdDWORD m_regMargBottom = CRegStdDWORD(L"Software\\TortoiseGit\\UDiffpagesetupmarginbottom", defaultMargin);
381 pagesetupMargin.left = (long)(DWORD)m_regMargLeft;
382 pagesetupMargin.top = (long)(DWORD)m_regMargTop;
383 pagesetupMargin.right = (long)(DWORD)m_regMargRight;
384 pagesetupMargin.bottom = (long)(DWORD)m_regMargBottom;
386 if (pagesetupMargin.left != 0 || pagesetupMargin.right != 0 ||
387 pagesetupMargin.top != 0 || pagesetupMargin.bottom != 0)
389 RECT rectSetup;
391 // Convert the hundredths of millimeters (HiMetric) or
392 // thousandths of inches (HiEnglish) margin values
393 // from the Page Setup dialog to device units.
394 // (There are 2540 hundredths of a mm in an inch.)
395 if (localeInfo[0] == '0')
397 // Metric system. '1' is US System
398 rectSetup.left = MulDiv (pagesetupMargin.left, ptDpi.x, 2540);
399 rectSetup.top = MulDiv (pagesetupMargin.top, ptDpi.y, 2540);
400 rectSetup.right = MulDiv(pagesetupMargin.right, ptDpi.x, 2540);
401 rectSetup.bottom = MulDiv(pagesetupMargin.bottom, ptDpi.y, 2540);
403 else
405 rectSetup.left = MulDiv(pagesetupMargin.left, ptDpi.x, 1000);
406 rectSetup.top = MulDiv(pagesetupMargin.top, ptDpi.y, 1000);
407 rectSetup.right = MulDiv(pagesetupMargin.right, ptDpi.x, 1000);
408 rectSetup.bottom = MulDiv(pagesetupMargin.bottom, ptDpi.y, 1000);
411 // Don't reduce margins below the minimum printable area
412 rectMargins.left = max(rectPhysMargins.left, rectSetup.left);
413 rectMargins.top = max(rectPhysMargins.top, rectSetup.top);
414 rectMargins.right = max(rectPhysMargins.right, rectSetup.right);
415 rectMargins.bottom = max(rectPhysMargins.bottom, rectSetup.bottom);
417 else
419 rectMargins.left = rectPhysMargins.left;
420 rectMargins.top = rectPhysMargins.top;
421 rectMargins.right = rectPhysMargins.right;
422 rectMargins.bottom = rectPhysMargins.bottom;
425 // rectMargins now contains the values used to shrink the printable
426 // area of the page.
428 // Convert device coordinates into logical coordinates
429 DPtoLP(hdc, (LPPOINT) &rectMargins, 2);
430 DPtoLP(hdc, (LPPOINT)&rectPhysMargins, 2);
432 // Convert page size to logical units and we're done!
433 DPtoLP(hdc, (LPPOINT) &ptPage, 1);
436 DOCINFO di = {sizeof(DOCINFO), 0, 0, 0, 0};
437 di.lpszDocName = m_filename.c_str();
438 di.lpszOutput = 0;
439 di.lpszDatatype = 0;
440 di.fwType = 0;
441 if (::StartDoc(hdc, &di) < 0)
443 ::DeleteDC(hdc);
444 return 0;
447 size_t lengthDoc = SendEditor(SCI_GETLENGTH);
448 size_t lengthDocMax = lengthDoc;
449 size_t lengthPrinted = 0;
451 // Requested to print selection
452 if (pdlg.Flags & PD_SELECTION)
454 if (startPos > endPos)
456 lengthPrinted = endPos;
457 lengthDoc = startPos;
459 else
461 lengthPrinted = startPos;
462 lengthDoc = endPos;
465 if (lengthDoc > lengthDocMax)
466 lengthDoc = lengthDocMax;
469 // We must subtract the physical margins from the printable area
470 Sci_RangeToFormat frPrint;
471 frPrint.hdc = hdc;
472 frPrint.hdcTarget = hdc;
473 frPrint.rc.left = rectMargins.left - rectPhysMargins.left;
474 frPrint.rc.top = rectMargins.top - rectPhysMargins.top;
475 frPrint.rc.right = ptPage.x - rectMargins.right - rectPhysMargins.left;
476 frPrint.rc.bottom = ptPage.y - rectMargins.bottom - rectPhysMargins.top;
477 frPrint.rcPage.left = 0;
478 frPrint.rcPage.top = 0;
479 frPrint.rcPage.right = ptPage.x - rectPhysMargins.left - rectPhysMargins.right - 1;
480 frPrint.rcPage.bottom = ptPage.y - rectPhysMargins.top - rectPhysMargins.bottom - 1;
482 // Print each page
483 while (lengthPrinted < lengthDoc)
485 ::StartPage(hdc);
487 frPrint.chrg.cpMin = (long)lengthPrinted;
488 frPrint.chrg.cpMax = (long)lengthDoc;
490 lengthPrinted = SendEditor(SCI_FORMATRANGE, true, reinterpret_cast<LPARAM>(&frPrint));
492 ::EndPage(hdc);
495 SendEditor(SCI_FORMATRANGE, FALSE, 0);
497 ::EndDoc(hdc);
498 ::DeleteDC(hdc);
500 if (pdlg.hDevMode != NULL)
501 GlobalFree(pdlg.hDevMode);
502 if (pdlg.hDevNames != NULL)
503 GlobalFree(pdlg.hDevNames);
504 if (pdlg.lpPageRanges != NULL)
505 GlobalFree(pdlg.lpPageRanges);
507 // reset the UI
508 SendEditor(SCI_SETVIEWWS, viewws);
509 SendEditor(SCI_SETEDGEMODE, edgemode);
510 SendEditor(SCI_SETWRAPVISUALFLAGS, SC_WRAPVISUALFLAG_NONE);
512 break;
513 default:
514 break;
516 return 1;
519 std::wstring CMainWindow::GetAppDirectory()
521 std::wstring path;
522 DWORD len = 0;
523 DWORD bufferlen = MAX_PATH; // MAX_PATH is not the limit here!
526 bufferlen += MAX_PATH; // MAX_PATH is not the limit here!
527 std::unique_ptr<TCHAR[]> pBuf(new TCHAR[bufferlen]);
528 len = GetModuleFileName(NULL, pBuf.get(), bufferlen);
529 path = pBuf.get();
530 } while(len == bufferlen);
531 path = path.substr(0, path.rfind('\\') + 1);
533 return path;
536 void CMainWindow::RunCommand(const std::wstring& command)
538 tstring tortoiseProcPath = GetAppDirectory() + _T("TortoiseGitProc.exe");
539 CCreateProcessHelper::CreateProcessDetached(tortoiseProcPath.c_str(), const_cast<TCHAR*>(command.c_str()));
542 LRESULT CMainWindow::SendEditor(UINT Msg, WPARAM wParam, LPARAM lParam)
544 if (m_directFunction)
546 return ((SciFnDirect) m_directFunction)(m_directPointer, Msg, wParam, lParam);
548 return ::SendMessage(m_hWndEdit, Msg, wParam, lParam);
551 bool CMainWindow::Initialize()
553 m_hWndEdit = ::CreateWindow(
554 _T("Scintilla"),
555 _T("Source"),
556 WS_CHILD | WS_VSCROLL | WS_HSCROLL | WS_CLIPCHILDREN,
557 CW_USEDEFAULT, CW_USEDEFAULT,
558 CW_USEDEFAULT, CW_USEDEFAULT,
559 *this,
561 hResource,
563 if (m_hWndEdit == NULL)
564 return false;
566 RECT rect;
567 GetClientRect(*this, &rect);
568 ::SetWindowPos(m_hWndEdit, HWND_TOP,
569 rect.left, rect.top,
570 rect.right-rect.left, rect.bottom-rect.top,
571 SWP_SHOWWINDOW);
573 m_directFunction = SendMessage(m_hWndEdit, SCI_GETDIRECTFUNCTION, 0, 0);
574 m_directPointer = SendMessage(m_hWndEdit, SCI_GETDIRECTPOINTER, 0, 0);
576 // Set up the global default style. These attributes are used wherever no explicit choices are made.
577 SetAStyle(STYLE_DEFAULT, ::GetSysColor(COLOR_WINDOWTEXT), ::GetSysColor(COLOR_WINDOW),
578 // Reusing TortoiseBlame's setting which already have an user friendly
579 // pane in TortoiseSVN's Settings dialog, while there is no such
580 // pane for TortoiseUDiff.
581 CRegStdDWORD(_T("Software\\TortoiseGit\\BlameFontSize"), 10),
582 CUnicodeUtils::StdGetUTF8(CRegStdString(_T("Software\\TortoiseGit\\BlameFontName"), _T("Courier New"))).c_str());
583 SendEditor(SCI_SETTABWIDTH, 4);
584 SendEditor(SCI_SETREADONLY, TRUE);
585 LRESULT pix = SendEditor(SCI_TEXTWIDTH, STYLE_LINENUMBER, (LPARAM)"_99999");
586 SendEditor(SCI_SETMARGINWIDTHN, 0, pix);
587 SendEditor(SCI_SETMARGINWIDTHN, 1);
588 SendEditor(SCI_SETMARGINWIDTHN, 2);
589 //Set the default windows colors for edit controls
590 SendEditor(SCI_STYLESETFORE, STYLE_DEFAULT, ::GetSysColor(COLOR_WINDOWTEXT));
591 SendEditor(SCI_STYLESETBACK, STYLE_DEFAULT, ::GetSysColor(COLOR_WINDOW));
592 SendEditor(SCI_SETSELFORE, TRUE, ::GetSysColor(COLOR_HIGHLIGHTTEXT));
593 SendEditor(SCI_SETSELBACK, TRUE, ::GetSysColor(COLOR_HIGHLIGHT));
594 SendEditor(SCI_SETCARETFORE, ::GetSysColor(COLOR_WINDOWTEXT));
595 CRegStdDWORD used2d(L"Software\\TortoiseGit\\ScintillaDirect2D", FALSE);
596 if (SysInfo::Instance().IsWin7OrLater() && DWORD(used2d))
598 SendEditor(SCI_SETTECHNOLOGY, SC_TECHNOLOGY_DIRECTWRITE);
599 SendEditor(SCI_SETBUFFEREDDRAW, 0);
601 SendEditor(SCI_SETVIEWWS, 1);
602 SendEditor(SCI_SETWHITESPACESIZE, 2);
603 SendEditor(SCI_SETWHITESPACEFORE, true, ::GetSysColor(COLOR_3DSHADOW));
604 SendEditor(SCI_STYLESETVISIBLE, STYLE_CONTROLCHAR, TRUE);
606 return true;
609 bool CMainWindow::LoadFile(HANDLE hFile)
611 InitEditor();
612 char data[4096] = { 0 };
613 DWORD dwRead = 0;
615 BOOL bRet = ReadFile(hFile, data, sizeof(data), &dwRead, NULL);
616 bool bUTF8 = IsUTF8(data, dwRead);
617 while ((dwRead > 0) && (bRet))
619 SendEditor(SCI_ADDTEXT, dwRead,
620 reinterpret_cast<LPARAM>(static_cast<char *>(data)));
621 bRet = ReadFile(hFile, data, sizeof(data), &dwRead, NULL);
623 SetupWindow(bUTF8);
624 return true;
627 bool CMainWindow::LoadFile(LPCTSTR filename)
629 InitEditor();
630 FILE *fp = NULL;
631 _tfopen_s(&fp, filename, _T("rb"));
632 if (!fp)
633 return false;
635 //SetTitle();
636 char data[4096] = { 0 };
637 size_t lenFile = fread(data, 1, sizeof(data), fp);
638 bool bUTF8 = IsUTF8(data, lenFile);
639 while (lenFile > 0)
641 SendEditor(SCI_ADDTEXT, lenFile,
642 reinterpret_cast<LPARAM>(static_cast<char *>(data)));
643 lenFile = fread(data, 1, sizeof(data), fp);
645 fclose(fp);
646 SetupWindow(bUTF8);
647 m_filename = filename;
648 return true;
651 void CMainWindow::InitEditor()
653 SendEditor(SCI_SETREADONLY, FALSE);
654 SendEditor(SCI_CLEARALL);
655 SendEditor(EM_EMPTYUNDOBUFFER);
656 SendEditor(SCI_SETSAVEPOINT);
657 SendEditor(SCI_CANCEL);
658 SendEditor(SCI_SETUNDOCOLLECTION, 0);
661 void CMainWindow::SetupWindow(bool bUTF8)
663 SendEditor(SCI_SETCODEPAGE, bUTF8 ? SC_CP_UTF8 : GetACP());
665 SendEditor(SCI_SETUNDOCOLLECTION, 1);
666 ::SetFocus(m_hWndEdit);
667 SendEditor(EM_EMPTYUNDOBUFFER);
668 SendEditor(SCI_SETSAVEPOINT);
669 SendEditor(SCI_GOTOPOS, 0);
671 SendEditor(SCI_CLEARDOCUMENTSTYLE, 0, 0);
672 SendEditor(SCI_SETSTYLEBITS, 5, 0);
674 //SetAStyle(SCE_DIFF_DEFAULT, RGB(0, 0, 0));
675 SetAStyle(SCE_DIFF_COMMAND, RGB(0x0A, 0x24, 0x36));
676 SetAStyle(SCE_DIFF_POSITION, RGB(0xFF, 0, 0));
677 SetAStyle(SCE_DIFF_HEADER, RGB(0x80, 0, 0), RGB(0xFF, 0xFF, 0x80));
678 SetAStyle(SCE_DIFF_COMMENT, RGB(0, 0x80, 0));
679 SendEditor(SCI_STYLESETBOLD, SCE_DIFF_COMMENT, TRUE);
680 SetAStyle(SCE_DIFF_DELETED, ::GetSysColor(COLOR_WINDOWTEXT), RGB(0xFF, 0x80, 0x80));
681 SetAStyle(SCE_DIFF_ADDED, ::GetSysColor(COLOR_WINDOWTEXT), RGB(0x80, 0xFF, 0x80));
683 SendEditor(SCI_SETLEXER, SCLEX_DIFF);
684 SendEditor(SCI_SETKEYWORDS, 0, (LPARAM)"revision");
685 SendEditor(SCI_COLOURISE, 0, -1);
686 ::ShowWindow(m_hWndEdit, SW_SHOW);
689 bool CMainWindow::SaveFile(LPCTSTR filename)
691 FILE *fp = NULL;
692 _tfopen_s(&fp, filename, _T("w+b"));
693 if (!fp)
694 return false;
696 LRESULT len = SendEditor(SCI_GETTEXT, 0, 0);
697 std::unique_ptr<char[]> data (new char[len+1]);
698 SendEditor(SCI_GETTEXT, len, reinterpret_cast<LPARAM>(static_cast<char *>(data.get())));
699 fwrite(data.get(), sizeof(char), len-1, fp);
700 fclose(fp);
702 SendEditor(SCI_SETSAVEPOINT);
703 ::ShowWindow(m_hWndEdit, SW_SHOW);
704 return true;
707 void CMainWindow::SetTitle(LPCTSTR title)
709 size_t len = _tcslen(title);
710 std::unique_ptr<TCHAR[]> pBuf(new TCHAR[len + 40]);
711 _stprintf_s(pBuf.get(), len + 40, _T("%s - TortoiseGitUDiff"), title);
712 SetWindowTitle(std::wstring(pBuf.get()));
715 void CMainWindow::SetAStyle(int style, COLORREF fore, COLORREF back, int size, const char *face)
717 SendEditor(SCI_STYLESETFORE, style, fore);
718 SendEditor(SCI_STYLESETBACK, style, back);
719 if (size >= 1)
720 SendEditor(SCI_STYLESETSIZE, style, size);
721 if (face)
722 SendEditor(SCI_STYLESETFONT, style, reinterpret_cast<LPARAM>(face));
725 bool CMainWindow::IsUTF8(LPVOID pBuffer, size_t cb)
727 if (cb < 2)
728 return true;
729 UINT16 * pVal16 = (UINT16 *)pBuffer;
730 UINT8 * pVal8 = (UINT8 *)(pVal16+1);
731 // scan the whole buffer for a 0x0000 sequence
732 // if found, we assume a binary file
733 for (size_t i=0; i<(cb-2); i=i+2)
735 if (0x0000 == *pVal16++)
736 return false;
738 pVal16 = (UINT16 *)pBuffer;
739 if (*pVal16 == 0xFEFF)
740 return false;
741 if (cb < 3)
742 return false;
743 if (*pVal16 == 0xBBEF)
745 if (*pVal8 == 0xBF)
746 return true;
748 // check for illegal UTF8 chars
749 pVal8 = (UINT8 *)pBuffer;
750 for (size_t i=0; i<cb; ++i)
752 if ((*pVal8 == 0xC0)||(*pVal8 == 0xC1)||(*pVal8 >= 0xF5))
753 return false;
754 pVal8++;
756 pVal8 = (UINT8 *)pBuffer;
757 bool bUTF8 = false;
758 for (size_t i=0; i<(cb-3); ++i)
760 if ((*pVal8 & 0xE0)==0xC0)
762 pVal8++;i++;
763 if ((*pVal8 & 0xC0)!=0x80)
764 return false;
765 bUTF8 = true;
767 else if ((*pVal8 & 0xF0)==0xE0)
769 pVal8++;i++;
770 if ((*pVal8 & 0xC0)!=0x80)
771 return false;
772 pVal8++;i++;
773 if ((*pVal8 & 0xC0)!=0x80)
774 return false;
775 bUTF8 = true;
777 else if ((*pVal8 & 0xF8)==0xF0)
779 pVal8++;i++;
780 if ((*pVal8 & 0xC0)!=0x80)
781 return false;
782 pVal8++;i++;
783 if ((*pVal8 & 0xC0)!=0x80)
784 return false;
785 pVal8++;i++;
786 if ((*pVal8 & 0xC0)!=0x80)
787 return false;
788 bUTF8 = true;
790 else if (*pVal8 >= 0x80)
791 return false;
793 pVal8++;
795 if (bUTF8)
796 return true;
797 return false;
800 void CMainWindow::loadOrSaveFile(bool doLoad, const std::wstring& filename /* = L"" */)
802 OPENFILENAME ofn = {0}; // common dialog box structure
803 TCHAR szFile[MAX_PATH] = {0}; // buffer for file name
804 // Initialize OPENFILENAME
805 ofn.lStructSize = sizeof(OPENFILENAME);
806 ofn.hwndOwner = *this;
807 ofn.lpstrFile = szFile;
808 ofn.nMaxFile = sizeof(szFile)/sizeof(TCHAR);
809 TCHAR filter[1024] = { 0 };
810 LoadString(hResource, IDS_PATCHFILEFILTER, filter, sizeof(filter)/sizeof(TCHAR));
811 CStringUtils::PipesToNulls(filter);
812 ofn.lpstrFilter = filter;
813 ofn.nFilterIndex = 1;
814 ofn.lpstrFileTitle = NULL;
815 ofn.nMaxFileTitle = 0;
816 ofn.lpstrInitialDir = NULL;
817 TCHAR fileTitle[1024] = { 0 };
818 LoadString(hResource, doLoad ? IDS_OPENPATCH : IDS_SAVEPATCH, fileTitle, sizeof(fileTitle)/sizeof(TCHAR));
819 ofn.lpstrTitle = fileTitle;
820 ofn.Flags = OFN_ENABLESIZING | OFN_EXPLORER;
821 if(doLoad)
822 ofn.Flags |= OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
823 else
824 ofn.Flags |= OFN_OVERWRITEPROMPT;
825 // Display the Open dialog box.
826 if( doLoad )
828 if (GetOpenFileName(&ofn)==TRUE)
830 LoadFile(ofn.lpstrFile);
833 else
835 if (filename.empty())
837 if (GetSaveFileName(&ofn)==TRUE)
839 SaveFile(ofn.lpstrFile);
842 else
843 SaveFile(filename.c_str());