Make Scintilla use Direct2D on Vista and later
[TortoiseGit.git] / src / TortoiseUDiff / MainWindow.cpp
blob65af4cd73e710e39a47b86df9d6eb00fd6b17bed
1 // TortoiseSVN - a Windows shell extension for easy version control
3 // Copyright (C) 2003-2009 - TortoiseSVN
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 #include "StdAfx.h"
20 #include "MainWindow.h"
21 #include "UnicodeUtils.h"
22 #include "SysInfo.h"
24 CMainWindow::CMainWindow(HINSTANCE hInst, const WNDCLASSEX* wcx /* = NULL*/)
25 : CWindow(hInst, wcx)
26 , m_bShowFindBar(false)
28 SetWindowTitle(_T("TortoiseUDiff"));
31 CMainWindow::~CMainWindow(void)
35 bool CMainWindow::RegisterAndCreateWindow()
37 WNDCLASSEX wcx;
39 // Fill in the window class structure with default parameters
40 wcx.cbSize = sizeof(WNDCLASSEX);
41 wcx.style = CS_HREDRAW | CS_VREDRAW;
42 wcx.lpfnWndProc = CWindow::stWinMsgHandler;
43 wcx.cbClsExtra = 0;
44 wcx.cbWndExtra = 0;
45 wcx.hInstance = hResource;
46 wcx.hCursor = NULL;
47 wcx.lpszClassName = ResString(hResource, IDS_APP_TITLE);
48 wcx.hIcon = LoadIcon(hResource, MAKEINTRESOURCE(IDI_TORTOISEUDIFF));
49 wcx.hbrBackground = (HBRUSH)(COLOR_3DFACE+1);
50 wcx.lpszMenuName = MAKEINTRESOURCE(IDC_TORTOISEUDIFF);
51 wcx.hIconSm = LoadIcon(wcx.hInstance, MAKEINTRESOURCE(IDI_TORTOISEUDIFF));
52 if (RegisterWindow(&wcx))
54 if (Create(WS_CAPTION | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_SIZEBOX | WS_SYSMENU | WS_CLIPCHILDREN, NULL))
56 m_FindBar.SetParent(*this);
57 m_FindBar.Create(hResource, IDD_FINDBAR, *this);
58 ShowWindow(*this, SW_SHOW);
59 UpdateWindow(*this);
60 return true;
63 return false;
66 LRESULT CALLBACK CMainWindow::WinMsgHandler(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
68 switch (uMsg)
70 case WM_CREATE:
72 m_hwnd = hwnd;
73 Initialize();
75 break;
76 case WM_COMMAND:
78 return DoCommand(LOWORD(wParam));
80 break;
81 case WM_MOUSEWHEEL:
83 if (GET_KEYSTATE_WPARAM(wParam) == MK_SHIFT)
85 // scroll sideways
86 SendEditor(SCI_LINESCROLL, -GET_WHEEL_DELTA_WPARAM(wParam)/40, 0);
88 else
89 return DefWindowProc(hwnd, uMsg, wParam, lParam);
91 break;
92 case WM_SIZE:
94 RECT rect;
95 GetClientRect(*this, &rect);
96 if (m_bShowFindBar)
98 ::SetWindowPos(m_hWndEdit, HWND_TOP,
99 rect.left, rect.top,
100 rect.right-rect.left, rect.bottom-rect.top-30,
101 SWP_SHOWWINDOW);
102 ::SetWindowPos(m_FindBar, HWND_TOP,
103 rect.left, rect.bottom-30,
104 rect.right-rect.left, 30,
105 SWP_SHOWWINDOW);
107 else
109 ::SetWindowPos(m_hWndEdit, HWND_TOP,
110 rect.left, rect.top,
111 rect.right-rect.left, rect.bottom-rect.top,
112 SWP_SHOWWINDOW);
113 ::ShowWindow(m_FindBar, SW_HIDE);
116 break;
117 case WM_GETMINMAXINFO:
119 MINMAXINFO * mmi = (MINMAXINFO*)lParam;
120 mmi->ptMinTrackSize.x = 100;
121 mmi->ptMinTrackSize.y = 100;
122 return 0;
124 break;
125 case WM_DESTROY:
126 PostQuitMessage(0);
127 break;
128 case WM_CLOSE:
130 CRegStdDWORD w = CRegStdDWORD(_T("Software\\TortoiseGit\\UDiffViewerWidth"), (DWORD)CW_USEDEFAULT);
131 CRegStdDWORD h = CRegStdDWORD(_T("Software\\TortoiseGit\\UDiffViewerHeight"), (DWORD)CW_USEDEFAULT);
132 CRegStdDWORD p = CRegStdDWORD(_T("Software\\TortoiseGit\\UDiffViewerPos"), 0);
134 RECT rect;
135 ::GetWindowRect(*this, &rect);
136 w = rect.right-rect.left;
137 h = rect.bottom-rect.top;
138 p = MAKELONG(rect.left, rect.top);
140 ::DestroyWindow(m_hwnd);
141 break;
142 case WM_SETFOCUS:
143 SetFocus(m_hWndEdit);
144 break;
145 case COMMITMONITOR_FINDMSGNEXT:
147 SendEditor(SCI_CHARRIGHT);
148 SendEditor(SCI_SEARCHANCHOR);
149 m_bMatchCase = !!wParam;
150 m_findtext = (LPCTSTR)lParam;
151 SendEditor(SCI_SEARCHNEXT, m_bMatchCase ? SCFIND_MATCHCASE : 0, (LPARAM)CUnicodeUtils::StdGetUTF8(m_findtext).c_str());
152 SendEditor(SCI_SCROLLCARET);
154 break;
155 case COMMITMONITOR_FINDMSGPREV:
157 SendEditor(SCI_SEARCHANCHOR);
158 m_bMatchCase = !!wParam;
159 m_findtext = (LPCTSTR)lParam;
160 SendEditor(SCI_SEARCHPREV, m_bMatchCase ? SCFIND_MATCHCASE : 0, (LPARAM)CUnicodeUtils::StdGetUTF8(m_findtext).c_str());
161 SendEditor(SCI_SCROLLCARET);
163 break;
164 case COMMITMONITOR_FINDEXIT:
166 RECT rect;
167 GetClientRect(*this, &rect);
168 m_bShowFindBar = false;
169 ::ShowWindow(m_FindBar, SW_HIDE);
170 ::SetWindowPos(m_hWndEdit, HWND_TOP,
171 rect.left, rect.top,
172 rect.right-rect.left, rect.bottom-rect.top,
173 SWP_SHOWWINDOW);
175 break;
176 case COMMITMONITOR_FINDRESET:
177 SendEditor(SCI_SETSELECTIONSTART, 0);
178 SendEditor(SCI_SETSELECTIONEND, 0);
179 SendEditor(SCI_SEARCHANCHOR);
180 break;
181 default:
182 return DefWindowProc(hwnd, uMsg, wParam, lParam);
185 return 0;
188 LRESULT CMainWindow::DoCommand(int id)
190 switch (id)
192 case ID_FILE_OPEN:
194 OPENFILENAME ofn = {0}; // common dialog box structure
195 TCHAR szFile[MAX_PATH] = {0}; // buffer for file name
196 // Initialize OPENFILENAME
197 ofn.lStructSize = sizeof(OPENFILENAME);
198 ofn.hwndOwner = *this;
199 ofn.lpstrFile = szFile;
200 ofn.nMaxFile = _countof(szFile);
201 TCHAR filter[1024];
202 LoadString(hResource, IDS_PATCHFILEFILTER, filter, _countof(filter));
203 TCHAR * pszFilters = filter;
204 // Replace '|' delimiters with '\0's
205 TCHAR *ptr = pszFilters + _tcslen(pszFilters); //set ptr at the NULL
206 while (ptr != pszFilters)
208 if (*ptr == '|')
209 *ptr = '\0';
210 ptr--;
212 ofn.lpstrFilter = pszFilters;
213 ofn.nFilterIndex = 1;
214 ofn.lpstrFileTitle = NULL;
215 ofn.nMaxFileTitle = 0;
216 ofn.lpstrInitialDir = NULL;
217 TCHAR opentitle[1024];
218 LoadString(hResource, IDS_OPENPATCH, opentitle, _countof(opentitle));
219 ofn.lpstrTitle = opentitle;
220 ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_ENABLESIZING | OFN_EXPLORER;
221 // Display the Open dialog box.
222 if (GetOpenFileName(&ofn)==TRUE)
224 LoadFile(ofn.lpstrFile);
227 break;
228 case ID_FILE_SAVEAS:
230 OPENFILENAME ofn = {0}; // common dialog box structure
231 TCHAR szFile[MAX_PATH] = {0}; // buffer for file name
232 // Initialize OPENFILENAME
233 ofn.lStructSize = sizeof(OPENFILENAME);
234 ofn.hwndOwner = *this;
235 ofn.lpstrFile = szFile;
236 ofn.nMaxFile = _countof(szFile);
237 TCHAR filter[1024];
238 LoadString(hResource, IDS_PATCHFILEFILTER, filter, _countof(filter));
239 TCHAR * pszFilters = filter;
240 // Replace '|' delimiters with '\0's
241 TCHAR *ptr = pszFilters + _tcslen(pszFilters); //set ptr at the NULL
242 while (ptr != pszFilters)
244 if (*ptr == '|')
245 *ptr = '\0';
246 ptr--;
248 ofn.lpstrFilter = pszFilters;
249 ofn.nFilterIndex = 1;
250 ofn.lpstrFileTitle = NULL;
251 ofn.nMaxFileTitle = 0;
252 ofn.lpstrInitialDir = NULL;
253 TCHAR savetitle[1024];
254 LoadString(hResource, IDS_SAVEPATCH, savetitle, _countof(savetitle));
255 ofn.lpstrTitle = savetitle;
256 ofn.Flags = OFN_OVERWRITEPROMPT | OFN_ENABLESIZING | OFN_EXPLORER;
257 // Display the Open dialog box.
258 if (GetSaveFileName(&ofn)==TRUE)
260 SaveFile(ofn.lpstrFile);
263 break;
264 case ID_FILE_EXIT:
265 ::PostQuitMessage(0);
266 return 0;
267 case IDM_SHOWFINDBAR:
269 m_bShowFindBar = true;
270 ::ShowWindow(m_FindBar, SW_SHOW);
271 RECT rect;
272 GetClientRect(*this, &rect);
273 ::SetWindowPos(m_hWndEdit, HWND_TOP,
274 rect.left, rect.top,
275 rect.right-rect.left, rect.bottom-rect.top-30,
276 SWP_SHOWWINDOW);
277 ::SetWindowPos(m_FindBar, HWND_TOP,
278 rect.left, rect.bottom-30,
279 rect.right-rect.left, 30,
280 SWP_SHOWWINDOW);
281 ::SetFocus(m_FindBar);
282 SendEditor(SCI_SETSELECTIONSTART, 0);
283 SendEditor(SCI_SETSELECTIONEND, 0);
284 SendEditor(SCI_SEARCHANCHOR);
286 break;
287 case IDM_FINDNEXT:
288 SendEditor(SCI_CHARRIGHT);
289 SendEditor(SCI_SEARCHANCHOR);
290 SendEditor(SCI_SEARCHNEXT, m_bMatchCase ? SCFIND_MATCHCASE : 0, (LPARAM)CUnicodeUtils::StdGetUTF8(m_findtext).c_str());
291 SendEditor(SCI_SCROLLCARET);
292 break;
293 case IDM_FINDPREV:
294 SendEditor(SCI_SEARCHANCHOR);
295 SendEditor(SCI_SEARCHPREV, m_bMatchCase ? SCFIND_MATCHCASE : 0, (LPARAM)CUnicodeUtils::StdGetUTF8(m_findtext).c_str());
296 SendEditor(SCI_SCROLLCARET);
297 break;
298 case IDM_FINDEXIT:
300 if (IsWindowVisible(m_FindBar))
302 RECT rect;
303 GetClientRect(*this, &rect);
304 m_bShowFindBar = false;
305 ::ShowWindow(m_FindBar, SW_HIDE);
306 ::SetWindowPos(m_hWndEdit, HWND_TOP,
307 rect.left, rect.top,
308 rect.right-rect.left, rect.bottom-rect.top,
309 SWP_SHOWWINDOW);
311 else
312 PostQuitMessage(0);
314 break;
315 default:
316 break;
318 return 1;
322 LRESULT CMainWindow::SendEditor(UINT Msg, WPARAM wParam, LPARAM lParam)
324 if (m_directFunction)
326 return ((SciFnDirect) m_directFunction)(m_directPointer, Msg, wParam, lParam);
328 return ::SendMessage(m_hWndEdit, Msg, wParam, lParam);
331 bool CMainWindow::Initialize()
333 CRegStdDWORD pos(_T("Software\\TortoiseGit\\UDiffViewerPos"), 0);
334 CRegStdDWORD width(_T("Software\\TortoiseGit\\UDiffViewerWidth"), (DWORD)640);
335 CRegStdDWORD height(_T("Software\\TortoiseGit\\UDiffViewerHeight"), (DWORD)480);
336 if (DWORD(pos) && DWORD(width) && DWORD(height))
338 RECT rc;
339 rc.left = LOWORD(DWORD(pos));
340 rc.top = HIWORD(DWORD(pos));
341 rc.right = rc.left + DWORD(width);
342 rc.bottom = rc.top + DWORD(height);
343 HMONITOR hMon = MonitorFromRect(&rc, MONITOR_DEFAULTTONULL);
344 if (hMon)
346 // only restore the window position if the monitor is valid
347 MoveWindow(*this, LOWORD(DWORD(pos)), HIWORD(DWORD(pos)),
348 DWORD(width), DWORD(height), FALSE);
352 m_hWndEdit = ::CreateWindow(
353 _T("Scintilla"),
354 _T("Source"),
355 WS_CHILD | WS_VSCROLL | WS_HSCROLL | WS_CLIPCHILDREN,
356 CW_USEDEFAULT, CW_USEDEFAULT,
357 CW_USEDEFAULT, CW_USEDEFAULT,
358 *this,
360 hResource,
362 if (m_hWndEdit == NULL)
363 return false;
365 RECT rect;
366 GetClientRect(*this, &rect);
367 ::SetWindowPos(m_hWndEdit, HWND_TOP,
368 rect.left, rect.top,
369 rect.right-rect.left, rect.bottom-rect.top,
370 SWP_SHOWWINDOW);
372 m_directFunction = SendMessage(m_hWndEdit, SCI_GETDIRECTFUNCTION, 0, 0);
373 m_directPointer = SendMessage(m_hWndEdit, SCI_GETDIRECTPOINTER, 0, 0);
375 // Set up the global default style. These attributes are used wherever no explicit choices are made.
376 SetAStyle(STYLE_DEFAULT, ::GetSysColor(COLOR_WINDOWTEXT), ::GetSysColor(COLOR_WINDOW),
377 // Reusing TortoiseBlame's setting which already have an user friendly
378 // pane in TortoiseSVN's Settings dialog, while there is no such
379 // pane for TortoiseUDiff.
380 CRegStdDWORD(_T("Software\\TortoiseGit\\BlameFontSize"), 10),
381 WideToMultibyte(CRegStdString(_T("Software\\TortoiseGit\\BlameFontName"), _T("Courier New"))).c_str());
382 SendEditor(SCI_SETTABWIDTH, 4);
383 SendEditor(SCI_SETREADONLY, TRUE);
384 LRESULT pix = SendEditor(SCI_TEXTWIDTH, STYLE_LINENUMBER, (LPARAM)"_99999");
385 SendEditor(SCI_SETMARGINWIDTHN, 0, pix);
386 SendEditor(SCI_SETMARGINWIDTHN, 1);
387 SendEditor(SCI_SETMARGINWIDTHN, 2);
388 //Set the default windows colors for edit controls
389 SendEditor(SCI_STYLESETFORE, STYLE_DEFAULT, ::GetSysColor(COLOR_WINDOWTEXT));
390 SendEditor(SCI_STYLESETBACK, STYLE_DEFAULT, ::GetSysColor(COLOR_WINDOW));
391 SendEditor(SCI_SETSELFORE, TRUE, ::GetSysColor(COLOR_HIGHLIGHTTEXT));
392 SendEditor(SCI_SETSELBACK, TRUE, ::GetSysColor(COLOR_HIGHLIGHT));
393 SendEditor(SCI_SETCARETFORE, ::GetSysColor(COLOR_WINDOWTEXT));
394 if (SysInfo::Instance().IsWin7OrLater())
396 SendEditor(SCI_SETTECHNOLOGY, SC_TECHNOLOGY_DIRECTWRITE);
397 SendEditor(SCI_SETBUFFEREDDRAW, 0);
399 SendEditor(SCI_SETFONTQUALITY, SC_EFF_QUALITY_LCD_OPTIMIZED);
401 return true;
404 bool CMainWindow::LoadFile(LPCTSTR filename)
406 SendEditor(SCI_SETREADONLY, FALSE);
407 SendEditor(SCI_CLEARALL);
408 SendEditor(EM_EMPTYUNDOBUFFER);
409 SendEditor(SCI_SETSAVEPOINT);
410 SendEditor(SCI_CANCEL);
411 SendEditor(SCI_SETUNDOCOLLECTION, 0);
413 FILE *fp = NULL;
414 _tfopen_s(&fp, filename, _T("rb"));
415 if (fp)
417 //SetTitle();
418 char data[4096];
419 size_t lenFile = fread(data, 1, sizeof(data), fp);
420 bool bUTF8 = IsUTF8(data, lenFile);
421 while (lenFile > 0)
423 SendEditor(SCI_ADDTEXT, lenFile,
424 reinterpret_cast<LPARAM>(static_cast<char *>(data)));
425 lenFile = fread(data, 1, sizeof(data), fp);
427 fclose(fp);
428 SendEditor(SCI_SETCODEPAGE, bUTF8 ? SC_CP_UTF8 : GetACP());
430 else
432 return false;
435 SendEditor(SCI_SETUNDOCOLLECTION, 1);
436 ::SetFocus(m_hWndEdit);
437 SendEditor(EM_EMPTYUNDOBUFFER);
438 SendEditor(SCI_SETSAVEPOINT);
439 SendEditor(SCI_GOTOPOS, 0);
441 SendEditor(SCI_CLEARDOCUMENTSTYLE, 0, 0);
442 SendEditor(SCI_SETSTYLEBITS, 5, 0);
444 //SetAStyle(SCE_DIFF_DEFAULT, RGB(0, 0, 0));
445 SetAStyle(SCE_DIFF_COMMAND, RGB(0x0A, 0x24, 0x36));
446 SetAStyle(SCE_DIFF_POSITION, RGB(0xFF, 0, 0));
447 SetAStyle(SCE_DIFF_HEADER, RGB(0x80, 0, 0), RGB(0xFF, 0xFF, 0x80));
448 SetAStyle(SCE_DIFF_COMMENT, RGB(0, 0x80, 0));
449 SendEditor(SCI_STYLESETBOLD, SCE_DIFF_COMMENT, TRUE);
450 SetAStyle(SCE_DIFF_DELETED, ::GetSysColor(COLOR_WINDOWTEXT), RGB(0xFF, 0x80, 0x80));
451 SetAStyle(SCE_DIFF_ADDED, ::GetSysColor(COLOR_WINDOWTEXT), RGB(0x80, 0xFF, 0x80));
453 SendEditor(SCI_SETLEXER, SCLEX_DIFF);
454 SendEditor(SCI_SETKEYWORDS, 0, (LPARAM)"revision");
455 SendEditor(SCI_COLOURISE, 0, -1);
456 ::ShowWindow(m_hWndEdit, SW_SHOW);
457 return true;
460 bool CMainWindow::SaveFile(LPCTSTR filename)
462 FILE *fp = NULL;
463 _tfopen_s(&fp, filename, _T("w+b"));
464 if (fp)
466 int len = SendEditor(SCI_GETTEXT, 0, 0);
467 char * data = new char[len+1];
468 SendEditor(SCI_GETTEXT, len, (LPARAM)data);
469 fwrite(data, sizeof(char), len-1, fp);
470 fclose(fp);
472 else
474 return false;
477 SendEditor(SCI_SETSAVEPOINT);
478 ::ShowWindow(m_hWndEdit, SW_SHOW);
479 return true;
482 void CMainWindow::SetTitle(LPCTSTR title)
484 size_t len = _tcslen(title);
485 TCHAR * pBuf = new TCHAR[len+40];
486 _stprintf_s(pBuf, len+40, _T("%s - TortoiseUDiff"), title);
487 SetWindowTitle(std::wstring(pBuf));
488 delete [] pBuf;
491 void CMainWindow::SetAStyle(int style, COLORREF fore, COLORREF back, int size, const char *face)
493 SendEditor(SCI_STYLESETFORE, style, fore);
494 SendEditor(SCI_STYLESETBACK, style, back);
495 if (size >= 1)
496 SendEditor(SCI_STYLESETSIZE, style, size);
497 if (face)
498 SendEditor(SCI_STYLESETFONT, style, reinterpret_cast<LPARAM>(face));
501 bool CMainWindow::IsUTF8(LPVOID pBuffer, size_t cb)
503 if (cb < 2)
504 return true;
505 UINT16 * pVal = (UINT16 *)pBuffer;
506 UINT8 * pVal2 = (UINT8 *)(pVal+1);
507 // scan the whole buffer for a 0x0000 sequence
508 // if found, we assume a binary file
509 for (size_t i=0; i<(cb-2); i=i+2)
511 if (0x0000 == *pVal++)
512 return false;
514 pVal = (UINT16 *)pBuffer;
515 if (*pVal == 0xFEFF)
516 return false;
517 if (cb < 3)
518 return false;
519 if (*pVal == 0xBBEF)
521 if (*pVal2 == 0xBF)
522 return true;
524 // check for illegal UTF8 chars
525 pVal2 = (UINT8 *)pBuffer;
526 for (size_t i=0; i<cb; ++i)
528 if ((*pVal2 == 0xC0)||(*pVal2 == 0xC1)||(*pVal2 >= 0xF5))
529 return false;
530 pVal2++;
532 pVal2 = (UINT8 *)pBuffer;
533 bool bUTF8 = false;
534 for (size_t i=0; i<(cb-3); ++i)
536 if ((*pVal2 & 0xE0)==0xC0)
538 pVal2++;i++;
539 if ((*pVal2 & 0xC0)!=0x80)
540 return false;
541 bUTF8 = true;
543 if ((*pVal2 & 0xF0)==0xE0)
545 pVal2++;i++;
546 if ((*pVal2 & 0xC0)!=0x80)
547 return false;
548 pVal2++;i++;
549 if ((*pVal2 & 0xC0)!=0x80)
550 return false;
551 bUTF8 = true;
553 if ((*pVal2 & 0xF8)==0xF0)
555 pVal2++;i++;
556 if ((*pVal2 & 0xC0)!=0x80)
557 return false;
558 pVal2++;i++;
559 if ((*pVal2 & 0xC0)!=0x80)
560 return false;
561 pVal2++;i++;
562 if ((*pVal2 & 0xC0)!=0x80)
563 return false;
564 bUTF8 = true;
566 pVal2++;
568 if (bUTF8)
569 return true;
570 return false;