cleanup
[TortoiseGit.git] / src / TortoiseUDiff / MainWindow.cpp
blob9af7b35ff35f02ea72d5a68b5d71cb38b5e17d9c
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"
23 CMainWindow::CMainWindow(HINSTANCE hInst, const WNDCLASSEX* wcx /* = NULL*/)
24 : CWindow(hInst, wcx)
25 , m_bShowFindBar(false)
27 SetWindowTitle(_T("TortoiseUDiff"));
30 CMainWindow::~CMainWindow(void)
34 bool CMainWindow::RegisterAndCreateWindow()
36 WNDCLASSEX wcx;
38 // Fill in the window class structure with default parameters
39 wcx.cbSize = sizeof(WNDCLASSEX);
40 wcx.style = CS_HREDRAW | CS_VREDRAW;
41 wcx.lpfnWndProc = CWindow::stWinMsgHandler;
42 wcx.cbClsExtra = 0;
43 wcx.cbWndExtra = 0;
44 wcx.hInstance = hResource;
45 wcx.hCursor = NULL;
46 wcx.lpszClassName = ResString(hResource, IDS_APP_TITLE);
47 wcx.hIcon = LoadIcon(hResource, MAKEINTRESOURCE(IDI_TORTOISEUDIFF));
48 wcx.hbrBackground = (HBRUSH)(COLOR_3DFACE+1);
49 wcx.lpszMenuName = MAKEINTRESOURCE(IDC_TORTOISEUDIFF);
50 wcx.hIconSm = LoadIcon(wcx.hInstance, MAKEINTRESOURCE(IDI_TORTOISEUDIFF));
51 if (RegisterWindow(&wcx))
53 if (Create(WS_CAPTION | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_SIZEBOX | WS_SYSMENU | WS_CLIPCHILDREN, NULL))
55 m_FindBar.SetParent(*this);
56 m_FindBar.Create(hResource, IDD_FINDBAR, *this);
57 ShowWindow(*this, SW_SHOW);
58 UpdateWindow(*this);
59 return true;
62 return false;
65 LRESULT CALLBACK CMainWindow::WinMsgHandler(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
67 switch (uMsg)
69 case WM_CREATE:
71 m_hwnd = hwnd;
72 Initialize();
74 break;
75 case WM_COMMAND:
77 return DoCommand(LOWORD(wParam));
79 break;
80 case WM_MOUSEWHEEL:
82 if (GET_KEYSTATE_WPARAM(wParam) == MK_SHIFT)
84 // scroll sideways
85 SendEditor(SCI_LINESCROLL, -GET_WHEEL_DELTA_WPARAM(wParam)/40, 0);
87 else
88 return DefWindowProc(hwnd, uMsg, wParam, lParam);
90 break;
91 case WM_SIZE:
93 RECT rect;
94 GetClientRect(*this, &rect);
95 if (m_bShowFindBar)
97 ::SetWindowPos(m_hWndEdit, HWND_TOP,
98 rect.left, rect.top,
99 rect.right-rect.left, rect.bottom-rect.top-30,
100 SWP_SHOWWINDOW);
101 ::SetWindowPos(m_FindBar, HWND_TOP,
102 rect.left, rect.bottom-30,
103 rect.right-rect.left, 30,
104 SWP_SHOWWINDOW);
106 else
108 ::SetWindowPos(m_hWndEdit, HWND_TOP,
109 rect.left, rect.top,
110 rect.right-rect.left, rect.bottom-rect.top,
111 SWP_SHOWWINDOW);
112 ::ShowWindow(m_FindBar, SW_HIDE);
115 break;
116 case WM_GETMINMAXINFO:
118 MINMAXINFO * mmi = (MINMAXINFO*)lParam;
119 mmi->ptMinTrackSize.x = 100;
120 mmi->ptMinTrackSize.y = 100;
121 return 0;
123 break;
124 case WM_DESTROY:
125 PostQuitMessage(0);
126 break;
127 case WM_CLOSE:
129 CRegStdDWORD w = CRegStdDWORD(_T("Software\\TortoiseGit\\UDiffViewerWidth"), (DWORD)CW_USEDEFAULT);
130 CRegStdDWORD h = CRegStdDWORD(_T("Software\\TortoiseGit\\UDiffViewerHeight"), (DWORD)CW_USEDEFAULT);
131 CRegStdDWORD p = CRegStdDWORD(_T("Software\\TortoiseGit\\UDiffViewerPos"), 0);
133 RECT rect;
134 ::GetWindowRect(*this, &rect);
135 w = rect.right-rect.left;
136 h = rect.bottom-rect.top;
137 p = MAKELONG(rect.left, rect.top);
139 ::DestroyWindow(m_hwnd);
140 break;
141 case WM_SETFOCUS:
142 SetFocus(m_hWndEdit);
143 break;
144 case COMMITMONITOR_FINDMSGNEXT:
146 SendEditor(SCI_CHARRIGHT);
147 SendEditor(SCI_SEARCHANCHOR);
148 m_bMatchCase = !!wParam;
149 m_findtext = (LPCTSTR)lParam;
150 SendEditor(SCI_SEARCHNEXT, m_bMatchCase ? SCFIND_MATCHCASE : 0, (LPARAM)CUnicodeUtils::StdGetUTF8(m_findtext).c_str());
151 SendEditor(SCI_SCROLLCARET);
153 break;
154 case COMMITMONITOR_FINDMSGPREV:
156 SendEditor(SCI_SEARCHANCHOR);
157 m_bMatchCase = !!wParam;
158 m_findtext = (LPCTSTR)lParam;
159 SendEditor(SCI_SEARCHPREV, m_bMatchCase ? SCFIND_MATCHCASE : 0, (LPARAM)CUnicodeUtils::StdGetUTF8(m_findtext).c_str());
160 SendEditor(SCI_SCROLLCARET);
162 break;
163 case COMMITMONITOR_FINDEXIT:
165 RECT rect;
166 GetClientRect(*this, &rect);
167 m_bShowFindBar = false;
168 ::ShowWindow(m_FindBar, SW_HIDE);
169 ::SetWindowPos(m_hWndEdit, HWND_TOP,
170 rect.left, rect.top,
171 rect.right-rect.left, rect.bottom-rect.top,
172 SWP_SHOWWINDOW);
174 break;
175 case COMMITMONITOR_FINDRESET:
176 SendEditor(SCI_SETSELECTIONSTART, 0);
177 SendEditor(SCI_SETSELECTIONEND, 0);
178 SendEditor(SCI_SEARCHANCHOR);
179 break;
180 default:
181 return DefWindowProc(hwnd, uMsg, wParam, lParam);
184 return 0;
187 LRESULT CMainWindow::DoCommand(int id)
189 switch (id)
191 case ID_FILE_OPEN:
193 OPENFILENAME ofn = {0}; // common dialog box structure
194 TCHAR szFile[MAX_PATH] = {0}; // buffer for file name
195 // Initialize OPENFILENAME
196 ofn.lStructSize = sizeof(OPENFILENAME);
197 ofn.hwndOwner = *this;
198 ofn.lpstrFile = szFile;
199 ofn.nMaxFile = _countof(szFile);
200 TCHAR filter[1024];
201 LoadString(hResource, IDS_PATCHFILEFILTER, filter, _countof(filter));
202 TCHAR * pszFilters = filter;
203 // Replace '|' delimiters with '\0's
204 TCHAR *ptr = pszFilters + _tcslen(pszFilters); //set ptr at the NULL
205 while (ptr != pszFilters)
207 if (*ptr == '|')
208 *ptr = '\0';
209 ptr--;
211 ofn.lpstrFilter = pszFilters;
212 ofn.nFilterIndex = 1;
213 ofn.lpstrFileTitle = NULL;
214 ofn.nMaxFileTitle = 0;
215 ofn.lpstrInitialDir = NULL;
216 TCHAR opentitle[1024];
217 LoadString(hResource, IDS_OPENPATCH, opentitle, _countof(opentitle));
218 ofn.lpstrTitle = opentitle;
219 ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_ENABLESIZING | OFN_EXPLORER;
220 // Display the Open dialog box.
221 if (GetOpenFileName(&ofn)==TRUE)
223 LoadFile(ofn.lpstrFile);
226 break;
227 case ID_FILE_SAVEAS:
229 OPENFILENAME ofn = {0}; // common dialog box structure
230 TCHAR szFile[MAX_PATH] = {0}; // buffer for file name
231 // Initialize OPENFILENAME
232 ofn.lStructSize = sizeof(OPENFILENAME);
233 ofn.hwndOwner = *this;
234 ofn.lpstrFile = szFile;
235 ofn.nMaxFile = _countof(szFile);
236 TCHAR filter[1024];
237 LoadString(hResource, IDS_PATCHFILEFILTER, filter, _countof(filter));
238 TCHAR * pszFilters = filter;
239 // Replace '|' delimiters with '\0's
240 TCHAR *ptr = pszFilters + _tcslen(pszFilters); //set ptr at the NULL
241 while (ptr != pszFilters)
243 if (*ptr == '|')
244 *ptr = '\0';
245 ptr--;
247 ofn.lpstrFilter = pszFilters;
248 ofn.nFilterIndex = 1;
249 ofn.lpstrFileTitle = NULL;
250 ofn.nMaxFileTitle = 0;
251 ofn.lpstrInitialDir = NULL;
252 TCHAR savetitle[1024];
253 LoadString(hResource, IDS_SAVEPATCH, savetitle, _countof(savetitle));
254 ofn.lpstrTitle = savetitle;
255 ofn.Flags = OFN_OVERWRITEPROMPT | OFN_ENABLESIZING | OFN_EXPLORER;
256 // Display the Open dialog box.
257 if (GetSaveFileName(&ofn)==TRUE)
259 SaveFile(ofn.lpstrFile);
262 break;
263 case ID_FILE_EXIT:
264 ::PostQuitMessage(0);
265 return 0;
266 case IDM_SHOWFINDBAR:
268 m_bShowFindBar = true;
269 ::ShowWindow(m_FindBar, SW_SHOW);
270 RECT rect;
271 GetClientRect(*this, &rect);
272 ::SetWindowPos(m_hWndEdit, HWND_TOP,
273 rect.left, rect.top,
274 rect.right-rect.left, rect.bottom-rect.top-30,
275 SWP_SHOWWINDOW);
276 ::SetWindowPos(m_FindBar, HWND_TOP,
277 rect.left, rect.bottom-30,
278 rect.right-rect.left, 30,
279 SWP_SHOWWINDOW);
280 ::SetFocus(m_FindBar);
281 SendEditor(SCI_SETSELECTIONSTART, 0);
282 SendEditor(SCI_SETSELECTIONEND, 0);
283 SendEditor(SCI_SEARCHANCHOR);
285 break;
286 case IDM_FINDNEXT:
287 SendEditor(SCI_CHARRIGHT);
288 SendEditor(SCI_SEARCHANCHOR);
289 SendEditor(SCI_SEARCHNEXT, m_bMatchCase ? SCFIND_MATCHCASE : 0, (LPARAM)CUnicodeUtils::StdGetUTF8(m_findtext).c_str());
290 SendEditor(SCI_SCROLLCARET);
291 break;
292 case IDM_FINDPREV:
293 SendEditor(SCI_SEARCHANCHOR);
294 SendEditor(SCI_SEARCHPREV, m_bMatchCase ? SCFIND_MATCHCASE : 0, (LPARAM)CUnicodeUtils::StdGetUTF8(m_findtext).c_str());
295 SendEditor(SCI_SCROLLCARET);
296 break;
297 case IDM_FINDEXIT:
299 if (IsWindowVisible(m_FindBar))
301 RECT rect;
302 GetClientRect(*this, &rect);
303 m_bShowFindBar = false;
304 ::ShowWindow(m_FindBar, SW_HIDE);
305 ::SetWindowPos(m_hWndEdit, HWND_TOP,
306 rect.left, rect.top,
307 rect.right-rect.left, rect.bottom-rect.top,
308 SWP_SHOWWINDOW);
310 else
311 PostQuitMessage(0);
313 break;
314 default:
315 break;
317 return 1;
321 LRESULT CMainWindow::SendEditor(UINT Msg, WPARAM wParam, LPARAM lParam)
323 if (m_directFunction)
325 return ((SciFnDirect) m_directFunction)(m_directPointer, Msg, wParam, lParam);
327 return ::SendMessage(m_hWndEdit, Msg, wParam, lParam);
330 bool CMainWindow::Initialize()
332 CRegStdDWORD pos(_T("Software\\TortoiseGit\\UDiffViewerPos"), 0);
333 CRegStdDWORD width(_T("Software\\TortoiseGit\\UDiffViewerWidth"), (DWORD)640);
334 CRegStdDWORD height(_T("Software\\TortoiseGit\\UDiffViewerHeight"), (DWORD)480);
335 if (DWORD(pos) && DWORD(width) && DWORD(height))
337 RECT rc;
338 rc.left = LOWORD(DWORD(pos));
339 rc.top = HIWORD(DWORD(pos));
340 rc.right = rc.left + DWORD(width);
341 rc.bottom = rc.top + DWORD(height);
342 HMONITOR hMon = MonitorFromRect(&rc, MONITOR_DEFAULTTONULL);
343 if (hMon)
345 // only restore the window position if the monitor is valid
346 MoveWindow(*this, LOWORD(DWORD(pos)), HIWORD(DWORD(pos)),
347 DWORD(width), DWORD(height), FALSE);
351 m_hWndEdit = ::CreateWindow(
352 _T("Scintilla"),
353 _T("Source"),
354 WS_CHILD | WS_VSCROLL | WS_HSCROLL | WS_CLIPCHILDREN,
355 CW_USEDEFAULT, CW_USEDEFAULT,
356 CW_USEDEFAULT, CW_USEDEFAULT,
357 *this,
359 hResource,
361 if (m_hWndEdit == NULL)
362 return false;
364 RECT rect;
365 GetClientRect(*this, &rect);
366 ::SetWindowPos(m_hWndEdit, HWND_TOP,
367 rect.left, rect.top,
368 rect.right-rect.left, rect.bottom-rect.top,
369 SWP_SHOWWINDOW);
371 m_directFunction = SendMessage(m_hWndEdit, SCI_GETDIRECTFUNCTION, 0, 0);
372 m_directPointer = SendMessage(m_hWndEdit, SCI_GETDIRECTPOINTER, 0, 0);
374 // Set up the global default style. These attributes are used wherever no explicit choices are made.
375 SetAStyle(STYLE_DEFAULT, ::GetSysColor(COLOR_WINDOWTEXT), ::GetSysColor(COLOR_WINDOW),
376 // Reusing TortoiseBlame's setting which already have an user friendly
377 // pane in TortoiseSVN's Settings dialog, while there is no such
378 // pane for TortoiseUDiff.
379 CRegStdDWORD(_T("Software\\TortoiseGit\\BlameFontSize"), 10),
380 WideToMultibyte(CRegStdString(_T("Software\\TortoiseGit\\BlameFontName"), _T("Courier New"))).c_str());
381 SendEditor(SCI_SETTABWIDTH, 4);
382 SendEditor(SCI_SETREADONLY, TRUE);
383 LRESULT pix = SendEditor(SCI_TEXTWIDTH, STYLE_LINENUMBER, (LPARAM)"_99999");
384 SendEditor(SCI_SETMARGINWIDTHN, 0, pix);
385 SendEditor(SCI_SETMARGINWIDTHN, 1);
386 SendEditor(SCI_SETMARGINWIDTHN, 2);
387 //Set the default windows colors for edit controls
388 SendEditor(SCI_STYLESETFORE, STYLE_DEFAULT, ::GetSysColor(COLOR_WINDOWTEXT));
389 SendEditor(SCI_STYLESETBACK, STYLE_DEFAULT, ::GetSysColor(COLOR_WINDOW));
390 SendEditor(SCI_SETSELFORE, TRUE, ::GetSysColor(COLOR_HIGHLIGHTTEXT));
391 SendEditor(SCI_SETSELBACK, TRUE, ::GetSysColor(COLOR_HIGHLIGHT));
392 SendEditor(SCI_SETCARETFORE, ::GetSysColor(COLOR_WINDOWTEXT));
394 return true;
397 bool CMainWindow::LoadFile(LPCTSTR filename)
399 SendEditor(SCI_SETREADONLY, FALSE);
400 SendEditor(SCI_CLEARALL);
401 SendEditor(EM_EMPTYUNDOBUFFER);
402 SendEditor(SCI_SETSAVEPOINT);
403 SendEditor(SCI_CANCEL);
404 SendEditor(SCI_SETUNDOCOLLECTION, 0);
406 FILE *fp = NULL;
407 _tfopen_s(&fp, filename, _T("rb"));
408 if (fp)
410 //SetTitle();
411 char data[4096];
412 size_t lenFile = fread(data, 1, sizeof(data), fp);
413 bool bUTF8 = IsUTF8(data, lenFile);
414 while (lenFile > 0)
416 SendEditor(SCI_ADDTEXT, lenFile,
417 reinterpret_cast<LPARAM>(static_cast<char *>(data)));
418 lenFile = fread(data, 1, sizeof(data), fp);
420 fclose(fp);
421 SendEditor(SCI_SETCODEPAGE, bUTF8 ? SC_CP_UTF8 : GetACP());
423 else
425 return false;
428 SendEditor(SCI_SETUNDOCOLLECTION, 1);
429 ::SetFocus(m_hWndEdit);
430 SendEditor(EM_EMPTYUNDOBUFFER);
431 SendEditor(SCI_SETSAVEPOINT);
432 SendEditor(SCI_GOTOPOS, 0);
434 SendEditor(SCI_CLEARDOCUMENTSTYLE, 0, 0);
435 SendEditor(SCI_SETSTYLEBITS, 5, 0);
437 //SetAStyle(SCE_DIFF_DEFAULT, RGB(0, 0, 0));
438 SetAStyle(SCE_DIFF_COMMAND, RGB(0x0A, 0x24, 0x36));
439 SetAStyle(SCE_DIFF_POSITION, RGB(0xFF, 0, 0));
440 SetAStyle(SCE_DIFF_HEADER, RGB(0x80, 0, 0), RGB(0xFF, 0xFF, 0x80));
441 SetAStyle(SCE_DIFF_COMMENT, RGB(0, 0x80, 0));
442 SendEditor(SCI_STYLESETBOLD, SCE_DIFF_COMMENT, TRUE);
443 SetAStyle(SCE_DIFF_DELETED, ::GetSysColor(COLOR_WINDOWTEXT), RGB(0xFF, 0x80, 0x80));
444 SetAStyle(SCE_DIFF_ADDED, ::GetSysColor(COLOR_WINDOWTEXT), RGB(0x80, 0xFF, 0x80));
446 SendEditor(SCI_SETLEXER, SCLEX_DIFF);
447 SendEditor(SCI_SETKEYWORDS, 0, (LPARAM)"revision");
448 SendEditor(SCI_COLOURISE, 0, -1);
449 ::ShowWindow(m_hWndEdit, SW_SHOW);
450 return true;
453 bool CMainWindow::SaveFile(LPCTSTR filename)
455 FILE *fp = NULL;
456 _tfopen_s(&fp, filename, _T("w+b"));
457 if (fp)
459 int len = SendEditor(SCI_GETTEXT, 0, 0);
460 char * data = new char[len+1];
461 SendEditor(SCI_GETTEXT, len, (LPARAM)data);
462 fwrite(data, sizeof(char), len-1, fp);
463 fclose(fp);
465 else
467 return false;
470 SendEditor(SCI_SETSAVEPOINT);
471 ::ShowWindow(m_hWndEdit, SW_SHOW);
472 return true;
475 void CMainWindow::SetTitle(LPCTSTR title)
477 size_t len = _tcslen(title);
478 TCHAR * pBuf = new TCHAR[len+40];
479 _stprintf_s(pBuf, len+40, _T("%s - TortoiseUDiff"), title);
480 SetWindowTitle(std::wstring(pBuf));
481 delete [] pBuf;
484 void CMainWindow::SetAStyle(int style, COLORREF fore, COLORREF back, int size, const char *face)
486 SendEditor(SCI_STYLESETFORE, style, fore);
487 SendEditor(SCI_STYLESETBACK, style, back);
488 if (size >= 1)
489 SendEditor(SCI_STYLESETSIZE, style, size);
490 if (face)
491 SendEditor(SCI_STYLESETFONT, style, reinterpret_cast<LPARAM>(face));
494 bool CMainWindow::IsUTF8(LPVOID pBuffer, size_t cb)
496 if (cb < 2)
497 return true;
498 UINT16 * pVal = (UINT16 *)pBuffer;
499 UINT8 * pVal2 = (UINT8 *)(pVal+1);
500 // scan the whole buffer for a 0x0000 sequence
501 // if found, we assume a binary file
502 for (size_t i=0; i<(cb-2); i=i+2)
504 if (0x0000 == *pVal++)
505 return false;
507 pVal = (UINT16 *)pBuffer;
508 if (*pVal == 0xFEFF)
509 return false;
510 if (cb < 3)
511 return false;
512 if (*pVal == 0xBBEF)
514 if (*pVal2 == 0xBF)
515 return true;
517 // check for illegal UTF8 chars
518 pVal2 = (UINT8 *)pBuffer;
519 for (size_t i=0; i<cb; ++i)
521 if ((*pVal2 == 0xC0)||(*pVal2 == 0xC1)||(*pVal2 >= 0xF5))
522 return false;
523 pVal2++;
525 pVal2 = (UINT8 *)pBuffer;
526 bool bUTF8 = false;
527 for (size_t i=0; i<(cb-3); ++i)
529 if ((*pVal2 & 0xE0)==0xC0)
531 pVal2++;i++;
532 if ((*pVal2 & 0xC0)!=0x80)
533 return false;
534 bUTF8 = true;
536 if ((*pVal2 & 0xF0)==0xE0)
538 pVal2++;i++;
539 if ((*pVal2 & 0xC0)!=0x80)
540 return false;
541 pVal2++;i++;
542 if ((*pVal2 & 0xC0)!=0x80)
543 return false;
544 bUTF8 = true;
546 if ((*pVal2 & 0xF8)==0xF0)
548 pVal2++;i++;
549 if ((*pVal2 & 0xC0)!=0x80)
550 return false;
551 pVal2++;i++;
552 if ((*pVal2 & 0xC0)!=0x80)
553 return false;
554 pVal2++;i++;
555 if ((*pVal2 & 0xC0)!=0x80)
556 return false;
557 bUTF8 = true;
559 pVal2++;
561 if (bUTF8)
562 return true;
563 return false;