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.
21 #include "MainWindow.h"
22 #include "UnicodeUtils.h"
23 #include "StringUtils.h"
24 #include "TaskbarUUID.h"
25 #include "CreateProcessHelper.h"
28 const UINT TaskBarButtonCreated
= RegisterWindowMessage(L
"TaskbarButtonCreated");
30 CMainWindow::CMainWindow(HINSTANCE hInst
, const WNDCLASSEX
* wcx
/* = NULL*/)
32 , m_bShowFindBar(false)
38 SetWindowTitle(_T("TortoiseGitUDiff"));
41 CMainWindow::~CMainWindow(void)
45 bool CMainWindow::RegisterAndCreateWindow()
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
;
55 wcx
.hInstance
= hResource
;
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);
75 LRESULT CALLBACK
CMainWindow::WinMsgHandler(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
77 if (uMsg
== TaskBarButtonCreated
)
79 SetUUIDOverlayIcon(hwnd
);
91 return DoCommand(LOWORD(wParam
));
96 if (GET_KEYSTATE_WPARAM(wParam
) == MK_SHIFT
)
99 SendEditor(SCI_LINESCROLL
, -GET_WHEEL_DELTA_WPARAM(wParam
)/40, 0);
102 return DefWindowProc(hwnd
, uMsg
, wParam
, lParam
);
108 GetClientRect(*this, &rect
);
111 ::SetWindowPos(m_hWndEdit
, HWND_TOP
,
113 rect
.right
-rect
.left
, rect
.bottom
-rect
.top
-30,
115 ::SetWindowPos(m_FindBar
, HWND_TOP
,
116 rect
.left
, rect
.bottom
-30,
117 rect
.right
-rect
.left
, 30,
122 ::SetWindowPos(m_hWndEdit
, HWND_TOP
,
124 rect
.right
-rect
.left
, rect
.bottom
-rect
.top
,
126 ::ShowWindow(m_FindBar
, SW_HIDE
);
130 case WM_GETMINMAXINFO
:
132 MINMAXINFO
* mmi
= (MINMAXINFO
*)lParam
;
133 mmi
->ptMinTrackSize
.x
= 100;
134 mmi
->ptMinTrackSize
.y
= 100;
142 ::DestroyWindow(m_hwnd
);
145 SetFocus(m_hWndEdit
);
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
);
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
);
166 case COMMITMONITOR_FINDEXIT
:
169 GetClientRect(*this, &rect
);
170 m_bShowFindBar
= false;
171 ::ShowWindow(m_FindBar
, SW_HIDE
);
172 ::SetWindowPos(m_hWndEdit
, HWND_TOP
,
174 rect
.right
-rect
.left
, rect
.bottom
-rect
.top
,
178 case COMMITMONITOR_FINDRESET
:
179 SendEditor(SCI_SETSELECTIONSTART
, 0);
180 SendEditor(SCI_SETSELECTIONEND
, 0);
181 SendEditor(SCI_SEARCHANCHOR
);
184 return DefWindowProc(hwnd
, uMsg
, wParam
, lParam
);
190 LRESULT
CMainWindow::DoCommand(int id
)
195 loadOrSaveFile(true);
198 loadOrSaveFile(false);
201 loadOrSaveFile(false, m_filename
);
204 ::PostQuitMessage(0);
206 case IDM_SHOWFINDBAR
:
208 m_bShowFindBar
= true;
209 ::ShowWindow(m_FindBar
, SW_SHOW
);
211 GetClientRect(*this, &rect
);
212 ::SetWindowPos(m_hWndEdit
, HWND_TOP
,
214 rect
.right
-rect
.left
, rect
.bottom
-rect
.top
-30,
216 ::SetWindowPos(m_FindBar
, HWND_TOP
,
217 rect
.left
, rect
.bottom
-30,
218 rect
.right
-rect
.left
, 30,
220 ::SetFocus(m_FindBar
);
221 SendEditor(SCI_SETSELECTIONSTART
, 0);
222 SendEditor(SCI_SETSELECTIONEND
, 0);
223 SendEditor(SCI_SEARCHANCHOR
);
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
);
233 SendEditor(SCI_SEARCHANCHOR
);
234 SendEditor(SCI_SEARCHPREV
, m_bMatchCase
? SCFIND_MATCHCASE
: 0, (LPARAM
)CUnicodeUtils::StdGetUTF8(m_findtext
).c_str());
235 SendEditor(SCI_SCROLLCARET
);
238 if (IsWindowVisible(m_FindBar
))
241 GetClientRect(*this, &rect
);
242 m_bShowFindBar
= false;
243 ::ShowWindow(m_FindBar
, SW_HIDE
);
244 ::SetWindowPos(m_hWndEdit
, HWND_TOP
,
246 rect
.right
-rect
.left
, rect
.bottom
-rect
.top
,
252 case ID_FILE_SETTINGS
:
254 tstring gitCmd
= _T(" /command:settings /page:blame");
264 std::wstring
CMainWindow::GetAppDirectory()
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
);
275 } while(len
== bufferlen
);
276 path
= path
.substr(0, path
.rfind('\\') + 1);
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(
301 WS_CHILD
| WS_VSCROLL
| WS_HSCROLL
| WS_CLIPCHILDREN
,
302 CW_USEDEFAULT
, CW_USEDEFAULT
,
303 CW_USEDEFAULT
, CW_USEDEFAULT
,
308 if (m_hWndEdit
== NULL
)
312 GetClientRect(*this, &rect
);
313 ::SetWindowPos(m_hWndEdit
, HWND_TOP
,
315 rect
.right
-rect
.left
, rect
.bottom
-rect
.top
,
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
);
351 bool CMainWindow::LoadFile(HANDLE hFile
)
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
);
369 bool CMainWindow::LoadFile(LPCTSTR filename
)
373 _tfopen_s(&fp
, filename
, _T("rb"));
379 size_t lenFile
= fread(data
, 1, sizeof(data
), fp
);
380 bool bUTF8
= IsUTF8(data
, lenFile
);
383 SendEditor(SCI_ADDTEXT
, lenFile
,
384 reinterpret_cast<LPARAM
>(static_cast<char *>(data
)));
385 lenFile
= fread(data
, 1, sizeof(data
), fp
);
389 m_filename
= filename
;
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
)
434 _tfopen_s(&fp
, filename
, _T("w+b"));
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
);
444 SendEditor(SCI_SETSAVEPOINT
);
445 ::ShowWindow(m_hWndEdit
, SW_SHOW
);
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
);
462 SendEditor(SCI_STYLESETSIZE
, style
, size
);
464 SendEditor(SCI_STYLESETFONT
, style
, reinterpret_cast<LPARAM
>(face
));
467 bool CMainWindow::IsUTF8(LPVOID pBuffer
, size_t cb
)
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
++)
480 pVal16
= (UINT16
*)pBuffer
;
481 if (*pVal16
== 0xFEFF)
485 if (*pVal16
== 0xBBEF)
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))
498 pVal8
= (UINT8
*)pBuffer
;
500 for (size_t i
=0; i
<(cb
-3); ++i
)
502 if ((*pVal8
& 0xE0)==0xC0)
505 if ((*pVal8
& 0xC0)!=0x80)
509 if ((*pVal8
& 0xF0)==0xE0)
512 if ((*pVal8
& 0xC0)!=0x80)
515 if ((*pVal8
& 0xC0)!=0x80)
519 if ((*pVal8
& 0xF8)==0xF0)
522 if ((*pVal8
& 0xC0)!=0x80)
525 if ((*pVal8
& 0xC0)!=0x80)
528 if ((*pVal8
& 0xC0)!=0x80)
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
);
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
;
561 ofn
.Flags
|= OFN_PATHMUSTEXIST
| OFN_FILEMUSTEXIST
;
563 ofn
.Flags
|= OFN_OVERWRITEPROMPT
;
564 // Display the Open dialog box.
567 if (GetOpenFileName(&ofn
)==TRUE
)
569 LoadFile(ofn
.lpstrFile
);
574 if (filename
.empty())
576 if (GetSaveFileName(&ofn
)==TRUE
)
578 SaveFile(ofn
.lpstrFile
);
582 SaveFile(filename
.c_str());