Fixed string order
[TortoiseGit.git] / src / TortoiseUDiff / MainWindow.cpp
blobdfea1e47e93ae7302cd2c42dac81c6322e27982d
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2003-2012 - 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"
23 #include "StringUtils.h"
24 #include "TaskbarUUID.h"
25 #include "CreateProcessHelper.h"
27 const UINT TaskBarButtonCreated = RegisterWindowMessage(L"TaskbarButtonCreated");
29 CMainWindow::CMainWindow(HINSTANCE hInst, const WNDCLASSEX* wcx /* = NULL*/)
30 : CWindow(hInst, wcx)
31 , m_bShowFindBar(false)
33 SetWindowTitle(_T("TortoiseUDiff"));
36 CMainWindow::~CMainWindow(void)
40 bool CMainWindow::RegisterAndCreateWindow()
42 WNDCLASSEX wcx;
44 // Fill in the window class structure with default parameters
45 wcx.cbSize = sizeof(WNDCLASSEX);
46 wcx.style = CS_HREDRAW | CS_VREDRAW;
47 wcx.lpfnWndProc = CWindow::stWinMsgHandler;
48 wcx.cbClsExtra = 0;
49 wcx.cbWndExtra = 0;
50 wcx.hInstance = hResource;
51 wcx.hCursor = NULL;
52 wcx.lpszClassName = ResString(hResource, IDS_APP_TITLE);
53 wcx.hIcon = LoadIcon(hResource, MAKEINTRESOURCE(IDI_TORTOISEUDIFF));
54 wcx.hbrBackground = (HBRUSH)(COLOR_3DFACE+1);
55 wcx.lpszMenuName = MAKEINTRESOURCE(IDC_TORTOISEUDIFF);
56 wcx.hIconSm = LoadIcon(wcx.hInstance, MAKEINTRESOURCE(IDI_TORTOISEUDIFF));
57 if (RegisterWindow(&wcx))
59 if (Create(WS_CAPTION | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_SIZEBOX | WS_SYSMENU | WS_CLIPCHILDREN, NULL))
61 m_FindBar.SetParent(*this);
62 m_FindBar.Create(hResource, IDD_FINDBAR, *this);
63 UpdateWindow(*this);
64 return true;
67 return false;
70 LRESULT CALLBACK CMainWindow::WinMsgHandler(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
72 if (uMsg == TaskBarButtonCreated)
74 SetUUIDOverlayIcon(hwnd);
76 switch (uMsg)
78 case WM_CREATE:
80 m_hwnd = hwnd;
81 Initialize();
83 break;
84 case WM_COMMAND:
86 return DoCommand(LOWORD(wParam));
88 break;
89 case WM_MOUSEWHEEL:
91 if (GET_KEYSTATE_WPARAM(wParam) == MK_SHIFT)
93 // scroll sideways
94 SendEditor(SCI_LINESCROLL, -GET_WHEEL_DELTA_WPARAM(wParam)/40, 0);
96 else
97 return DefWindowProc(hwnd, uMsg, wParam, lParam);
99 break;
100 case WM_SIZE:
102 RECT rect;
103 GetClientRect(*this, &rect);
104 if (m_bShowFindBar)
106 ::SetWindowPos(m_hWndEdit, HWND_TOP,
107 rect.left, rect.top,
108 rect.right-rect.left, rect.bottom-rect.top-30,
109 SWP_SHOWWINDOW);
110 ::SetWindowPos(m_FindBar, HWND_TOP,
111 rect.left, rect.bottom-30,
112 rect.right-rect.left, 30,
113 SWP_SHOWWINDOW);
115 else
117 ::SetWindowPos(m_hWndEdit, HWND_TOP,
118 rect.left, rect.top,
119 rect.right-rect.left, rect.bottom-rect.top,
120 SWP_SHOWWINDOW);
121 ::ShowWindow(m_FindBar, SW_HIDE);
124 break;
125 case WM_GETMINMAXINFO:
127 MINMAXINFO * mmi = (MINMAXINFO*)lParam;
128 mmi->ptMinTrackSize.x = 100;
129 mmi->ptMinTrackSize.y = 100;
130 return 0;
132 break;
133 case WM_DESTROY:
134 PostQuitMessage(0);
135 break;
136 case WM_CLOSE:
137 ::DestroyWindow(m_hwnd);
138 break;
139 case WM_SETFOCUS:
140 SetFocus(m_hWndEdit);
141 break;
142 case COMMITMONITOR_FINDMSGNEXT:
144 SendEditor(SCI_CHARRIGHT);
145 SendEditor(SCI_SEARCHANCHOR);
146 m_bMatchCase = !!wParam;
147 m_findtext = (LPCTSTR)lParam;
148 SendEditor(SCI_SEARCHNEXT, m_bMatchCase ? SCFIND_MATCHCASE : 0, (LPARAM)CUnicodeUtils::StdGetUTF8(m_findtext).c_str());
149 SendEditor(SCI_SCROLLCARET);
151 break;
152 case COMMITMONITOR_FINDMSGPREV:
154 SendEditor(SCI_SEARCHANCHOR);
155 m_bMatchCase = !!wParam;
156 m_findtext = (LPCTSTR)lParam;
157 SendEditor(SCI_SEARCHPREV, m_bMatchCase ? SCFIND_MATCHCASE : 0, (LPARAM)CUnicodeUtils::StdGetUTF8(m_findtext).c_str());
158 SendEditor(SCI_SCROLLCARET);
160 break;
161 case COMMITMONITOR_FINDEXIT:
163 RECT rect;
164 GetClientRect(*this, &rect);
165 m_bShowFindBar = false;
166 ::ShowWindow(m_FindBar, SW_HIDE);
167 ::SetWindowPos(m_hWndEdit, HWND_TOP,
168 rect.left, rect.top,
169 rect.right-rect.left, rect.bottom-rect.top,
170 SWP_SHOWWINDOW);
172 break;
173 case COMMITMONITOR_FINDRESET:
174 SendEditor(SCI_SETSELECTIONSTART, 0);
175 SendEditor(SCI_SETSELECTIONEND, 0);
176 SendEditor(SCI_SEARCHANCHOR);
177 break;
178 default:
179 return DefWindowProc(hwnd, uMsg, wParam, lParam);
182 return 0;
185 LRESULT CMainWindow::DoCommand(int id)
187 switch (id)
189 case ID_FILE_OPEN:
190 loadOrSaveFile(true);
191 break;
192 case ID_FILE_SAVEAS:
193 loadOrSaveFile(false);
194 break;
195 case ID_FILE_SAVE:
196 loadOrSaveFile(false, m_filename);
197 break;
198 case ID_FILE_EXIT:
199 ::PostQuitMessage(0);
200 return 0;
201 case IDM_SHOWFINDBAR:
203 m_bShowFindBar = true;
204 ::ShowWindow(m_FindBar, SW_SHOW);
205 RECT rect;
206 GetClientRect(*this, &rect);
207 ::SetWindowPos(m_hWndEdit, HWND_TOP,
208 rect.left, rect.top,
209 rect.right-rect.left, rect.bottom-rect.top-30,
210 SWP_SHOWWINDOW);
211 ::SetWindowPos(m_FindBar, HWND_TOP,
212 rect.left, rect.bottom-30,
213 rect.right-rect.left, 30,
214 SWP_SHOWWINDOW);
215 ::SetFocus(m_FindBar);
216 SendEditor(SCI_SETSELECTIONSTART, 0);
217 SendEditor(SCI_SETSELECTIONEND, 0);
218 SendEditor(SCI_SEARCHANCHOR);
220 break;
221 case IDM_FINDNEXT:
222 SendEditor(SCI_CHARRIGHT);
223 SendEditor(SCI_SEARCHANCHOR);
224 SendEditor(SCI_SEARCHNEXT, m_bMatchCase ? SCFIND_MATCHCASE : 0, (LPARAM)CUnicodeUtils::StdGetUTF8(m_findtext).c_str());
225 SendEditor(SCI_SCROLLCARET);
226 break;
227 case IDM_FINDPREV:
228 SendEditor(SCI_SEARCHANCHOR);
229 SendEditor(SCI_SEARCHPREV, m_bMatchCase ? SCFIND_MATCHCASE : 0, (LPARAM)CUnicodeUtils::StdGetUTF8(m_findtext).c_str());
230 SendEditor(SCI_SCROLLCARET);
231 break;
232 case IDM_FINDEXIT:
233 if (IsWindowVisible(m_FindBar))
235 RECT rect;
236 GetClientRect(*this, &rect);
237 m_bShowFindBar = false;
238 ::ShowWindow(m_FindBar, SW_HIDE);
239 ::SetWindowPos(m_hWndEdit, HWND_TOP,
240 rect.left, rect.top,
241 rect.right-rect.left, rect.bottom-rect.top,
242 SWP_SHOWWINDOW);
244 else
245 PostQuitMessage(0);
246 break;
247 case ID_FILE_SETTINGS:
249 tstring gitCmd = _T(" /command:settings /page:blame");
250 RunCommand(gitCmd);
252 break;
253 default:
254 break;
256 return 1;
259 std::wstring CMainWindow::GetAppDirectory()
261 std::wstring path;
262 DWORD len = 0;
263 DWORD bufferlen = MAX_PATH; // MAX_PATH is not the limit here!
266 bufferlen += MAX_PATH; // MAX_PATH is not the limit here!
267 TCHAR * pBuf = new TCHAR[bufferlen];
268 len = GetModuleFileName(NULL, pBuf, bufferlen);
269 path = pBuf;
270 delete [] pBuf;
271 } while(len == bufferlen);
273 path = path.substr(0, path.rfind('\\') + 1);
274 return path;
277 void CMainWindow::RunCommand(const std::wstring& command)
279 tstring tortoiseProcPath = GetAppDirectory() + _T("TortoiseProc.exe");
280 CCreateProcessHelper::CreateProcessDetached(tortoiseProcPath.c_str(), const_cast<TCHAR*>(command.c_str()));
283 LRESULT CMainWindow::SendEditor(UINT Msg, WPARAM wParam, LPARAM lParam)
285 if (m_directFunction)
287 return ((SciFnDirect) m_directFunction)(m_directPointer, Msg, wParam, lParam);
289 return ::SendMessage(m_hWndEdit, Msg, wParam, lParam);
292 bool CMainWindow::Initialize()
294 m_hWndEdit = ::CreateWindow(
295 _T("Scintilla"),
296 _T("Source"),
297 WS_CHILD | WS_VSCROLL | WS_HSCROLL | WS_CLIPCHILDREN,
298 CW_USEDEFAULT, CW_USEDEFAULT,
299 CW_USEDEFAULT, CW_USEDEFAULT,
300 *this,
302 hResource,
304 if (m_hWndEdit == NULL)
305 return false;
307 RECT rect;
308 GetClientRect(*this, &rect);
309 ::SetWindowPos(m_hWndEdit, HWND_TOP,
310 rect.left, rect.top,
311 rect.right-rect.left, rect.bottom-rect.top,
312 SWP_SHOWWINDOW);
314 m_directFunction = SendMessage(m_hWndEdit, SCI_GETDIRECTFUNCTION, 0, 0);
315 m_directPointer = SendMessage(m_hWndEdit, SCI_GETDIRECTPOINTER, 0, 0);
317 // Set up the global default style. These attributes are used wherever no explicit choices are made.
318 SetAStyle(STYLE_DEFAULT, ::GetSysColor(COLOR_WINDOWTEXT), ::GetSysColor(COLOR_WINDOW),
319 // Reusing TortoiseBlame's setting which already have an user friendly
320 // pane in TortoiseSVN's Settings dialog, while there is no such
321 // pane for TortoiseUDiff.
322 CRegStdDWORD(_T("Software\\TortoiseGit\\BlameFontSize"), 10),
323 CUnicodeUtils::StdGetUTF8(CRegStdString(_T("Software\\TortoiseGit\\BlameFontName"), _T("Courier New"))).c_str());
324 SendEditor(SCI_SETTABWIDTH, 4);
325 SendEditor(SCI_SETREADONLY, TRUE);
326 LRESULT pix = SendEditor(SCI_TEXTWIDTH, STYLE_LINENUMBER, (LPARAM)"_99999");
327 SendEditor(SCI_SETMARGINWIDTHN, 0, pix);
328 SendEditor(SCI_SETMARGINWIDTHN, 1);
329 SendEditor(SCI_SETMARGINWIDTHN, 2);
330 //Set the default windows colors for edit controls
331 SendEditor(SCI_STYLESETFORE, STYLE_DEFAULT, ::GetSysColor(COLOR_WINDOWTEXT));
332 SendEditor(SCI_STYLESETBACK, STYLE_DEFAULT, ::GetSysColor(COLOR_WINDOW));
333 SendEditor(SCI_SETSELFORE, TRUE, ::GetSysColor(COLOR_HIGHLIGHTTEXT));
334 SendEditor(SCI_SETSELBACK, TRUE, ::GetSysColor(COLOR_HIGHLIGHT));
335 SendEditor(SCI_SETCARETFORE, ::GetSysColor(COLOR_WINDOWTEXT));
336 CRegStdDWORD used2d(L"Software\\TortoiseGit\\ScintillaDirect2D", FALSE);
337 if (SysInfo::Instance().IsWin7OrLater() && DWORD(used2d))
339 SendEditor(SCI_SETTECHNOLOGY, SC_TECHNOLOGY_DIRECTWRITE);
340 SendEditor(SCI_SETBUFFEREDDRAW, 0);
342 SendEditor(SCI_SETFONTQUALITY, SC_EFF_QUALITY_DEFAULT);
344 return true;
347 bool CMainWindow::LoadFile(HANDLE hFile)
349 InitEditor();
350 char data[4096];
351 DWORD dwRead = 0;
353 BOOL bRet = ReadFile(hFile, data, sizeof(data), &dwRead, NULL);
354 bool bUTF8 = IsUTF8(data, dwRead);
355 while ((dwRead > 0) && (bRet))
357 SendEditor(SCI_ADDTEXT, dwRead,
358 reinterpret_cast<LPARAM>(static_cast<char *>(data)));
359 bRet = ReadFile(hFile, data, sizeof(data), &dwRead, NULL);
361 SetupWindow(bUTF8);
362 return true;
365 bool CMainWindow::LoadFile(LPCTSTR filename)
367 InitEditor();
368 FILE *fp = NULL;
369 _tfopen_s(&fp, filename, _T("rb"));
370 if (!fp)
371 return false;
373 //SetTitle();
374 char data[4096];
375 size_t lenFile = fread(data, 1, sizeof(data), fp);
376 bool bUTF8 = IsUTF8(data, lenFile);
377 while (lenFile > 0)
379 SendEditor(SCI_ADDTEXT, lenFile,
380 reinterpret_cast<LPARAM>(static_cast<char *>(data)));
381 lenFile = fread(data, 1, sizeof(data), fp);
383 fclose(fp);
384 SetupWindow(bUTF8);
385 m_filename = filename;
386 return true;
389 void CMainWindow::InitEditor()
391 SendEditor(SCI_SETREADONLY, FALSE);
392 SendEditor(SCI_CLEARALL);
393 SendEditor(EM_EMPTYUNDOBUFFER);
394 SendEditor(SCI_SETSAVEPOINT);
395 SendEditor(SCI_CANCEL);
396 SendEditor(SCI_SETUNDOCOLLECTION, 0);
399 void CMainWindow::SetupWindow(bool bUTF8)
401 SendEditor(SCI_SETCODEPAGE, bUTF8 ? SC_CP_UTF8 : GetACP());
403 SendEditor(SCI_SETUNDOCOLLECTION, 1);
404 ::SetFocus(m_hWndEdit);
405 SendEditor(EM_EMPTYUNDOBUFFER);
406 SendEditor(SCI_SETSAVEPOINT);
407 SendEditor(SCI_GOTOPOS, 0);
409 SendEditor(SCI_CLEARDOCUMENTSTYLE, 0, 0);
410 SendEditor(SCI_SETSTYLEBITS, 5, 0);
412 //SetAStyle(SCE_DIFF_DEFAULT, RGB(0, 0, 0));
413 SetAStyle(SCE_DIFF_COMMAND, RGB(0x0A, 0x24, 0x36));
414 SetAStyle(SCE_DIFF_POSITION, RGB(0xFF, 0, 0));
415 SetAStyle(SCE_DIFF_HEADER, RGB(0x80, 0, 0), RGB(0xFF, 0xFF, 0x80));
416 SetAStyle(SCE_DIFF_COMMENT, RGB(0, 0x80, 0));
417 SendEditor(SCI_STYLESETBOLD, SCE_DIFF_COMMENT, TRUE);
418 SetAStyle(SCE_DIFF_DELETED, ::GetSysColor(COLOR_WINDOWTEXT), RGB(0xFF, 0x80, 0x80));
419 SetAStyle(SCE_DIFF_ADDED, ::GetSysColor(COLOR_WINDOWTEXT), RGB(0x80, 0xFF, 0x80));
421 SendEditor(SCI_SETLEXER, SCLEX_DIFF);
422 SendEditor(SCI_SETKEYWORDS, 0, (LPARAM)"revision");
423 SendEditor(SCI_COLOURISE, 0, -1);
424 ::ShowWindow(m_hWndEdit, SW_SHOW);
427 bool CMainWindow::SaveFile(LPCTSTR filename)
429 FILE *fp = NULL;
430 _tfopen_s(&fp, filename, _T("w+b"));
431 if (!fp)
432 return false;
434 int len = SendEditor(SCI_GETTEXT, 0, 0);
435 char * data = new char[len+1];
436 SendEditor(SCI_GETTEXT, len, (LPARAM)data);
437 fwrite(data, sizeof(char), len-1, fp);
438 fclose(fp);
440 SendEditor(SCI_SETSAVEPOINT);
441 ::ShowWindow(m_hWndEdit, SW_SHOW);
442 return true;
445 void CMainWindow::SetTitle(LPCTSTR title)
447 size_t len = _tcslen(title);
448 TCHAR * pBuf = new TCHAR[len+40];
449 _stprintf_s(pBuf, len+40, _T("%s - TortoiseUDiff"), title);
450 SetWindowTitle(std::wstring(pBuf));
451 delete [] pBuf;
454 void CMainWindow::SetAStyle(int style, COLORREF fore, COLORREF back, int size, const char *face)
456 SendEditor(SCI_STYLESETFORE, style, fore);
457 SendEditor(SCI_STYLESETBACK, style, back);
458 if (size >= 1)
459 SendEditor(SCI_STYLESETSIZE, style, size);
460 if (face)
461 SendEditor(SCI_STYLESETFONT, style, reinterpret_cast<LPARAM>(face));
464 bool CMainWindow::IsUTF8(LPVOID pBuffer, size_t cb)
466 if (cb < 2)
467 return true;
468 UINT16 * pVal16 = (UINT16 *)pBuffer;
469 UINT8 * pVal8 = (UINT8 *)(pVal16+1);
470 // scan the whole buffer for a 0x0000 sequence
471 // if found, we assume a binary file
472 for (size_t i=0; i<(cb-2); i=i+2)
474 if (0x0000 == *pVal16++)
475 return false;
477 pVal16 = (UINT16 *)pBuffer;
478 if (*pVal16 == 0xFEFF)
479 return false;
480 if (cb < 3)
481 return false;
482 if (*pVal16 == 0xBBEF)
484 if (*pVal8 == 0xBF)
485 return true;
487 // check for illegal UTF8 chars
488 pVal8 = (UINT8 *)pBuffer;
489 for (size_t i=0; i<cb; ++i)
491 if ((*pVal8 == 0xC0)||(*pVal8 == 0xC1)||(*pVal8 >= 0xF5))
492 return false;
493 pVal8++;
495 pVal8 = (UINT8 *)pBuffer;
496 bool bUTF8 = false;
497 for (size_t i=0; i<(cb-3); ++i)
499 if ((*pVal8 & 0xE0)==0xC0)
501 pVal8++;i++;
502 if ((*pVal8 & 0xC0)!=0x80)
503 return false;
504 bUTF8 = true;
506 if ((*pVal8 & 0xF0)==0xE0)
508 pVal8++;i++;
509 if ((*pVal8 & 0xC0)!=0x80)
510 return false;
511 pVal8++;i++;
512 if ((*pVal8 & 0xC0)!=0x80)
513 return false;
514 bUTF8 = true;
516 if ((*pVal8 & 0xF8)==0xF0)
518 pVal8++;i++;
519 if ((*pVal8 & 0xC0)!=0x80)
520 return false;
521 pVal8++;i++;
522 if ((*pVal8 & 0xC0)!=0x80)
523 return false;
524 pVal8++;i++;
525 if ((*pVal8 & 0xC0)!=0x80)
526 return false;
527 bUTF8 = true;
529 pVal8++;
531 if (bUTF8)
532 return true;
533 return false;
536 void CMainWindow::loadOrSaveFile(bool doLoad, const wstring& filename /* = L"" */)
538 OPENFILENAME ofn = {0}; // common dialog box structure
539 TCHAR szFile[MAX_PATH] = {0}; // buffer for file name
540 // Initialize OPENFILENAME
541 ofn.lStructSize = sizeof(OPENFILENAME);
542 ofn.hwndOwner = *this;
543 ofn.lpstrFile = szFile;
544 ofn.nMaxFile = sizeof(szFile)/sizeof(TCHAR);
545 TCHAR filter[1024];
546 LoadString(hResource, IDS_PATCHFILEFILTER, filter, sizeof(filter)/sizeof(TCHAR));
547 CStringUtils::PipesToNulls(filter);
548 ofn.lpstrFilter = filter;
549 ofn.nFilterIndex = 1;
550 ofn.lpstrFileTitle = NULL;
551 ofn.nMaxFileTitle = 0;
552 ofn.lpstrInitialDir = NULL;
553 TCHAR fileTitle[1024];
554 LoadString(hResource, doLoad ? IDS_OPENPATCH : IDS_SAVEPATCH, fileTitle, sizeof(fileTitle)/sizeof(TCHAR));
555 ofn.lpstrTitle = fileTitle;
556 ofn.Flags = OFN_ENABLESIZING | OFN_EXPLORER;
557 if(doLoad)
558 ofn.Flags |= OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
559 else
560 ofn.Flags |= OFN_OVERWRITEPROMPT;
561 // Display the Open dialog box.
562 if( doLoad )
564 if (GetOpenFileName(&ofn)==TRUE)
566 LoadFile(ofn.lpstrFile);
569 else
571 if (filename.empty())
573 if (GetSaveFileName(&ofn)==TRUE)
575 SaveFile(ofn.lpstrFile);
578 else
579 SaveFile(filename.c_str());