Use static method
[TortoiseGit.git] / src / TortoiseUDiff / MainWindow.cpp
blob370e926f2eba9564721855ab6037932f36ebdab6
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2012 - TortoiseGit
4 // Copyright (C) 2003-2012 - 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 wcx.lpszClassName = ResString(hResource, IDS_APP_TITLE);
58 wcx.hIcon = LoadIcon(hResource, MAKEINTRESOURCE(IDI_TORTOISEUDIFF));
59 wcx.hbrBackground = (HBRUSH)(COLOR_3DFACE+1);
60 wcx.lpszMenuName = MAKEINTRESOURCE(IDC_TORTOISEUDIFF);
61 wcx.hIconSm = LoadIcon(wcx.hInstance, MAKEINTRESOURCE(IDI_TORTOISEUDIFF));
62 if (RegisterWindow(&wcx))
64 if (Create(WS_CAPTION | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_SIZEBOX | WS_SYSMENU | WS_CLIPCHILDREN, NULL))
66 m_FindBar.SetParent(*this);
67 m_FindBar.Create(hResource, IDD_FINDBAR, *this);
68 UpdateWindow(*this);
69 return true;
72 return false;
75 LRESULT CALLBACK CMainWindow::WinMsgHandler(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
77 if (uMsg == TaskBarButtonCreated)
79 SetUUIDOverlayIcon(hwnd);
81 switch (uMsg)
83 case WM_CREATE:
85 m_hwnd = hwnd;
86 Initialize();
88 break;
89 case WM_COMMAND:
91 return DoCommand(LOWORD(wParam));
93 break;
94 case WM_MOUSEWHEEL:
96 if (GET_KEYSTATE_WPARAM(wParam) == MK_SHIFT)
98 // scroll sideways
99 SendEditor(SCI_LINESCROLL, -GET_WHEEL_DELTA_WPARAM(wParam)/40, 0);
101 else
102 return DefWindowProc(hwnd, uMsg, wParam, lParam);
104 break;
105 case WM_SIZE:
107 RECT rect;
108 GetClientRect(*this, &rect);
109 if (m_bShowFindBar)
111 ::SetWindowPos(m_hWndEdit, HWND_TOP,
112 rect.left, rect.top,
113 rect.right-rect.left, rect.bottom-rect.top-30,
114 SWP_SHOWWINDOW);
115 ::SetWindowPos(m_FindBar, HWND_TOP,
116 rect.left, rect.bottom-30,
117 rect.right-rect.left, 30,
118 SWP_SHOWWINDOW);
120 else
122 ::SetWindowPos(m_hWndEdit, HWND_TOP,
123 rect.left, rect.top,
124 rect.right-rect.left, rect.bottom-rect.top,
125 SWP_SHOWWINDOW);
126 ::ShowWindow(m_FindBar, SW_HIDE);
129 break;
130 case WM_GETMINMAXINFO:
132 MINMAXINFO * mmi = (MINMAXINFO*)lParam;
133 mmi->ptMinTrackSize.x = 100;
134 mmi->ptMinTrackSize.y = 100;
135 return 0;
137 break;
138 case WM_DESTROY:
139 PostQuitMessage(0);
140 break;
141 case WM_CLOSE:
142 ::DestroyWindow(m_hwnd);
143 break;
144 case WM_SETFOCUS:
145 SetFocus(m_hWndEdit);
146 break;
147 case COMMITMONITOR_FINDMSGNEXT:
149 SendEditor(SCI_CHARRIGHT);
150 SendEditor(SCI_SEARCHANCHOR);
151 m_bMatchCase = !!wParam;
152 m_findtext = (LPCTSTR)lParam;
153 SendEditor(SCI_SEARCHNEXT, m_bMatchCase ? SCFIND_MATCHCASE : 0, (LPARAM)CUnicodeUtils::StdGetUTF8(m_findtext).c_str());
154 SendEditor(SCI_SCROLLCARET);
156 break;
157 case COMMITMONITOR_FINDMSGPREV:
159 SendEditor(SCI_SEARCHANCHOR);
160 m_bMatchCase = !!wParam;
161 m_findtext = (LPCTSTR)lParam;
162 SendEditor(SCI_SEARCHPREV, m_bMatchCase ? SCFIND_MATCHCASE : 0, (LPARAM)CUnicodeUtils::StdGetUTF8(m_findtext).c_str());
163 SendEditor(SCI_SCROLLCARET);
165 break;
166 case COMMITMONITOR_FINDEXIT:
168 RECT rect;
169 GetClientRect(*this, &rect);
170 m_bShowFindBar = false;
171 ::ShowWindow(m_FindBar, SW_HIDE);
172 ::SetWindowPos(m_hWndEdit, HWND_TOP,
173 rect.left, rect.top,
174 rect.right-rect.left, rect.bottom-rect.top,
175 SWP_SHOWWINDOW);
177 break;
178 case COMMITMONITOR_FINDRESET:
179 SendEditor(SCI_SETSELECTIONSTART, 0);
180 SendEditor(SCI_SETSELECTIONEND, 0);
181 SendEditor(SCI_SEARCHANCHOR);
182 break;
183 default:
184 return DefWindowProc(hwnd, uMsg, wParam, lParam);
187 return 0;
190 LRESULT CMainWindow::DoCommand(int id)
192 switch (id)
194 case ID_FILE_OPEN:
195 loadOrSaveFile(true);
196 break;
197 case ID_FILE_SAVEAS:
198 loadOrSaveFile(false);
199 break;
200 case ID_FILE_SAVE:
201 loadOrSaveFile(false, m_filename);
202 break;
203 case ID_FILE_EXIT:
204 ::PostQuitMessage(0);
205 return 0;
206 case IDM_SHOWFINDBAR:
208 m_bShowFindBar = true;
209 ::ShowWindow(m_FindBar, SW_SHOW);
210 RECT rect;
211 GetClientRect(*this, &rect);
212 ::SetWindowPos(m_hWndEdit, HWND_TOP,
213 rect.left, rect.top,
214 rect.right-rect.left, rect.bottom-rect.top-30,
215 SWP_SHOWWINDOW);
216 ::SetWindowPos(m_FindBar, HWND_TOP,
217 rect.left, rect.bottom-30,
218 rect.right-rect.left, 30,
219 SWP_SHOWWINDOW);
220 ::SetFocus(m_FindBar);
221 SendEditor(SCI_SETSELECTIONSTART, 0);
222 SendEditor(SCI_SETSELECTIONEND, 0);
223 SendEditor(SCI_SEARCHANCHOR);
225 break;
226 case IDM_FINDNEXT:
227 SendEditor(SCI_CHARRIGHT);
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 default:
259 break;
261 return 1;
264 std::wstring CMainWindow::GetAppDirectory()
266 std::wstring path;
267 DWORD len = 0;
268 DWORD bufferlen = MAX_PATH; // MAX_PATH is not the limit here!
271 bufferlen += MAX_PATH; // MAX_PATH is not the limit here!
272 std::unique_ptr<TCHAR[]> pBuf(new TCHAR[bufferlen]);
273 len = GetModuleFileName(NULL, pBuf.get(), bufferlen);
274 path = pBuf.get();
275 } while(len == bufferlen);
276 path = path.substr(0, path.rfind('\\') + 1);
278 return path;
281 void CMainWindow::RunCommand(const std::wstring& command)
283 tstring tortoiseProcPath = GetAppDirectory() + _T("TortoiseGitProc.exe");
284 CCreateProcessHelper::CreateProcessDetached(tortoiseProcPath.c_str(), const_cast<TCHAR*>(command.c_str()));
287 LRESULT CMainWindow::SendEditor(UINT Msg, WPARAM wParam, LPARAM lParam)
289 if (m_directFunction)
291 return ((SciFnDirect) m_directFunction)(m_directPointer, Msg, wParam, lParam);
293 return ::SendMessage(m_hWndEdit, Msg, wParam, lParam);
296 bool CMainWindow::Initialize()
298 m_hWndEdit = ::CreateWindow(
299 _T("Scintilla"),
300 _T("Source"),
301 WS_CHILD | WS_VSCROLL | WS_HSCROLL | WS_CLIPCHILDREN,
302 CW_USEDEFAULT, CW_USEDEFAULT,
303 CW_USEDEFAULT, CW_USEDEFAULT,
304 *this,
306 hResource,
308 if (m_hWndEdit == NULL)
309 return false;
311 RECT rect;
312 GetClientRect(*this, &rect);
313 ::SetWindowPos(m_hWndEdit, HWND_TOP,
314 rect.left, rect.top,
315 rect.right-rect.left, rect.bottom-rect.top,
316 SWP_SHOWWINDOW);
318 m_directFunction = SendMessage(m_hWndEdit, SCI_GETDIRECTFUNCTION, 0, 0);
319 m_directPointer = SendMessage(m_hWndEdit, SCI_GETDIRECTPOINTER, 0, 0);
321 // Set up the global default style. These attributes are used wherever no explicit choices are made.
322 SetAStyle(STYLE_DEFAULT, ::GetSysColor(COLOR_WINDOWTEXT), ::GetSysColor(COLOR_WINDOW),
323 // Reusing TortoiseBlame's setting which already have an user friendly
324 // pane in TortoiseSVN's Settings dialog, while there is no such
325 // pane for TortoiseUDiff.
326 CRegStdDWORD(_T("Software\\TortoiseGit\\BlameFontSize"), 10),
327 CUnicodeUtils::StdGetUTF8(CRegStdString(_T("Software\\TortoiseGit\\BlameFontName"), _T("Courier New"))).c_str());
328 SendEditor(SCI_SETTABWIDTH, 4);
329 SendEditor(SCI_SETREADONLY, TRUE);
330 LRESULT pix = SendEditor(SCI_TEXTWIDTH, STYLE_LINENUMBER, (LPARAM)"_99999");
331 SendEditor(SCI_SETMARGINWIDTHN, 0, pix);
332 SendEditor(SCI_SETMARGINWIDTHN, 1);
333 SendEditor(SCI_SETMARGINWIDTHN, 2);
334 //Set the default windows colors for edit controls
335 SendEditor(SCI_STYLESETFORE, STYLE_DEFAULT, ::GetSysColor(COLOR_WINDOWTEXT));
336 SendEditor(SCI_STYLESETBACK, STYLE_DEFAULT, ::GetSysColor(COLOR_WINDOW));
337 SendEditor(SCI_SETSELFORE, TRUE, ::GetSysColor(COLOR_HIGHLIGHTTEXT));
338 SendEditor(SCI_SETSELBACK, TRUE, ::GetSysColor(COLOR_HIGHLIGHT));
339 SendEditor(SCI_SETCARETFORE, ::GetSysColor(COLOR_WINDOWTEXT));
340 CRegStdDWORD used2d(L"Software\\TortoiseGit\\ScintillaDirect2D", FALSE);
341 if (SysInfo::Instance().IsWin7OrLater() && DWORD(used2d))
343 SendEditor(SCI_SETTECHNOLOGY, SC_TECHNOLOGY_DIRECTWRITE);
344 SendEditor(SCI_SETBUFFEREDDRAW, 0);
346 SendEditor(SCI_SETFONTQUALITY, SC_EFF_QUALITY_LCD_OPTIMIZED);
348 return true;
351 bool CMainWindow::LoadFile(HANDLE hFile)
353 InitEditor();
354 char data[4096];
355 DWORD dwRead = 0;
357 BOOL bRet = ReadFile(hFile, data, sizeof(data), &dwRead, NULL);
358 bool bUTF8 = IsUTF8(data, dwRead);
359 while ((dwRead > 0) && (bRet))
361 SendEditor(SCI_ADDTEXT, dwRead,
362 reinterpret_cast<LPARAM>(static_cast<char *>(data)));
363 bRet = ReadFile(hFile, data, sizeof(data), &dwRead, NULL);
365 SetupWindow(bUTF8);
366 return true;
369 bool CMainWindow::LoadFile(LPCTSTR filename)
371 InitEditor();
372 FILE *fp = NULL;
373 _tfopen_s(&fp, filename, _T("rb"));
374 if (!fp)
375 return false;
377 //SetTitle();
378 char data[4096];
379 size_t lenFile = fread(data, 1, sizeof(data), fp);
380 bool bUTF8 = IsUTF8(data, lenFile);
381 while (lenFile > 0)
383 SendEditor(SCI_ADDTEXT, lenFile,
384 reinterpret_cast<LPARAM>(static_cast<char *>(data)));
385 lenFile = fread(data, 1, sizeof(data), fp);
387 fclose(fp);
388 SetupWindow(bUTF8);
389 m_filename = filename;
390 return true;
393 void CMainWindow::InitEditor()
395 SendEditor(SCI_SETREADONLY, FALSE);
396 SendEditor(SCI_CLEARALL);
397 SendEditor(EM_EMPTYUNDOBUFFER);
398 SendEditor(SCI_SETSAVEPOINT);
399 SendEditor(SCI_CANCEL);
400 SendEditor(SCI_SETUNDOCOLLECTION, 0);
403 void CMainWindow::SetupWindow(bool bUTF8)
405 SendEditor(SCI_SETCODEPAGE, bUTF8 ? SC_CP_UTF8 : GetACP());
407 SendEditor(SCI_SETUNDOCOLLECTION, 1);
408 ::SetFocus(m_hWndEdit);
409 SendEditor(EM_EMPTYUNDOBUFFER);
410 SendEditor(SCI_SETSAVEPOINT);
411 SendEditor(SCI_GOTOPOS, 0);
413 SendEditor(SCI_CLEARDOCUMENTSTYLE, 0, 0);
414 SendEditor(SCI_SETSTYLEBITS, 5, 0);
416 //SetAStyle(SCE_DIFF_DEFAULT, RGB(0, 0, 0));
417 SetAStyle(SCE_DIFF_COMMAND, RGB(0x0A, 0x24, 0x36));
418 SetAStyle(SCE_DIFF_POSITION, RGB(0xFF, 0, 0));
419 SetAStyle(SCE_DIFF_HEADER, RGB(0x80, 0, 0), RGB(0xFF, 0xFF, 0x80));
420 SetAStyle(SCE_DIFF_COMMENT, RGB(0, 0x80, 0));
421 SendEditor(SCI_STYLESETBOLD, SCE_DIFF_COMMENT, TRUE);
422 SetAStyle(SCE_DIFF_DELETED, ::GetSysColor(COLOR_WINDOWTEXT), RGB(0xFF, 0x80, 0x80));
423 SetAStyle(SCE_DIFF_ADDED, ::GetSysColor(COLOR_WINDOWTEXT), RGB(0x80, 0xFF, 0x80));
425 SendEditor(SCI_SETLEXER, SCLEX_DIFF);
426 SendEditor(SCI_SETKEYWORDS, 0, (LPARAM)"revision");
427 SendEditor(SCI_COLOURISE, 0, -1);
428 ::ShowWindow(m_hWndEdit, SW_SHOW);
431 bool CMainWindow::SaveFile(LPCTSTR filename)
433 FILE *fp = NULL;
434 _tfopen_s(&fp, filename, _T("w+b"));
435 if (!fp)
436 return false;
438 LRESULT len = SendEditor(SCI_GETTEXT, 0, 0);
439 std::unique_ptr<char[]> data (new char[len+1]);
440 SendEditor(SCI_GETTEXT, len, reinterpret_cast<LPARAM>(static_cast<char *>(data.get())));
441 fwrite(data.get(), sizeof(char), len-1, fp);
442 fclose(fp);
444 SendEditor(SCI_SETSAVEPOINT);
445 ::ShowWindow(m_hWndEdit, SW_SHOW);
446 return true;
449 void CMainWindow::SetTitle(LPCTSTR title)
451 size_t len = _tcslen(title);
452 std::unique_ptr<TCHAR[]> pBuf(new TCHAR[len + 40]);
453 _stprintf_s(pBuf.get(), len + 40, _T("%s - TortoiseGitUDiff"), title);
454 SetWindowTitle(std::wstring(pBuf.get()));
457 void CMainWindow::SetAStyle(int style, COLORREF fore, COLORREF back, int size, const char *face)
459 SendEditor(SCI_STYLESETFORE, style, fore);
460 SendEditor(SCI_STYLESETBACK, style, back);
461 if (size >= 1)
462 SendEditor(SCI_STYLESETSIZE, style, size);
463 if (face)
464 SendEditor(SCI_STYLESETFONT, style, reinterpret_cast<LPARAM>(face));
467 bool CMainWindow::IsUTF8(LPVOID pBuffer, size_t cb)
469 if (cb < 2)
470 return true;
471 UINT16 * pVal16 = (UINT16 *)pBuffer;
472 UINT8 * pVal8 = (UINT8 *)(pVal16+1);
473 // scan the whole buffer for a 0x0000 sequence
474 // if found, we assume a binary file
475 for (size_t i=0; i<(cb-2); i=i+2)
477 if (0x0000 == *pVal16++)
478 return false;
480 pVal16 = (UINT16 *)pBuffer;
481 if (*pVal16 == 0xFEFF)
482 return false;
483 if (cb < 3)
484 return false;
485 if (*pVal16 == 0xBBEF)
487 if (*pVal8 == 0xBF)
488 return true;
490 // check for illegal UTF8 chars
491 pVal8 = (UINT8 *)pBuffer;
492 for (size_t i=0; i<cb; ++i)
494 if ((*pVal8 == 0xC0)||(*pVal8 == 0xC1)||(*pVal8 >= 0xF5))
495 return false;
496 pVal8++;
498 pVal8 = (UINT8 *)pBuffer;
499 bool bUTF8 = false;
500 for (size_t i=0; i<(cb-3); ++i)
502 if ((*pVal8 & 0xE0)==0xC0)
504 pVal8++;i++;
505 if ((*pVal8 & 0xC0)!=0x80)
506 return false;
507 bUTF8 = true;
509 if ((*pVal8 & 0xF0)==0xE0)
511 pVal8++;i++;
512 if ((*pVal8 & 0xC0)!=0x80)
513 return false;
514 pVal8++;i++;
515 if ((*pVal8 & 0xC0)!=0x80)
516 return false;
517 bUTF8 = true;
519 if ((*pVal8 & 0xF8)==0xF0)
521 pVal8++;i++;
522 if ((*pVal8 & 0xC0)!=0x80)
523 return false;
524 pVal8++;i++;
525 if ((*pVal8 & 0xC0)!=0x80)
526 return false;
527 pVal8++;i++;
528 if ((*pVal8 & 0xC0)!=0x80)
529 return false;
530 bUTF8 = true;
532 pVal8++;
534 if (bUTF8)
535 return true;
536 return false;
539 void CMainWindow::loadOrSaveFile(bool doLoad, const wstring& filename /* = L"" */)
541 OPENFILENAME ofn = {0}; // common dialog box structure
542 TCHAR szFile[MAX_PATH] = {0}; // buffer for file name
543 // Initialize OPENFILENAME
544 ofn.lStructSize = sizeof(OPENFILENAME);
545 ofn.hwndOwner = *this;
546 ofn.lpstrFile = szFile;
547 ofn.nMaxFile = sizeof(szFile)/sizeof(TCHAR);
548 TCHAR filter[1024];
549 LoadString(hResource, IDS_PATCHFILEFILTER, filter, sizeof(filter)/sizeof(TCHAR));
550 CStringUtils::PipesToNulls(filter);
551 ofn.lpstrFilter = filter;
552 ofn.nFilterIndex = 1;
553 ofn.lpstrFileTitle = NULL;
554 ofn.nMaxFileTitle = 0;
555 ofn.lpstrInitialDir = NULL;
556 TCHAR fileTitle[1024];
557 LoadString(hResource, doLoad ? IDS_OPENPATCH : IDS_SAVEPATCH, fileTitle, sizeof(fileTitle)/sizeof(TCHAR));
558 ofn.lpstrTitle = fileTitle;
559 ofn.Flags = OFN_ENABLESIZING | OFN_EXPLORER;
560 if(doLoad)
561 ofn.Flags |= OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
562 else
563 ofn.Flags |= OFN_OVERWRITEPROMPT;
564 // Display the Open dialog box.
565 if( doLoad )
567 if (GetOpenFileName(&ofn)==TRUE)
569 LoadFile(ofn.lpstrFile);
572 else
574 if (filename.empty())
576 if (GetSaveFileName(&ofn)==TRUE)
578 SaveFile(ofn.lpstrFile);
581 else
582 SaveFile(filename.c_str());