OSDN Git Service

Merge X64 Build
[tortoisegit/TortoiseGitJp.git] / src / TortoiseBlame / TortoiseBlame.cpp
1 // TortoiseBlame - a Viewer for Subversion Blames\r
2 \r
3 // Copyright (C) 2003-2008 - TortoiseSVN\r
4 \r
5 // This program is free software; you can redistribute it and/or\r
6 // modify it under the terms of the GNU General Public License\r
7 // as published by the Free Software Foundation; either version 2\r
8 // of the License, or (at your option) any later version.\r
9 \r
10 // This program is distributed in the hope that it will be useful,\r
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of\r
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
13 // GNU General Public License for more details.\r
14 \r
15 // You should have received a copy of the GNU General Public License\r
16 // along with this program; if not, write to the Free Software Foundation,\r
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
18 \r
19 #include "stdafx.h"\r
20 #include "CmdLineParser.h"\r
21 #include "TortoiseBlame.h"\r
22 #include "registry.h"\r
23 #include "LangDll.h"\r
24 \r
25 #define MAX_LOADSTRING 1000\r
26 \r
27 #pragma comment(linker, "\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")\r
28 \r
29 #pragma warning(push)\r
30 #pragma warning(disable:4127)           // conditional expression is constant\r
31 \r
32 // Global Variables:\r
33 TCHAR szTitle[MAX_LOADSTRING];                                  // The title bar text\r
34 TCHAR szWindowClass[MAX_LOADSTRING];                    // the main window class name\r
35 TCHAR szViewtitle[MAX_PATH];\r
36 TCHAR szOrigPath[MAX_PATH];\r
37 TCHAR searchstringnotfound[MAX_LOADSTRING];\r
38 \r
39 const bool ShowDate = false;\r
40 const bool ShowAuthor = true;\r
41 const bool ShowLine = true;\r
42 bool ShowPath = false;\r
43 \r
44 static TortoiseBlame app;\r
45 long TortoiseBlame::m_gotoline = 0;\r
46 \r
47 TortoiseBlame::TortoiseBlame()\r
48 {\r
49         hInstance = 0;\r
50         hResource = 0;\r
51         currentDialog = 0;\r
52         wMain = 0;\r
53         wEditor = 0;\r
54         wLocator = 0;\r
55 \r
56         m_font = 0;\r
57         m_italicfont = 0;\r
58         m_blamewidth = 0;\r
59         m_revwidth = 0;\r
60         m_datewidth = 0;\r
61         m_authorwidth = 0;\r
62         m_pathwidth = 0;\r
63         m_linewidth = 0;\r
64 \r
65         m_windowcolor = ::GetSysColor(COLOR_WINDOW);\r
66         m_textcolor = ::GetSysColor(COLOR_WINDOWTEXT);\r
67         m_texthighlightcolor = ::GetSysColor(COLOR_HIGHLIGHTTEXT);\r
68         m_mouserevcolor = InterColor(m_windowcolor, m_textcolor, 20);\r
69         m_mouseauthorcolor = InterColor(m_windowcolor, m_textcolor, 10);\r
70         m_selectedrevcolor = ::GetSysColor(COLOR_HIGHLIGHT);\r
71         m_selectedauthorcolor = InterColor(m_selectedrevcolor, m_texthighlightcolor, 35);\r
72         m_mouserev = -2;\r
73 \r
74         m_selectedrev = -1;\r
75         m_selectedorigrev = -1;\r
76         m_SelectedLine = -1;\r
77         m_directPointer = 0;\r
78         m_directFunction = 0;\r
79 \r
80         m_lowestrev = LONG_MAX;\r
81         m_highestrev = 0;\r
82         m_colorage = true;\r
83 }\r
84 \r
85 TortoiseBlame::~TortoiseBlame()\r
86 {\r
87         if (m_font)\r
88                 DeleteObject(m_font);\r
89         if (m_italicfont)\r
90                 DeleteObject(m_italicfont);\r
91 }\r
92 \r
93 std::string TortoiseBlame::GetAppDirectory()\r
94 {\r
95         std::string path;\r
96         DWORD len = 0;\r
97         DWORD bufferlen = MAX_PATH;             // MAX_PATH is not the limit here!\r
98         do \r
99         {\r
100                 bufferlen += MAX_PATH;          // MAX_PATH is not the limit here!\r
101                 TCHAR * pBuf = new TCHAR[bufferlen];\r
102                 len = GetModuleFileName(NULL, pBuf, bufferlen); \r
103                 path = std::string(pBuf, len);\r
104                 delete [] pBuf;\r
105         } while(len == bufferlen);\r
106         path = path.substr(0, path.rfind('\\') + 1);\r
107 \r
108         return path;\r
109 }\r
110 \r
111 // Return a color which is interpolated between c1 and c2.\r
112 // Slider controls the relative proportions as a percentage:\r
113 // Slider = 0   represents pure c1\r
114 // Slider = 50  represents equal mixture\r
115 // Slider = 100 represents pure c2\r
116 COLORREF TortoiseBlame::InterColor(COLORREF c1, COLORREF c2, int Slider)\r
117 {\r
118         int r, g, b;\r
119         \r
120         // Limit Slider to 0..100% range\r
121         if (Slider < 0)\r
122                 Slider = 0;\r
123         if (Slider > 100)\r
124                 Slider = 100;\r
125         \r
126         // The color components have to be treated individually.\r
127         r = (GetRValue(c2) * Slider + GetRValue(c1) * (100 - Slider)) / 100;\r
128         g = (GetGValue(c2) * Slider + GetGValue(c1) * (100 - Slider)) / 100;\r
129         b = (GetBValue(c2) * Slider + GetBValue(c1) * (100 - Slider)) / 100;\r
130         \r
131         return RGB(r, g, b);\r
132 }\r
133 \r
134 LRESULT TortoiseBlame::SendEditor(UINT Msg, WPARAM wParam, LPARAM lParam)\r
135 {\r
136         if (m_directFunction)\r
137         {\r
138                 return ((SciFnDirect) m_directFunction)(m_directPointer, Msg, wParam, lParam);\r
139         }\r
140         return ::SendMessage(wEditor, Msg, wParam, lParam);     \r
141 }\r
142 \r
143 void TortoiseBlame::GetRange(int start, int end, char *text) \r
144 {\r
145         TEXTRANGE tr;\r
146         tr.chrg.cpMin = start;\r
147         tr.chrg.cpMax = end;\r
148         tr.lpstrText = text;\r
149         SendMessage(wEditor, EM_GETTEXTRANGE, 0, reinterpret_cast<LPARAM>(&tr));\r
150 }\r
151 \r
152 void TortoiseBlame::SetTitle() \r
153 {\r
154         char title[MAX_PATH + 100];\r
155         strcpy_s(title, MAX_PATH + 100, szTitle);\r
156         strcat_s(title, MAX_PATH + 100, " - ");\r
157         strcat_s(title, MAX_PATH + 100, szViewtitle);\r
158         ::SetWindowText(wMain, title);\r
159 }\r
160 \r
161 BOOL TortoiseBlame::OpenLogFile(const char *fileName)\r
162 {\r
163         char logmsgbuf[10000+1];\r
164         FILE * File;\r
165         fopen_s(&File, fileName, "rb");\r
166         if (File == 0)\r
167         {\r
168                 return FALSE;\r
169         }\r
170         LONG rev = 0;\r
171         std::string msg;\r
172         int slength = 0;\r
173         int reallength = 0;\r
174         size_t len = 0;\r
175         wchar_t wbuf[MAX_LOG_LENGTH+6];\r
176         for (;;)\r
177         {\r
178                 len = fread(&rev, sizeof(LONG), 1, File);\r
179                 if (len == 0)\r
180                 {\r
181                         fclose(File);\r
182             InitSize();\r
183                         return TRUE;\r
184                 }\r
185                 len = fread(&slength, sizeof(int), 1, File);\r
186                 if (len == 0)\r
187                 {\r
188                         fclose(File);\r
189             InitSize();\r
190                         return FALSE;\r
191                 }\r
192                 if (slength > MAX_LOG_LENGTH)\r
193                 {\r
194                         reallength = slength;\r
195                         slength = MAX_LOG_LENGTH;\r
196                 }\r
197                 else\r
198                         reallength = 0;\r
199                 len = fread(logmsgbuf, sizeof(char), slength, File);\r
200                 if (len < (size_t)slength)\r
201                 {\r
202                         fclose(File);\r
203             InitSize();\r
204                         return FALSE;\r
205                 }\r
206                 msg = std::string(logmsgbuf, slength);\r
207                 if (reallength)\r
208                 {\r
209                         fseek(File, reallength-MAX_LOG_LENGTH, SEEK_CUR);\r
210                         msg = msg + _T("\n...");\r
211                 }\r
212                 int len2 = ::MultiByteToWideChar(CP_UTF8, NULL, msg.c_str(), min(msg.size(), MAX_LOG_LENGTH+5), wbuf, MAX_LOG_LENGTH+5);\r
213                 wbuf[len2] = 0;\r
214                 len2 = ::WideCharToMultiByte(CP_ACP, NULL, wbuf, len2, logmsgbuf, MAX_LOG_LENGTH+5, NULL, NULL);\r
215                 logmsgbuf[len2] = 0;\r
216                 msg = std::string(logmsgbuf);\r
217                 logmessages[rev] = msg;\r
218         }\r
219 }\r
220 \r
221 BOOL TortoiseBlame::OpenFile(const char *fileName) \r
222 {\r
223         SendEditor(SCI_SETREADONLY, FALSE);\r
224         SendEditor(SCI_CLEARALL);\r
225         SendEditor(EM_EMPTYUNDOBUFFER);\r
226         SetTitle();\r
227         SendEditor(SCI_SETSAVEPOINT);\r
228         SendEditor(SCI_CANCEL);\r
229         SendEditor(SCI_SETUNDOCOLLECTION, 0);\r
230         ::ShowWindow(wEditor, SW_HIDE);\r
231         std::ifstream File;\r
232         File.open(fileName);\r
233         if (!File.good())\r
234         {\r
235                 return FALSE;\r
236         }\r
237         char line[100*1024];\r
238         char * lineptr = NULL;\r
239         char * trimptr = NULL;\r
240         //ignore the first two lines, they're of no interest to us\r
241         File.getline(line, sizeof(line)/sizeof(char));\r
242         File.getline(line, sizeof(line)/sizeof(char));\r
243         m_lowestrev = LONG_MAX;\r
244         m_highestrev = 0;\r
245         bool bUTF8 = true;\r
246         do\r
247         {\r
248                 File.getline(line, sizeof(line)/sizeof(TCHAR));\r
249                 if (File.gcount()>139)\r
250                 {\r
251                         mergelines.push_back((line[0] != ' '));\r
252                         lineptr = &line[9];\r
253                         long rev = _ttol(lineptr);\r
254                         revs.push_back(rev);\r
255                         m_lowestrev = min(m_lowestrev, rev);\r
256                         m_highestrev = max(m_highestrev, rev);\r
257                         lineptr += 7;\r
258                         rev = _ttol(lineptr);\r
259                         origrevs.push_back(rev);\r
260                         lineptr += 7;\r
261                         dates.push_back(std::string(lineptr, 30));\r
262                         lineptr += 31;\r
263                         // unfortunately, the 'path' entry can be longer than the 60 chars\r
264                         // we made the column. We therefore have to step through the path\r
265                         // string until we find a space\r
266                         trimptr = lineptr;\r
267                         do \r
268                         {\r
269                                 // TODO: how can we deal with the situation where the path has\r
270                                 // a space in it, but the space is after the 60 chars reserved\r
271                                 // for it?\r
272                                 // The only way to deal with that would be to use a custom\r
273                                 // binary format for the blame file.\r
274                                 trimptr++;\r
275                                 trimptr = _tcschr(trimptr, ' ');\r
276                         } while ((trimptr)&&(trimptr+1 < lineptr+61));\r
277                         if (trimptr)\r
278                                 *trimptr = 0;\r
279                         else\r
280                                 trimptr = lineptr;\r
281                         paths.push_back(std::string(lineptr));\r
282                         if (trimptr+1 < lineptr+61)\r
283                                 lineptr +=61;\r
284                         else\r
285                                 lineptr = (trimptr+1);\r
286                         trimptr = lineptr+30;\r
287                         while ((*trimptr == ' ')&&(trimptr > lineptr))\r
288                                 trimptr--;\r
289                         *(trimptr+1) = 0;\r
290                         authors.push_back(std::string(lineptr));\r
291                         lineptr += 31;\r
292                         // in case we find an UTF8 BOM at the beginning of the line, we remove it\r
293                         if (((unsigned char)lineptr[0] == 0xEF)&&((unsigned char)lineptr[1] == 0xBB)&&((unsigned char)lineptr[2] == 0xBF))\r
294                         {\r
295                                 lineptr += 3;\r
296                         }\r
297                         if (((unsigned char)lineptr[0] == 0xBB)&&((unsigned char)lineptr[1] == 0xEF)&&((unsigned char)lineptr[2] == 0xBF))\r
298                         {\r
299                                 lineptr += 3;\r
300                         }\r
301                         // check each line for illegal utf8 sequences. If one is found, we treat\r
302                         // the file as ASCII, otherwise we assume an UTF8 file.\r
303                         char * utf8CheckBuf = lineptr;\r
304                         while ((bUTF8)&&(*utf8CheckBuf))\r
305                         {\r
306                                 if ((*utf8CheckBuf == 0xC0)||(*utf8CheckBuf == 0xC1)||(*utf8CheckBuf >= 0xF5))\r
307                                 {\r
308                                         bUTF8 = false;\r
309                                         break;\r
310                                 }\r
311                                 if ((*utf8CheckBuf & 0xE0)==0xC0)\r
312                                 {\r
313                                         utf8CheckBuf++;\r
314                                         if (*utf8CheckBuf == 0)\r
315                                                 break;\r
316                                         if ((*utf8CheckBuf & 0xC0)!=0x80)\r
317                                         {\r
318                                                 bUTF8 = false;\r
319                                                 break;\r
320                                         }\r
321                                 }\r
322                                 if ((*utf8CheckBuf & 0xF0)==0xE0)\r
323                                 {\r
324                                         utf8CheckBuf++;\r
325                                         if (*utf8CheckBuf == 0)\r
326                                                 break;\r
327                                         if ((*utf8CheckBuf & 0xC0)!=0x80)\r
328                                         {\r
329                                                 bUTF8 = false;\r
330                                                 break;\r
331                                         }\r
332                                         utf8CheckBuf++;\r
333                                         if (*utf8CheckBuf == 0)\r
334                                                 break;\r
335                                         if ((*utf8CheckBuf & 0xC0)!=0x80)\r
336                                         {\r
337                                                 bUTF8 = false;\r
338                                                 break;\r
339                                         }\r
340                                 }\r
341                                 if ((*utf8CheckBuf & 0xF8)==0xF0)\r
342                                 {\r
343                                         utf8CheckBuf++;\r
344                                         if (*utf8CheckBuf == 0)\r
345                                                 break;\r
346                                         if ((*utf8CheckBuf & 0xC0)!=0x80)\r
347                                         {\r
348                                                 bUTF8 = false;\r
349                                                 break;\r
350                                         }\r
351                                         utf8CheckBuf++;\r
352                                         if (*utf8CheckBuf == 0)\r
353                                                 break;\r
354                                         if ((*utf8CheckBuf & 0xC0)!=0x80)\r
355                                         {\r
356                                                 bUTF8 = false;\r
357                                                 break;\r
358                                         }\r
359                                         utf8CheckBuf++;\r
360                                         if (*utf8CheckBuf == 0)\r
361                                                 break;\r
362                                         if ((*utf8CheckBuf & 0xC0)!=0x80)\r
363                                         {\r
364                                                 bUTF8 = false;\r
365                                                 break;\r
366                                         }\r
367                                 }\r
368 \r
369                                 utf8CheckBuf++;\r
370                         }\r
371                         SendEditor(SCI_ADDTEXT, _tcslen(lineptr), reinterpret_cast<LPARAM>(static_cast<char *>(lineptr)));\r
372                         SendEditor(SCI_ADDTEXT, 2, (LPARAM)_T("\r\n"));\r
373                 }\r
374         } while (File.gcount() > 0);\r
375 \r
376         if (bUTF8)\r
377                 SendEditor(SCI_SETCODEPAGE, SC_CP_UTF8);\r
378 \r
379         SendEditor(SCI_SETUNDOCOLLECTION, 1);\r
380         ::SetFocus(wEditor);\r
381         SendEditor(EM_EMPTYUNDOBUFFER);\r
382         SendEditor(SCI_SETSAVEPOINT);\r
383         SendEditor(SCI_GOTOPOS, 0);\r
384         SendEditor(SCI_SETSCROLLWIDTHTRACKING, TRUE);\r
385         SendEditor(SCI_SETREADONLY, TRUE);\r
386 \r
387         //check which lexer to use, depending on the filetype\r
388         SetupLexer(fileName);\r
389         ::ShowWindow(wEditor, SW_SHOW);\r
390         m_blamewidth = 0;\r
391         ::InvalidateRect(wMain, NULL, TRUE);\r
392         RECT rc;\r
393         GetWindowRect(wMain, &rc);\r
394         SetWindowPos(wMain, 0, rc.left, rc.top, rc.right-rc.left-1, rc.bottom - rc.top, 0);\r
395         return TRUE;\r
396 }\r
397 \r
398 void TortoiseBlame::SetAStyle(int style, COLORREF fore, COLORREF back, int size, const char *face) \r
399 {\r
400         SendEditor(SCI_STYLESETFORE, style, fore);\r
401         SendEditor(SCI_STYLESETBACK, style, back);\r
402         if (size >= 1)\r
403                 SendEditor(SCI_STYLESETSIZE, style, size);\r
404         if (face) \r
405                 SendEditor(SCI_STYLESETFONT, style, reinterpret_cast<LPARAM>(face));\r
406 }\r
407 \r
408 void TortoiseBlame::InitialiseEditor() \r
409 {\r
410         m_directFunction = SendMessage(wEditor, SCI_GETDIRECTFUNCTION, 0, 0);\r
411         m_directPointer = SendMessage(wEditor, SCI_GETDIRECTPOINTER, 0, 0);\r
412         // Set up the global default style. These attributes are used wherever no explicit choices are made.\r
413         SetAStyle(STYLE_DEFAULT, black, white, (DWORD)CRegStdWORD(_T("Software\\TortoiseGit\\BlameFontSize"), 10), \r
414                 ((stdstring)(CRegStdString(_T("Software\\TortoiseGit\\BlameFontName"), _T("Courier New")))).c_str());\r
415         SendEditor(SCI_SETTABWIDTH, (DWORD)CRegStdWORD(_T("Software\\TortoiseGit\\BlameTabSize"), 4));\r
416         SendEditor(SCI_SETREADONLY, TRUE);\r
417         LRESULT pix = SendEditor(SCI_TEXTWIDTH, STYLE_LINENUMBER, (LPARAM)_T("_99999"));\r
418         if (ShowLine)\r
419                 SendEditor(SCI_SETMARGINWIDTHN, 0, pix);\r
420         else\r
421                 SendEditor(SCI_SETMARGINWIDTHN, 0);\r
422         SendEditor(SCI_SETMARGINWIDTHN, 1);\r
423         SendEditor(SCI_SETMARGINWIDTHN, 2);\r
424         //Set the default windows colors for edit controls\r
425         SendEditor(SCI_STYLESETFORE, STYLE_DEFAULT, ::GetSysColor(COLOR_WINDOWTEXT));\r
426         SendEditor(SCI_STYLESETBACK, STYLE_DEFAULT, ::GetSysColor(COLOR_WINDOW));\r
427         SendEditor(SCI_SETSELFORE, TRUE, ::GetSysColor(COLOR_HIGHLIGHTTEXT));\r
428         SendEditor(SCI_SETSELBACK, TRUE, ::GetSysColor(COLOR_HIGHLIGHT));\r
429         SendEditor(SCI_SETCARETFORE, ::GetSysColor(COLOR_WINDOWTEXT));\r
430         m_regOldLinesColor = CRegStdWORD(_T("Software\\TortoiseGit\\BlameOldColor"), RGB(230, 230, 255));\r
431         m_regNewLinesColor = CRegStdWORD(_T("Software\\TortoiseGit\\BlameNewColor"), RGB(255, 230, 230));\r
432 }\r
433 \r
434 void TortoiseBlame::StartSearch()\r
435 {\r
436         if (currentDialog)\r
437                 return;\r
438         bool bCase = false;\r
439         // Initialize FINDREPLACE\r
440         if (fr.Flags & FR_MATCHCASE)\r
441                 bCase = true;\r
442         SecureZeroMemory(&fr, sizeof(fr));\r
443         fr.lStructSize = sizeof(fr);\r
444         fr.hwndOwner = wMain;\r
445         fr.lpstrFindWhat = szFindWhat;\r
446         fr.wFindWhatLen = 80;\r
447         fr.Flags = FR_HIDEUPDOWN | FR_HIDEWHOLEWORD;\r
448         fr.Flags |= bCase ? FR_MATCHCASE : 0;\r
449 \r
450         currentDialog = FindText(&fr);\r
451 }\r
452 \r
453 bool TortoiseBlame::DoSearch(LPSTR what, DWORD flags)\r
454 {\r
455         TCHAR szWhat[80];\r
456         int pos = SendEditor(SCI_GETCURRENTPOS);\r
457         int line = SendEditor(SCI_LINEFROMPOSITION, pos);\r
458         bool bFound = false;\r
459         bool bCaseSensitive = !!(flags & FR_MATCHCASE);\r
460 \r
461         strcpy_s(szWhat, sizeof(szWhat), what);\r
462 \r
463         if(!bCaseSensitive)\r
464         {\r
465                 char *p;\r
466                 size_t len = strlen(szWhat);\r
467                 for (p = szWhat; p < szWhat + len; p++)\r
468                 {\r
469                         if (isupper(*p)&&__isascii(*p))\r
470                                 *p = _tolower(*p);\r
471                 }\r
472         }\r
473 \r
474         std::string sWhat = std::string(szWhat);\r
475         \r
476         char buf[20];\r
477         int i=0;\r
478         for (i=line; (i<(int)authors.size())&&(!bFound); ++i)\r
479         {\r
480                 int bufsize = SendEditor(SCI_GETLINE, i);\r
481                 char * linebuf = new char[bufsize+1];\r
482                 SecureZeroMemory(linebuf, bufsize+1);\r
483                 SendEditor(SCI_GETLINE, i, (LPARAM)linebuf);\r
484                 if (!bCaseSensitive)\r
485                 {\r
486                         char *p;\r
487                         for (p = linebuf; p < linebuf + bufsize; p++)\r
488                         {\r
489                                 if (isupper(*p)&&__isascii(*p))\r
490                                         *p = _tolower(*p);\r
491                         }\r
492                 }\r
493                 _stprintf_s(buf, 20, _T("%ld"), revs[i]);\r
494                 if (authors[i].compare(sWhat)==0)\r
495                         bFound = true;\r
496                 else if ((!bCaseSensitive)&&(_stricmp(authors[i].c_str(), szWhat)==0))\r
497                         bFound = true;\r
498                 else if (strcmp(buf, szWhat) == 0)\r
499                         bFound = true;\r
500                 else if (strstr(linebuf, szWhat))\r
501                         bFound = true;\r
502                 delete [] linebuf;\r
503         }\r
504         if (!bFound)\r
505         {\r
506                 for (i=0; (i<line)&&(!bFound); ++i)\r
507                 {\r
508                         int bufsize = SendEditor(SCI_GETLINE, i);\r
509                         char * linebuf = new char[bufsize+1];\r
510                         SecureZeroMemory(linebuf, bufsize+1);\r
511                         SendEditor(SCI_GETLINE, i, (LPARAM)linebuf);\r
512                         if (!bCaseSensitive)\r
513                         {\r
514                                 char *p;\r
515                                 for (p = linebuf; p < linebuf + bufsize; p++)\r
516                                 {\r
517                                         if (isupper(*p)&&__isascii(*p))\r
518                                                 *p = _tolower(*p);\r
519                                 }\r
520                         }\r
521                         _stprintf_s(buf, 20, _T("%ld"), revs[i]);\r
522                         if (authors[i].compare(sWhat)==0)\r
523                                 bFound = true;\r
524                         else if ((!bCaseSensitive)&&(_stricmp(authors[i].c_str(), szWhat)==0))\r
525                                 bFound = true;\r
526                         else if (strcmp(buf, szWhat) == 0)\r
527                                 bFound = true;\r
528                         else if (strstr(linebuf, szWhat))\r
529                                 bFound = true;\r
530                         delete [] linebuf;\r
531                 }\r
532         }\r
533         if (bFound)\r
534         {\r
535                 GotoLine(i);\r
536                 int selstart = SendEditor(SCI_GETCURRENTPOS);\r
537                 int selend = SendEditor(SCI_POSITIONFROMLINE, i);\r
538                 SendEditor(SCI_SETSELECTIONSTART, selstart);\r
539                 SendEditor(SCI_SETSELECTIONEND, selend);\r
540                 m_SelectedLine = i-1;\r
541         }\r
542         else\r
543         {\r
544                 ::MessageBox(wMain, searchstringnotfound, "TortoiseBlame", MB_ICONINFORMATION);\r
545         }\r
546         return true;\r
547 }\r
548 \r
549 bool TortoiseBlame::GotoLine(long line)\r
550 {\r
551         --line;\r
552         if (line < 0)\r
553                 return false;\r
554         if ((unsigned long)line >= authors.size())\r
555         {\r
556                 line = authors.size()-1;\r
557         }\r
558 \r
559         int nCurrentPos = SendEditor(SCI_GETCURRENTPOS);\r
560         int nCurrentLine = SendEditor(SCI_LINEFROMPOSITION,nCurrentPos);\r
561         int nFirstVisibleLine = SendEditor(SCI_GETFIRSTVISIBLELINE);\r
562         int nLinesOnScreen = SendEditor(SCI_LINESONSCREEN);\r
563 \r
564         if ( line>=nFirstVisibleLine && line<=nFirstVisibleLine+nLinesOnScreen)\r
565         {\r
566                 // no need to scroll\r
567                 SendEditor(SCI_GOTOLINE, line);\r
568         }\r
569         else\r
570         {\r
571                 // Place the requested line one third from the top\r
572                 if ( line > nCurrentLine )\r
573                 {\r
574                         SendEditor(SCI_GOTOLINE, (WPARAM)(line+(int)nLinesOnScreen*(2/3.0)));\r
575                 }\r
576                 else\r
577                 {\r
578                         SendEditor(SCI_GOTOLINE, (WPARAM)(line-(int)nLinesOnScreen*(1/3.0)));\r
579                 }\r
580         }\r
581 \r
582         // Highlight the line\r
583         int nPosStart = SendEditor(SCI_POSITIONFROMLINE,line);\r
584         int nPosEnd = SendEditor(SCI_GETLINEENDPOSITION,line);\r
585         SendEditor(SCI_SETSEL,nPosEnd,nPosStart);\r
586 \r
587         return true;\r
588 }\r
589 \r
590 bool TortoiseBlame::ScrollToLine(long line)\r
591 {\r
592         if (line < 0)\r
593                 return false;\r
594 \r
595         int nCurrentLine = SendEditor(SCI_GETFIRSTVISIBLELINE);\r
596 \r
597         int scrolldelta = line - nCurrentLine;\r
598         SendEditor(SCI_LINESCROLL, 0, scrolldelta);\r
599 \r
600         return true;\r
601 }\r
602 \r
603 void TortoiseBlame::CopySelectedLogToClipboard()\r
604 {\r
605         if (m_selectedrev <= 0)\r
606                 return;\r
607         std::map<LONG, std::string>::iterator iter;\r
608         if ((iter = app.logmessages.find(m_selectedrev)) != app.logmessages.end())\r
609         {\r
610                 std::string msg;\r
611                 msg += m_selectedauthor;\r
612                 msg += "  ";\r
613                 msg += app.m_selecteddate;\r
614                 msg += '\n';\r
615                 msg += iter->second;\r
616                 msg += _T("\n");\r
617                 if (OpenClipboard(app.wBlame))\r
618                 {\r
619                         EmptyClipboard();\r
620                         HGLOBAL hClipboardData;\r
621                         hClipboardData = GlobalAlloc(GMEM_DDESHARE, msg.size()+1);\r
622                         char * pchData;\r
623                         pchData = (char*)GlobalLock(hClipboardData);\r
624                         strcpy_s(pchData, msg.size()+1, msg.c_str());\r
625                         GlobalUnlock(hClipboardData);\r
626                         SetClipboardData(CF_TEXT,hClipboardData);\r
627                         CloseClipboard();\r
628                 }\r
629         }\r
630 }\r
631 \r
632 void TortoiseBlame::BlamePreviousRevision()\r
633 {\r
634         LONG nRevisionTo = m_selectedorigrev - 1;\r
635         if ( nRevisionTo<1 )\r
636         {\r
637                 return;\r
638         }\r
639 \r
640         // We now determine the smallest revision number in the blame file (but ignore "-1")\r
641         // We do this for two reasons:\r
642         // 1. we respect the "From revision" which the user entered\r
643         // 2. we speed up the call of "svn blame" because previous smaller revision numbers don't have any effect on the result\r
644         LONG nSmallestRevision = -1;\r
645         for (LONG line=0;line<(LONG)app.revs.size();line++)\r
646         {\r
647                 const LONG nRevision = app.revs[line];\r
648                 if ( nRevision > 0 )\r
649                 {\r
650                         if ( nSmallestRevision < 1 )\r
651                         {\r
652                                 nSmallestRevision = nRevision;\r
653                         }\r
654                         else\r
655                         {\r
656                                 nSmallestRevision = min(nSmallestRevision,nRevision);\r
657                         }\r
658                 }\r
659         }\r
660 \r
661         char bufStartRev[20];\r
662         _stprintf_s(bufStartRev, 20, _T("%d"), nSmallestRevision);\r
663 \r
664         char bufEndRev[20];\r
665         _stprintf_s(bufEndRev, 20, _T("%d"), nRevisionTo);\r
666 \r
667         char bufLine[20];\r
668         _stprintf_s(bufLine, 20, _T("%d"), m_SelectedLine+1); //using the current line is a good guess.\r
669 \r
670         STARTUPINFO startup;\r
671         PROCESS_INFORMATION process;\r
672         memset(&startup, 0, sizeof(startup));\r
673         startup.cb = sizeof(startup);\r
674         memset(&process, 0, sizeof(process));\r
675         stdstring tortoiseProcPath = GetAppDirectory() + _T("TortoiseProc.exe");\r
676         stdstring svnCmd = _T(" /command:blame ");\r
677         svnCmd += _T(" /path:\"");\r
678         svnCmd += szOrigPath;\r
679         svnCmd += _T("\"");\r
680         svnCmd += _T(" /startrev:");\r
681         svnCmd += bufStartRev;\r
682         svnCmd += _T(" /endrev:");\r
683         svnCmd += bufEndRev;\r
684         svnCmd += _T(" /line:");\r
685         svnCmd += bufLine;\r
686         if (bIgnoreEOL)\r
687                 svnCmd += _T(" /ignoreeol");\r
688         if (bIgnoreSpaces)\r
689                 svnCmd += _T(" /ignorespaces");\r
690         if (bIgnoreAllSpaces)\r
691                 svnCmd += _T(" /ignoreallspaces");\r
692     if (CreateProcess(tortoiseProcPath.c_str(), const_cast<TCHAR*>(svnCmd.c_str()), NULL, NULL, FALSE, 0, 0, 0, &startup, &process))\r
693         {\r
694                 CloseHandle(process.hThread);\r
695                 CloseHandle(process.hProcess);\r
696         }\r
697 }\r
698 \r
699 void TortoiseBlame::DiffPreviousRevision()\r
700 {\r
701         LONG nRevisionTo = m_selectedorigrev;\r
702         if ( nRevisionTo<1 )\r
703         {\r
704                 return;\r
705         }\r
706 \r
707         LONG nRevisionFrom = nRevisionTo-1;\r
708 \r
709         char bufStartRev[20];\r
710         _stprintf_s(bufStartRev, 20, _T("%d"), nRevisionFrom);\r
711 \r
712         char bufEndRev[20];\r
713         _stprintf_s(bufEndRev, 20, _T("%d"), nRevisionTo);\r
714 \r
715         STARTUPINFO startup;\r
716         PROCESS_INFORMATION process;\r
717         memset(&startup, 0, sizeof(startup));\r
718         startup.cb = sizeof(startup);\r
719         memset(&process, 0, sizeof(process));\r
720         stdstring tortoiseProcPath = GetAppDirectory() + _T("TortoiseProc.exe");\r
721         stdstring svnCmd = _T(" /command:diff ");\r
722         svnCmd += _T(" /path:\"");\r
723         svnCmd += szOrigPath;\r
724         svnCmd += _T("\"");\r
725         svnCmd += _T(" /startrev:");\r
726         svnCmd += bufStartRev;\r
727         svnCmd += _T(" /endrev:");\r
728         svnCmd += bufEndRev;\r
729         if (CreateProcess(tortoiseProcPath.c_str(), const_cast<TCHAR*>(svnCmd.c_str()), NULL, NULL, FALSE, 0, 0, 0, &startup, &process))\r
730         {\r
731                 CloseHandle(process.hThread);\r
732                 CloseHandle(process.hProcess);\r
733         }\r
734 }\r
735 \r
736 void TortoiseBlame::ShowLog()\r
737 {\r
738         char bufRev[20];\r
739         _stprintf_s(bufRev, 20, _T("%d"), m_selectedorigrev);\r
740 \r
741         STARTUPINFO startup;\r
742         PROCESS_INFORMATION process;\r
743         memset(&startup, 0, sizeof(startup));\r
744         startup.cb = sizeof(startup);\r
745         memset(&process, 0, sizeof(process));\r
746         stdstring tortoiseProcPath = GetAppDirectory() + _T("TortoiseProc.exe");\r
747         stdstring svnCmd = _T(" /command:log ");\r
748         svnCmd += _T(" /path:\"");\r
749         svnCmd += szOrigPath;\r
750         svnCmd += _T("\"");\r
751         svnCmd += _T(" /startrev:");\r
752         svnCmd += bufRev;\r
753         svnCmd += _T(" /pegrev:");\r
754         svnCmd += bufRev;\r
755         if (CreateProcess(tortoiseProcPath.c_str(), const_cast<TCHAR*>(svnCmd.c_str()), NULL, NULL, FALSE, 0, 0, 0, &startup, &process))\r
756         {\r
757                 CloseHandle(process.hThread);\r
758                 CloseHandle(process.hProcess);\r
759         }\r
760 }\r
761 \r
762 void TortoiseBlame::Notify(SCNotification *notification) \r
763 {\r
764         switch (notification->nmhdr.code) \r
765         {\r
766         case SCN_SAVEPOINTREACHED:\r
767                 break;\r
768 \r
769         case SCN_SAVEPOINTLEFT:\r
770                 break;\r
771         case SCN_PAINTED:\r
772                 InvalidateRect(wBlame, NULL, FALSE);\r
773                 InvalidateRect(wLocator, NULL, FALSE);\r
774                 break;\r
775         case SCN_GETBKCOLOR:\r
776                 if ((m_colorage)&&(notification->line < (int)revs.size()))\r
777                 {\r
778                         notification->lParam = InterColor(DWORD(m_regOldLinesColor), DWORD(m_regNewLinesColor), (revs[notification->line]-m_lowestrev)*100/((m_highestrev-m_lowestrev)+1));\r
779                 }\r
780                 break;\r
781         }\r
782 }\r
783 \r
784 void TortoiseBlame::Command(int id)\r
785 {\r
786         switch (id) \r
787         {\r
788         case IDM_EXIT:\r
789                 ::PostQuitMessage(0);\r
790                 break;\r
791         case ID_EDIT_FIND:\r
792                 StartSearch();\r
793                 break;\r
794         case ID_COPYTOCLIPBOARD:\r
795                 CopySelectedLogToClipboard();\r
796                 break;\r
797         case ID_BLAME_PREVIOUS_REVISION:\r
798                 BlamePreviousRevision();\r
799                 break;\r
800         case ID_DIFF_PREVIOUS_REVISION:\r
801                 DiffPreviousRevision();\r
802                 break;\r
803         case ID_SHOWLOG:\r
804                 ShowLog();\r
805                 break;\r
806         case ID_EDIT_GOTOLINE:\r
807                 GotoLineDlg();\r
808                 break;\r
809         case ID_VIEW_COLORAGEOFLINES:\r
810                 {\r
811                         m_colorage = !m_colorage;\r
812                         HMENU hMenu = GetMenu(wMain);\r
813                         UINT uCheck = MF_BYCOMMAND;\r
814                         uCheck |= m_colorage ? MF_CHECKED : MF_UNCHECKED;\r
815                         CheckMenuItem(hMenu, ID_VIEW_COLORAGEOFLINES, uCheck);\r
816                         m_blamewidth = 0;\r
817                         InitSize();\r
818                 }\r
819                 break;\r
820         case ID_VIEW_MERGEPATH:\r
821                 {\r
822                         ShowPath = !ShowPath;\r
823                         HMENU hMenu = GetMenu(wMain);\r
824                         UINT uCheck = MF_BYCOMMAND;\r
825                         uCheck |= ShowPath ? MF_CHECKED : MF_UNCHECKED;\r
826                         CheckMenuItem(hMenu, ID_VIEW_MERGEPATH, uCheck);\r
827                         m_blamewidth = 0;\r
828                         InitSize();\r
829                 }\r
830         default:\r
831                 break;\r
832         };\r
833 }\r
834 \r
835 void TortoiseBlame::GotoLineDlg()\r
836 {\r
837         if (DialogBox(hResource, MAKEINTRESOURCE(IDD_GOTODLG), wMain, GotoDlgProc)==IDOK)\r
838         {\r
839                 GotoLine(m_gotoline);\r
840         }\r
841 }\r
842 \r
843 INT_PTR CALLBACK TortoiseBlame::GotoDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM /*lParam*/)\r
844 {\r
845         switch (uMsg)\r
846         {\r
847         case WM_COMMAND:\r
848                 {\r
849                         switch (LOWORD(wParam))\r
850                         {\r
851                         case IDOK:\r
852                                 {\r
853                                         HWND hEditCtrl = GetDlgItem(hwndDlg, IDC_LINENUMBER);\r
854                                         if (hEditCtrl)\r
855                                         {\r
856                                                 TCHAR buf[MAX_PATH];\r
857                                                 if (::GetWindowText(hEditCtrl, buf, MAX_PATH))\r
858                                                 {\r
859                                                         m_gotoline = _ttol(buf);\r
860                                                 }\r
861 \r
862                                         }\r
863                                 }\r
864                         // fall through\r
865                         case IDCANCEL:\r
866                                 EndDialog(hwndDlg, wParam);\r
867                                 break;\r
868                         }\r
869                 }\r
870                 break;\r
871         }\r
872         return FALSE;\r
873 }\r
874 \r
875 LONG TortoiseBlame::GetBlameWidth()\r
876 {\r
877         if (m_blamewidth)\r
878                 return m_blamewidth;\r
879         LONG blamewidth = 0;\r
880         SIZE width;\r
881         CreateFont();\r
882         HDC hDC = ::GetDC(wBlame);\r
883         HFONT oldfont = (HFONT)::SelectObject(hDC, m_font);\r
884         TCHAR buf[MAX_PATH];\r
885         _stprintf_s(buf, MAX_PATH, _T("%8ld "), 88888888);\r
886         ::GetTextExtentPoint(hDC, buf, _tcslen(buf), &width);\r
887         m_revwidth = width.cx + BLAMESPACE;\r
888         blamewidth += m_revwidth;\r
889         if (ShowDate)\r
890         {\r
891                 _stprintf_s(buf, MAX_PATH, _T("%30s"), _T("31.08.2001 06:24:14"));\r
892                 ::GetTextExtentPoint32(hDC, buf, _tcslen(buf), &width);\r
893                 m_datewidth = width.cx + BLAMESPACE;\r
894                 blamewidth += m_datewidth;\r
895         }\r
896         if (ShowAuthor)\r
897         {\r
898                 SIZE maxwidth = {0};\r
899                 for (std::vector<std::string>::iterator I = authors.begin(); I != authors.end(); ++I)\r
900                 {\r
901                         ::GetTextExtentPoint32(hDC, I->c_str(), I->size(), &width);\r
902                         if (width.cx > maxwidth.cx)\r
903                                 maxwidth = width;\r
904                 }\r
905                 m_authorwidth = maxwidth.cx + BLAMESPACE;\r
906                 blamewidth += m_authorwidth;\r
907         }\r
908         if (ShowPath)\r
909         {\r
910                 SIZE maxwidth = {0};\r
911                 for (std::vector<std::string>::iterator I = paths.begin(); I != paths.end(); ++I)\r
912                 {\r
913                         ::GetTextExtentPoint32(hDC, I->c_str(), I->size(), &width);\r
914                         if (width.cx > maxwidth.cx)\r
915                                 maxwidth = width;\r
916                 }\r
917                 m_pathwidth = maxwidth.cx + BLAMESPACE;\r
918                 blamewidth += m_pathwidth;\r
919         }\r
920         ::SelectObject(hDC, oldfont);\r
921         POINT pt = {blamewidth, 0};\r
922         LPtoDP(hDC, &pt, 1);\r
923         m_blamewidth = pt.x;\r
924         ReleaseDC(wBlame, hDC);\r
925         return m_blamewidth;\r
926 }\r
927 \r
928 void TortoiseBlame::CreateFont()\r
929 {\r
930         if (m_font)\r
931                 return;\r
932         LOGFONT lf = {0};\r
933         lf.lfWeight = 400;\r
934         HDC hDC = ::GetDC(wBlame);\r
935         lf.lfHeight = -MulDiv((DWORD)CRegStdWORD(_T("Software\\TortoiseGit\\BlameFontSize"), 10), GetDeviceCaps(hDC, LOGPIXELSY), 72);\r
936         lf.lfCharSet = DEFAULT_CHARSET;\r
937         CRegStdString fontname = CRegStdString(_T("Software\\TortoiseGit\\BlameFontName"), _T("Courier New"));\r
938         _tcscpy_s(lf.lfFaceName, 32, ((stdstring)fontname).c_str());\r
939         m_font = ::CreateFontIndirect(&lf);\r
940 \r
941         lf.lfItalic = TRUE;\r
942         m_italicfont = ::CreateFontIndirect(&lf);\r
943 \r
944         ReleaseDC(wBlame, hDC);\r
945 }\r
946 \r
947 void TortoiseBlame::DrawBlame(HDC hDC)\r
948 {\r
949         if (hDC == NULL)\r
950                 return;\r
951         if (m_font == NULL)\r
952                 return;\r
953 \r
954         HFONT oldfont = NULL;\r
955         LONG_PTR line = SendEditor(SCI_GETFIRSTVISIBLELINE);\r
956         LONG_PTR linesonscreen = SendEditor(SCI_LINESONSCREEN);\r
957         LONG_PTR height = SendEditor(SCI_TEXTHEIGHT);\r
958         LONG_PTR Y = 0;\r
959         TCHAR buf[MAX_PATH];\r
960         RECT rc;\r
961         BOOL sel = FALSE;\r
962         GetClientRect(wBlame, &rc);\r
963         for (LRESULT i=line; i<(line+linesonscreen); ++i)\r
964         {\r
965                 sel = FALSE;\r
966                 if (i < (int)revs.size())\r
967                 {\r
968                         if (mergelines[i])\r
969                                 oldfont = (HFONT)::SelectObject(hDC, m_italicfont);\r
970                         else\r
971                                 oldfont = (HFONT)::SelectObject(hDC, m_font);\r
972                         ::SetBkColor(hDC, m_windowcolor);\r
973                         ::SetTextColor(hDC, m_textcolor);\r
974                         if (authors[i].size()>0)\r
975                         {\r
976                                 if (authors[i].compare(m_mouseauthor)==0)\r
977                                         ::SetBkColor(hDC, m_mouseauthorcolor);\r
978                                 if (authors[i].compare(m_selectedauthor)==0)\r
979                                 {\r
980                                         ::SetBkColor(hDC, m_selectedauthorcolor);\r
981                                         ::SetTextColor(hDC, m_texthighlightcolor);\r
982                                         sel = TRUE;\r
983                                 }\r
984                         }\r
985                         if ((revs[i] == m_mouserev)&&(!sel))\r
986                                 ::SetBkColor(hDC, m_mouserevcolor);\r
987                         if (revs[i] == m_selectedrev)\r
988                         {\r
989                                 ::SetBkColor(hDC, m_selectedrevcolor);\r
990                                 ::SetTextColor(hDC, m_texthighlightcolor);\r
991                         }\r
992                         _stprintf_s(buf, MAX_PATH, _T("%8ld       "), revs[i]);\r
993                         rc.right = rc.left + m_revwidth;\r
994                         ::ExtTextOut(hDC, 0, Y, ETO_CLIPPED, &rc, buf, _tcslen(buf), 0);\r
995                         int Left = m_revwidth;\r
996                         if (ShowDate)\r
997                         {\r
998                                 rc.right = rc.left + Left + m_datewidth;\r
999                                 _stprintf_s(buf, MAX_PATH, _T("%30s            "), dates[i].c_str());\r
1000                                 ::ExtTextOut(hDC, Left, Y, ETO_CLIPPED, &rc, buf, _tcslen(buf), 0);\r
1001                                 Left += m_datewidth;\r
1002                         }\r
1003                         if (ShowAuthor)\r
1004                         {\r
1005                                 rc.right = rc.left + Left + m_authorwidth;\r
1006                                 _stprintf_s(buf, MAX_PATH, _T("%-30s            "), authors[i].c_str());\r
1007                                 ::ExtTextOut(hDC, Left, Y, ETO_CLIPPED, &rc, buf, _tcslen(buf), 0);\r
1008                                 Left += m_authorwidth;\r
1009                         }\r
1010                         if (ShowPath)\r
1011                         {\r
1012                                 rc.right = rc.left + Left + m_pathwidth;\r
1013                                 _stprintf_s(buf, MAX_PATH, _T("%-60s            "), paths[i].c_str());\r
1014                                 ::ExtTextOut(hDC, Left, Y, ETO_CLIPPED, &rc, buf, _tcslen(buf), 0);\r
1015                                 Left += m_authorwidth;\r
1016                         }\r
1017                         if ((i==m_SelectedLine)&&(currentDialog))\r
1018                         {\r
1019                                 LOGBRUSH brush;\r
1020                                 brush.lbColor = m_textcolor;\r
1021                                 brush.lbHatch = 0;\r
1022                                 brush.lbStyle = BS_SOLID;\r
1023                                 HPEN pen = ExtCreatePen(PS_SOLID | PS_GEOMETRIC, 2, &brush, 0, NULL);\r
1024                                 HGDIOBJ hPenOld = SelectObject(hDC, pen);\r
1025                                 RECT rc2 = rc;\r
1026                                 rc2.top = Y;\r
1027                                 rc2.bottom = Y + height;\r
1028                                 ::MoveToEx(hDC, rc2.left, rc2.top, NULL);\r
1029                                 ::LineTo(hDC, rc2.right, rc2.top);\r
1030                                 ::LineTo(hDC, rc2.right, rc2.bottom);\r
1031                                 ::LineTo(hDC, rc2.left, rc2.bottom);\r
1032                                 ::LineTo(hDC, rc2.left, rc2.top);\r
1033                                 SelectObject(hDC, hPenOld); \r
1034                                 DeleteObject(pen); \r
1035                         }\r
1036                         Y += height;\r
1037                         ::SelectObject(hDC, oldfont);\r
1038                 }\r
1039                 else\r
1040                 {\r
1041                         ::SetBkColor(hDC, m_windowcolor);\r
1042                         for (int j=0; j< MAX_PATH; ++j)\r
1043                                 buf[j]=' ';\r
1044                         ::ExtTextOut(hDC, 0, Y, ETO_CLIPPED, &rc, buf, MAX_PATH-1, 0);\r
1045                         Y += height;\r
1046                 }\r
1047         }\r
1048 }\r
1049 \r
1050 void TortoiseBlame::DrawHeader(HDC hDC)\r
1051 {\r
1052         if (hDC == NULL)\r
1053                 return;\r
1054 \r
1055         RECT rc;\r
1056         HFONT oldfont = (HFONT)::SelectObject(hDC, GetStockObject(DEFAULT_GUI_FONT));\r
1057         GetClientRect(wHeader, &rc);\r
1058 \r
1059         ::SetBkColor(hDC, ::GetSysColor(COLOR_BTNFACE));\r
1060 \r
1061         TCHAR szText[MAX_LOADSTRING];\r
1062         LoadString(app.hResource, IDS_HEADER_REVISION, szText, MAX_LOADSTRING);\r
1063         ::ExtTextOut(hDC, LOCATOR_WIDTH, 0, ETO_CLIPPED, &rc, szText, _tcslen(szText), 0);\r
1064         int Left = m_revwidth+LOCATOR_WIDTH;\r
1065         if (ShowDate)\r
1066         {\r
1067                 LoadString(app.hResource, IDS_HEADER_DATE, szText, MAX_LOADSTRING);\r
1068                 ::ExtTextOut(hDC, Left, 0, ETO_CLIPPED, &rc, szText, _tcslen(szText), 0);\r
1069                 Left += m_datewidth;\r
1070         }\r
1071         if (ShowAuthor)\r
1072         {\r
1073                 LoadString(app.hResource, IDS_HEADER_AUTHOR, szText, MAX_LOADSTRING);\r
1074                 ::ExtTextOut(hDC, Left, 0, ETO_CLIPPED, &rc, szText, _tcslen(szText), 0);\r
1075                 Left += m_authorwidth;\r
1076         }\r
1077         if (ShowPath)\r
1078         {\r
1079                 LoadString(app.hResource, IDS_HEADER_PATH, szText, MAX_LOADSTRING);\r
1080                 ::ExtTextOut(hDC, Left, 0, ETO_CLIPPED, &rc, szText, _tcslen(szText), 0);\r
1081                 Left += m_pathwidth;\r
1082         }\r
1083         LoadString(app.hResource, IDS_HEADER_LINE, szText, MAX_LOADSTRING);\r
1084         ::ExtTextOut(hDC, Left, 0, ETO_CLIPPED, &rc, szText, _tcslen(szText), 0);\r
1085 \r
1086         ::SelectObject(hDC, oldfont);\r
1087 }\r
1088 \r
1089 void TortoiseBlame::DrawLocatorBar(HDC hDC)\r
1090 {\r
1091         if (hDC == NULL)\r
1092                 return;\r
1093 \r
1094         LONG_PTR line = SendEditor(SCI_GETFIRSTVISIBLELINE);\r
1095         LONG_PTR linesonscreen = SendEditor(SCI_LINESONSCREEN);\r
1096         LONG_PTR Y = 0;\r
1097         COLORREF blackColor = GetSysColor(COLOR_WINDOWTEXT);\r
1098 \r
1099         RECT rc;\r
1100         GetClientRect(wLocator, &rc);\r
1101         RECT lineRect = rc;\r
1102         LONG height = rc.bottom-rc.top;\r
1103         LONG currentLine = 0;\r
1104 \r
1105         // draw the colored bar\r
1106         for (std::vector<LONG>::const_iterator it = revs.begin(); it != revs.end(); ++it)\r
1107         {\r
1108                 currentLine++;\r
1109                 // get the line color\r
1110                 COLORREF cr = InterColor(DWORD(m_regOldLinesColor), DWORD(m_regNewLinesColor), (*it - m_lowestrev)*100/((m_highestrev-m_lowestrev)+1));\r
1111                 if ((currentLine > line)&&(currentLine <= (line + linesonscreen)))\r
1112                 {\r
1113                         cr = InterColor(cr, blackColor, 10);\r
1114                 }\r
1115                 SetBkColor(hDC, cr);\r
1116                 lineRect.top = Y;\r
1117                 lineRect.bottom = (currentLine * height / revs.size());\r
1118                 ::ExtTextOut(hDC, 0, 0, ETO_OPAQUE, &lineRect, NULL, 0, NULL);\r
1119                 Y = lineRect.bottom;\r
1120         }\r
1121 \r
1122         if (revs.size())\r
1123         {\r
1124                 // now draw two lines indicating the scroll position of the source view\r
1125                 SetBkColor(hDC, blackColor);\r
1126                 lineRect.top = line * height / revs.size();\r
1127                 lineRect.bottom = lineRect.top+1;\r
1128                 ::ExtTextOut(hDC, 0, 0, ETO_OPAQUE, &lineRect, NULL, 0, NULL);\r
1129                 lineRect.top = (line + linesonscreen) * height / revs.size();\r
1130                 lineRect.bottom = lineRect.top+1;\r
1131                 ::ExtTextOut(hDC, 0, 0, ETO_OPAQUE, &lineRect, NULL, 0, NULL);\r
1132         }\r
1133 }\r
1134 \r
1135 void TortoiseBlame::StringExpand(LPSTR str)\r
1136 {\r
1137         char * cPos = str;\r
1138         do\r
1139         {\r
1140                 cPos = strchr(cPos, '\n');\r
1141                 if (cPos)\r
1142                 {\r
1143                         memmove(cPos+1, cPos, strlen(cPos)*sizeof(char));\r
1144                         *cPos = '\r';\r
1145                         cPos++;\r
1146                         cPos++;\r
1147                 }\r
1148         } while (cPos != NULL);\r
1149 }\r
1150 void TortoiseBlame::StringExpand(LPWSTR str)\r
1151 {\r
1152         wchar_t * cPos = str;\r
1153         do\r
1154         {\r
1155                 cPos = wcschr(cPos, '\n');\r
1156                 if (cPos)\r
1157                 {\r
1158                         memmove(cPos+1, cPos, wcslen(cPos)*sizeof(wchar_t));\r
1159                         *cPos = '\r';\r
1160                         cPos++;\r
1161                         cPos++;\r
1162                 }\r
1163         } while (cPos != NULL);\r
1164 }\r
1165 \r
1166 // Forward declarations of functions included in this code module:\r
1167 ATOM                            MyRegisterClass(HINSTANCE hResource);\r
1168 ATOM                            MyRegisterBlameClass(HINSTANCE hResource);\r
1169 ATOM                            MyRegisterHeaderClass(HINSTANCE hResource);\r
1170 ATOM                            MyRegisterLocatorClass(HINSTANCE hResource);\r
1171 BOOL                            InitInstance(HINSTANCE, int);\r
1172 LRESULT CALLBACK        WndProc(HWND, UINT, WPARAM, LPARAM);\r
1173 LRESULT CALLBACK        WndBlameProc(HWND, UINT, WPARAM, LPARAM);\r
1174 LRESULT CALLBACK        WndHeaderProc(HWND, UINT, WPARAM, LPARAM);\r
1175 LRESULT CALLBACK        WndLocatorProc(HWND, UINT, WPARAM, LPARAM);\r
1176 UINT                            uFindReplaceMsg;\r
1177 \r
1178 int APIENTRY _tWinMain(HINSTANCE hInstance,\r
1179                      HINSTANCE /*hPrevInstance*/,\r
1180                      LPTSTR    lpCmdLine,\r
1181                      int       nCmdShow)\r
1182 {\r
1183         app.hInstance = hInstance;\r
1184         MSG msg;\r
1185         HACCEL hAccelTable;\r
1186 \r
1187         if (::LoadLibrary("SciLexer.DLL") == NULL)\r
1188                 return FALSE;\r
1189 \r
1190         CRegStdWORD loc = CRegStdWORD(_T("Software\\TortoiseGit\\LanguageID"), 1033);\r
1191         long langId = loc;\r
1192 \r
1193         CLangDll langDLL;\r
1194         app.hResource = langDLL.Init(_T("TortoiseBlame"), langId);\r
1195         if (app.hResource == NULL)\r
1196                 app.hResource = app.hInstance;\r
1197 \r
1198         // Initialize global strings\r
1199         LoadString(app.hResource, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);\r
1200         LoadString(app.hResource, IDC_TORTOISEBLAME, szWindowClass, MAX_LOADSTRING);\r
1201         LoadString(app.hResource, IDS_SEARCHNOTFOUND, searchstringnotfound, MAX_LOADSTRING);\r
1202         MyRegisterClass(app.hResource);\r
1203         MyRegisterBlameClass(app.hResource);\r
1204         MyRegisterHeaderClass(app.hResource);\r
1205         MyRegisterLocatorClass(app.hResource);\r
1206 \r
1207         // Perform application initialization:\r
1208         if (!InitInstance (app.hResource, nCmdShow)) \r
1209         {\r
1210                 langDLL.Close();\r
1211                 return FALSE;\r
1212         }\r
1213 \r
1214         SecureZeroMemory(szViewtitle, MAX_PATH);\r
1215         SecureZeroMemory(szOrigPath, MAX_PATH);\r
1216         char blamefile[MAX_PATH] = {0};\r
1217         char logfile[MAX_PATH] = {0};\r
1218 \r
1219         CCmdLineParser parser(lpCmdLine);\r
1220 \r
1221 \r
1222         if (__argc > 1)\r
1223         {\r
1224                 _tcscpy_s(blamefile, MAX_PATH, __argv[1]);\r
1225         }\r
1226         if (__argc > 2)\r
1227         {\r
1228                 _tcscpy_s(logfile, MAX_PATH, __argv[2]);\r
1229         }\r
1230         if (__argc > 3)\r
1231         {\r
1232                 _tcscpy_s(szViewtitle, MAX_PATH, __argv[3]);\r
1233                 if (parser.HasVal(_T("revrange")))\r
1234                 {\r
1235                         _tcscat_s(szViewtitle, MAX_PATH, _T(" : "));\r
1236                         _tcscat_s(szViewtitle, MAX_PATH, parser.GetVal(_T("revrange")));\r
1237                 }\r
1238         }\r
1239         if ((_tcslen(blamefile)==0) || parser.HasKey(_T("?")) || parser.HasKey(_T("help")))\r
1240         {\r
1241                 TCHAR szInfo[MAX_LOADSTRING];\r
1242                 LoadString(app.hResource, IDS_COMMANDLINE_INFO, szInfo, MAX_LOADSTRING);\r
1243                 MessageBox(NULL, szInfo, _T("TortoiseBlame"), MB_ICONERROR);\r
1244                 langDLL.Close();\r
1245                 return 0;\r
1246         }\r
1247 \r
1248         if ( parser.HasKey(_T("path")) )\r
1249         {\r
1250                 _tcscpy_s(szOrigPath, MAX_PATH, parser.GetVal(_T("path")));\r
1251         }\r
1252         app.bIgnoreEOL = parser.HasKey(_T("ignoreeol"));\r
1253         app.bIgnoreSpaces = parser.HasKey(_T("ignorespaces"));\r
1254         app.bIgnoreAllSpaces = parser.HasKey(_T("ignoreallspaces"));\r
1255 \r
1256         app.SendEditor(SCI_SETCODEPAGE, GetACP());\r
1257         app.OpenFile(blamefile);\r
1258         if (_tcslen(logfile)>0)\r
1259                 app.OpenLogFile(logfile);\r
1260 \r
1261         if (parser.HasKey(_T("line")))\r
1262         {\r
1263                 app.GotoLine(parser.GetLongVal(_T("line")));\r
1264         }\r
1265 \r
1266         CheckMenuItem(GetMenu(app.wMain), ID_VIEW_COLORAGEOFLINES, MF_CHECKED|MF_BYCOMMAND);\r
1267 \r
1268 \r
1269         hAccelTable = LoadAccelerators(app.hResource, (LPCTSTR)IDC_TORTOISEBLAME);\r
1270 \r
1271         BOOL going = TRUE;\r
1272         msg.wParam = 0;\r
1273         while (going) \r
1274         {\r
1275                 going = GetMessage(&msg, NULL, 0, 0);\r
1276                 if (app.currentDialog && going) \r
1277                 {\r
1278                         if (!IsDialogMessage(app.currentDialog, &msg)) \r
1279                         {\r
1280                                 if (TranslateAccelerator(msg.hwnd, hAccelTable, &msg) == 0) \r
1281                                 {\r
1282                                         TranslateMessage(&msg);\r
1283                                         DispatchMessage(&msg);\r
1284                                 }\r
1285                         }\r
1286                 } \r
1287                 else if (going) \r
1288                 {\r
1289                         if (TranslateAccelerator(app.wMain, hAccelTable, &msg) == 0) \r
1290                         {\r
1291                                 TranslateMessage(&msg);\r
1292                                 DispatchMessage(&msg);\r
1293                         }\r
1294                 }\r
1295         }\r
1296         langDLL.Close();\r
1297         return msg.wParam;\r
1298 }\r
1299 \r
1300 ATOM MyRegisterClass(HINSTANCE hResource)\r
1301 {\r
1302         WNDCLASSEX wcex;\r
1303 \r
1304         wcex.cbSize = sizeof(WNDCLASSEX); \r
1305 \r
1306         wcex.style                      = CS_HREDRAW | CS_VREDRAW;\r
1307         wcex.lpfnWndProc        = (WNDPROC)WndProc;\r
1308         wcex.cbClsExtra         = 0;\r
1309         wcex.cbWndExtra         = 0;\r
1310         wcex.hInstance          = hResource;\r
1311         wcex.hIcon                      = LoadIcon(hResource, (LPCTSTR)IDI_TORTOISEBLAME);\r
1312         wcex.hCursor            = LoadCursor(NULL, IDC_ARROW);\r
1313         wcex.hbrBackground      = (HBRUSH)(COLOR_WINDOW+1);\r
1314         wcex.lpszMenuName       = (LPCTSTR)IDC_TORTOISEBLAME;\r
1315         wcex.lpszClassName      = szWindowClass;\r
1316         wcex.hIconSm            = LoadIcon(wcex.hInstance, (LPCTSTR)IDI_SMALL);\r
1317 \r
1318         return RegisterClassEx(&wcex);\r
1319 }\r
1320 \r
1321 ATOM MyRegisterBlameClass(HINSTANCE hResource)\r
1322 {\r
1323         WNDCLASSEX wcex;\r
1324 \r
1325         wcex.cbSize = sizeof(WNDCLASSEX); \r
1326 \r
1327         wcex.style                      = CS_HREDRAW | CS_VREDRAW;\r
1328         wcex.lpfnWndProc        = (WNDPROC)WndBlameProc;\r
1329         wcex.cbClsExtra         = 0;\r
1330         wcex.cbWndExtra         = 0;\r
1331         wcex.hInstance          = hResource;\r
1332         wcex.hIcon                      = LoadIcon(hResource, (LPCTSTR)IDI_TORTOISEBLAME);\r
1333         wcex.hCursor            = LoadCursor(NULL, IDC_ARROW);\r
1334         wcex.hbrBackground      = (HBRUSH)(COLOR_WINDOW+1);\r
1335         wcex.lpszMenuName       = 0;\r
1336         wcex.lpszClassName      = _T("TortoiseBlameBlame");\r
1337         wcex.hIconSm            = LoadIcon(wcex.hInstance, (LPCTSTR)IDI_SMALL);\r
1338 \r
1339         return RegisterClassEx(&wcex);\r
1340 }\r
1341 \r
1342 ATOM MyRegisterHeaderClass(HINSTANCE hResource)\r
1343 {\r
1344         WNDCLASSEX wcex;\r
1345 \r
1346         wcex.cbSize = sizeof(WNDCLASSEX); \r
1347 \r
1348         wcex.style                      = CS_HREDRAW | CS_VREDRAW;\r
1349         wcex.lpfnWndProc        = (WNDPROC)WndHeaderProc;\r
1350         wcex.cbClsExtra         = 0;\r
1351         wcex.cbWndExtra         = 0;\r
1352         wcex.hInstance          = hResource;\r
1353         wcex.hIcon                      = LoadIcon(hResource, (LPCTSTR)IDI_TORTOISEBLAME);\r
1354         wcex.hCursor            = LoadCursor(NULL, IDC_ARROW);\r
1355         wcex.hbrBackground      = (HBRUSH)(COLOR_BTNFACE+1);\r
1356         wcex.lpszMenuName       = 0;\r
1357         wcex.lpszClassName      = _T("TortoiseBlameHeader");\r
1358         wcex.hIconSm            = LoadIcon(wcex.hInstance, (LPCTSTR)IDI_SMALL);\r
1359 \r
1360         return RegisterClassEx(&wcex);\r
1361 }\r
1362 \r
1363 ATOM MyRegisterLocatorClass(HINSTANCE hResource)\r
1364 {\r
1365         WNDCLASSEX wcex;\r
1366 \r
1367         wcex.cbSize = sizeof(WNDCLASSEX); \r
1368 \r
1369         wcex.style                      = CS_HREDRAW | CS_VREDRAW;\r
1370         wcex.lpfnWndProc        = (WNDPROC)WndLocatorProc;\r
1371         wcex.cbClsExtra         = 0;\r
1372         wcex.cbWndExtra         = 0;\r
1373         wcex.hInstance          = hResource;\r
1374         wcex.hIcon                      = LoadIcon(hResource, (LPCTSTR)IDI_TORTOISEBLAME);\r
1375         wcex.hCursor            = LoadCursor(NULL, IDC_ARROW);\r
1376         wcex.hbrBackground      = (HBRUSH)(COLOR_WINDOW+1);\r
1377         wcex.lpszMenuName       = 0;\r
1378         wcex.lpszClassName      = _T("TortoiseBlameLocator");\r
1379         wcex.hIconSm            = LoadIcon(wcex.hInstance, (LPCTSTR)IDI_SMALL);\r
1380 \r
1381         return RegisterClassEx(&wcex);\r
1382 }\r
1383 \r
1384 BOOL InitInstance(HINSTANCE hResource, int nCmdShow)\r
1385 {\r
1386    app.wMain = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,\r
1387       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hResource, NULL);   \r
1388 \r
1389    if (!app.wMain)\r
1390    {\r
1391       return FALSE;\r
1392    }\r
1393 \r
1394    CRegStdWORD pos(_T("Software\\TortoiseGit\\TBlamePos"), 0);\r
1395    CRegStdWORD width(_T("Software\\TortoiseGit\\TBlameSize"), 0);\r
1396    CRegStdWORD state(_T("Software\\TortoiseGit\\TBlameState"), 0);\r
1397    if (DWORD(pos) && DWORD(width))\r
1398    {\r
1399            RECT rc;\r
1400            rc.left = LOWORD(DWORD(pos));\r
1401            rc.top = HIWORD(DWORD(pos));\r
1402            rc.right = rc.left + LOWORD(DWORD(width));\r
1403            rc.bottom = rc.top + HIWORD(DWORD(width));\r
1404            HMONITOR hMon = MonitorFromRect(&rc, MONITOR_DEFAULTTONULL);\r
1405            if (hMon)\r
1406            {\r
1407                    // only restore the window position if the monitor is valid\r
1408                    MoveWindow(app.wMain, LOWORD(DWORD(pos)), HIWORD(DWORD(pos)),\r
1409                            LOWORD(DWORD(width)), HIWORD(DWORD(width)), FALSE);\r
1410            }\r
1411    }\r
1412    if (DWORD(state) == SW_MAXIMIZE)\r
1413            ShowWindow(app.wMain, SW_MAXIMIZE);\r
1414    else\r
1415            ShowWindow(app.wMain, nCmdShow);\r
1416    UpdateWindow(app.wMain);\r
1417 \r
1418    //Create the tooltips\r
1419 \r
1420    INITCOMMONCONTROLSEX iccex; \r
1421    app.hwndTT;                 // handle to the ToolTip control\r
1422    TOOLINFO ti;\r
1423    RECT rect;                  // for client area coordinates\r
1424    iccex.dwICC = ICC_WIN95_CLASSES;\r
1425    iccex.dwSize = sizeof(INITCOMMONCONTROLSEX);\r
1426    InitCommonControlsEx(&iccex);\r
1427 \r
1428    /* CREATE A TOOLTIP WINDOW */\r
1429    app.hwndTT = CreateWindowEx(WS_EX_TOPMOST,\r
1430            TOOLTIPS_CLASS,\r
1431            NULL,\r
1432            WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,             \r
1433            CW_USEDEFAULT,\r
1434            CW_USEDEFAULT,\r
1435            CW_USEDEFAULT,\r
1436            CW_USEDEFAULT,\r
1437            app.wBlame,\r
1438            NULL,\r
1439            app.hResource,\r
1440            NULL\r
1441            );\r
1442 \r
1443    SetWindowPos(app.hwndTT,\r
1444            HWND_TOPMOST,\r
1445            0,\r
1446            0,\r
1447            0,\r
1448            0,\r
1449            SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);\r
1450 \r
1451    /* GET COORDINATES OF THE MAIN CLIENT AREA */\r
1452    GetClientRect (app.wBlame, &rect);\r
1453 \r
1454    /* INITIALIZE MEMBERS OF THE TOOLINFO STRUCTURE */\r
1455    ti.cbSize = sizeof(TOOLINFO);\r
1456    ti.uFlags = TTF_TRACK | TTF_ABSOLUTE;//TTF_SUBCLASS | TTF_PARSELINKS;\r
1457    ti.hwnd = app.wBlame;\r
1458    ti.hinst = app.hResource;\r
1459    ti.uId = 0;\r
1460    ti.lpszText = LPSTR_TEXTCALLBACK;\r
1461    // ToolTip control will cover the whole window\r
1462    ti.rect.left = rect.left;    \r
1463    ti.rect.top = rect.top;\r
1464    ti.rect.right = rect.right;\r
1465    ti.rect.bottom = rect.bottom;\r
1466 \r
1467    /* SEND AN ADDTOOL MESSAGE TO THE TOOLTIP CONTROL WINDOW */\r
1468    SendMessage(app.hwndTT, TTM_ADDTOOL, 0, (LPARAM) (LPTOOLINFO) &ti);  \r
1469    SendMessage(app.hwndTT, TTM_SETMAXTIPWIDTH, 0, 600);\r
1470    //SendMessage(app.hwndTT, TTM_SETDELAYTIME, TTDT_AUTOPOP, MAKELONG(50000, 0));\r
1471    //SendMessage(app.hwndTT, TTM_SETDELAYTIME, TTDT_RESHOW, MAKELONG(1000, 0));\r
1472    \r
1473    uFindReplaceMsg = RegisterWindowMessage(FINDMSGSTRING);\r
1474    \r
1475    return TRUE;\r
1476 }\r
1477 \r
1478 void TortoiseBlame::InitSize()\r
1479 {\r
1480     RECT rc;\r
1481     RECT blamerc;\r
1482     RECT sourcerc;\r
1483     ::GetClientRect(wMain, &rc);\r
1484     ::SetWindowPos(wHeader, 0, rc.left, rc.top, rc.right-rc.left, HEADER_HEIGHT, 0);\r
1485     rc.top += HEADER_HEIGHT;\r
1486     blamerc.left = rc.left;\r
1487     blamerc.top = rc.top;\r
1488         LONG w = GetBlameWidth();\r
1489     blamerc.right = w > abs(rc.right - rc.left) ? rc.right : w + rc.left;\r
1490     blamerc.bottom = rc.bottom;\r
1491     sourcerc.left = blamerc.right;\r
1492     sourcerc.top = rc.top;\r
1493     sourcerc.bottom = rc.bottom;\r
1494     sourcerc.right = rc.right;\r
1495         if (m_colorage)\r
1496         {\r
1497                 ::OffsetRect(&blamerc, LOCATOR_WIDTH, 0);\r
1498                 ::OffsetRect(&sourcerc, LOCATOR_WIDTH, 0);\r
1499                 sourcerc.right -= LOCATOR_WIDTH;\r
1500         }\r
1501         InvalidateRect(wMain, NULL, FALSE);\r
1502     ::SetWindowPos(wEditor, 0, sourcerc.left, sourcerc.top, sourcerc.right - sourcerc.left, sourcerc.bottom - sourcerc.top, 0);\r
1503         ::SetWindowPos(wBlame, 0, blamerc.left, blamerc.top, blamerc.right - blamerc.left, blamerc.bottom - blamerc.top, 0);\r
1504         if (m_colorage)\r
1505                 ::SetWindowPos(wLocator, 0, 0, blamerc.top, LOCATOR_WIDTH, blamerc.bottom - blamerc.top, SWP_SHOWWINDOW);\r
1506         else\r
1507                 ::ShowWindow(wLocator, SW_HIDE);\r
1508 }\r
1509 \r
1510 LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)\r
1511 {\r
1512         if (message == uFindReplaceMsg)\r
1513         {\r
1514                 LPFINDREPLACE lpfr = (LPFINDREPLACE)lParam;\r
1515 \r
1516                 // If the FR_DIALOGTERM flag is set, \r
1517                 // invalidate the handle identifying the dialog box. \r
1518                 if (lpfr->Flags & FR_DIALOGTERM)\r
1519                 {\r
1520                         app.currentDialog = NULL; \r
1521                         return 0; \r
1522                 } \r
1523                 if (lpfr->Flags & FR_FINDNEXT)\r
1524                 {\r
1525                         app.DoSearch(lpfr->lpstrFindWhat, lpfr->Flags);\r
1526                 }\r
1527                 return 0; \r
1528         }\r
1529         switch (message) \r
1530         {\r
1531         case WM_CREATE:\r
1532                 app.wEditor = ::CreateWindow(\r
1533                         "Scintilla",\r
1534                         "Source",\r
1535                         WS_CHILD | WS_VSCROLL | WS_HSCROLL | WS_CLIPCHILDREN,\r
1536                         0, 0,\r
1537                         100, 100,\r
1538                         hWnd,\r
1539                         0,\r
1540                         app.hResource,\r
1541                         0);\r
1542                 app.InitialiseEditor();\r
1543                 ::ShowWindow(app.wEditor, SW_SHOW);\r
1544                 ::SetFocus(app.wEditor);\r
1545                 app.wBlame = ::CreateWindow(\r
1546                         _T("TortoiseBlameBlame"), \r
1547                         _T("blame"), \r
1548                         WS_CHILD | WS_CLIPCHILDREN,\r
1549                         CW_USEDEFAULT, 0, \r
1550                         CW_USEDEFAULT, 0, \r
1551                         hWnd, \r
1552                         NULL, \r
1553                         app.hResource, \r
1554                         NULL);\r
1555                 ::ShowWindow(app.wBlame, SW_SHOW);\r
1556                 app.wHeader = ::CreateWindow(\r
1557                         _T("TortoiseBlameHeader"), \r
1558                         _T("header"), \r
1559                         WS_CHILD | WS_CLIPCHILDREN | WS_BORDER,\r
1560                         CW_USEDEFAULT, 0, \r
1561                         CW_USEDEFAULT, 0, \r
1562                         hWnd, \r
1563                         NULL, \r
1564                         app.hResource, \r
1565                         NULL);\r
1566                 ::ShowWindow(app.wHeader, SW_SHOW);\r
1567                 app.wLocator = ::CreateWindow(\r
1568                         _T("TortoiseBlameLocator"), \r
1569                         _T("locator"), \r
1570                         WS_CHILD | WS_CLIPCHILDREN | WS_BORDER,\r
1571                         CW_USEDEFAULT, 0, \r
1572                         CW_USEDEFAULT, 0, \r
1573                         hWnd, \r
1574                         NULL, \r
1575                         app.hResource, \r
1576                         NULL);\r
1577                 ::ShowWindow(app.wLocator, SW_SHOW);\r
1578                 return 0;\r
1579 \r
1580         case WM_SIZE:\r
1581                 if (wParam != 1) \r
1582                 {\r
1583             app.InitSize();\r
1584                 }\r
1585                 return 0;\r
1586 \r
1587         case WM_COMMAND:\r
1588                 app.Command(LOWORD(wParam));\r
1589                 break;\r
1590         case WM_NOTIFY:\r
1591                 app.Notify(reinterpret_cast<SCNotification *>(lParam));\r
1592                 return 0;\r
1593         case WM_DESTROY:\r
1594                 PostQuitMessage(0);\r
1595                 break;\r
1596         case WM_CLOSE:\r
1597                 {\r
1598                         CRegStdWORD pos(_T("Software\\TortoiseGit\\TBlamePos"), 0);\r
1599                         CRegStdWORD width(_T("Software\\TortoiseGit\\TBlameSize"), 0);\r
1600                         CRegStdWORD state(_T("Software\\TortoiseGit\\TBlameState"), 0);\r
1601                         RECT rc;\r
1602                         GetWindowRect(app.wMain, &rc);\r
1603                         if ((rc.left >= 0)&&(rc.top >= 0))\r
1604                         {\r
1605                                 pos = MAKELONG(rc.left, rc.top);\r
1606                                 width = MAKELONG(rc.right-rc.left, rc.bottom-rc.top);\r
1607                         }\r
1608                         WINDOWPLACEMENT wp = {0};\r
1609                         wp.length = sizeof(WINDOWPLACEMENT);\r
1610                         GetWindowPlacement(app.wMain, &wp);\r
1611                         state = wp.showCmd;\r
1612                         ::DestroyWindow(app.wEditor);\r
1613                         ::PostQuitMessage(0);\r
1614                 }\r
1615                 return 0;\r
1616         case WM_SETFOCUS:\r
1617                 ::SetFocus(app.wBlame);\r
1618                 break;\r
1619         default:\r
1620                 return DefWindowProc(hWnd, message, wParam, lParam);\r
1621         }\r
1622         return 0;\r
1623 }\r
1624 \r
1625 LRESULT CALLBACK WndBlameProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)\r
1626 {\r
1627         PAINTSTRUCT ps;\r
1628         TRACKMOUSEEVENT mevt;\r
1629         HDC hDC;\r
1630         switch (message) \r
1631         {\r
1632         case WM_CREATE:\r
1633                 return 0;\r
1634         case WM_PAINT:\r
1635                 hDC = BeginPaint(app.wBlame, &ps);\r
1636                 app.DrawBlame(hDC);\r
1637                 EndPaint(app.wBlame, &ps);\r
1638                 break;\r
1639         case WM_COMMAND:\r
1640                 app.Command(LOWORD(wParam));\r
1641                 break;\r
1642         case WM_NOTIFY:\r
1643                 switch (((LPNMHDR)lParam)->code)\r
1644                 {\r
1645                 case TTN_GETDISPINFO:\r
1646                         {\r
1647                                 LPNMHDR pNMHDR = (LPNMHDR)lParam;\r
1648                                 NMTTDISPINFOA* pTTTA = (NMTTDISPINFOA*)pNMHDR;\r
1649                                 NMTTDISPINFOW* pTTTW = (NMTTDISPINFOW*)pNMHDR;\r
1650                                 POINT point;\r
1651                                 DWORD ptW = GetMessagePos();\r
1652                                 point.x = GET_X_LPARAM(ptW);\r
1653                                 point.y = GET_Y_LPARAM(ptW);\r
1654                                 ::ScreenToClient(app.wBlame, &point);\r
1655                                 LONG_PTR line = app.SendEditor(SCI_GETFIRSTVISIBLELINE);\r
1656                                 LONG_PTR height = app.SendEditor(SCI_TEXTHEIGHT);\r
1657                                 line = line + (point.y/height);\r
1658                                 if (line >= (LONG)app.revs.size())\r
1659                                         break;\r
1660                                 if (line < 0)\r
1661                                         break;\r
1662                                 LONG rev = app.revs[line];\r
1663                                 if (line >= (LONG)app.revs.size())\r
1664                                         break;\r
1665 \r
1666                                 SecureZeroMemory(app.m_szTip, sizeof(app.m_szTip));\r
1667                                 SecureZeroMemory(app.m_wszTip, sizeof(app.m_wszTip));\r
1668                                 std::map<LONG, std::string>::iterator iter;\r
1669                                 if ((iter = app.logmessages.find(rev)) != app.logmessages.end())\r
1670                                 {\r
1671                                         std::string msg;\r
1672                                         if (!ShowAuthor)\r
1673                                         {\r
1674                                                 msg += app.authors[line];\r
1675                                         }\r
1676                                         if (!ShowDate)\r
1677                                         {\r
1678                                                 if (!ShowAuthor) msg += "  ";\r
1679                                                 msg += app.dates[line];\r
1680                                         }\r
1681                                         if (!ShowAuthor || !ShowDate)\r
1682                                                 msg += '\n';\r
1683                                         msg += iter->second;\r
1684                                         // an empty tooltip string will deactivate the tooltips,\r
1685                                         // which means we must make sure that the tooltip won't\r
1686                                         // be empty.\r
1687                                         if (msg.empty())\r
1688                                                 msg = _T(" ");\r
1689                                         if (pNMHDR->code == TTN_NEEDTEXTA)\r
1690                                         {\r
1691                                                 lstrcpyn(app.m_szTip, msg.c_str(), MAX_LOG_LENGTH*2);\r
1692                                                 app.StringExpand(app.m_szTip);\r
1693                                                 pTTTA->lpszText = app.m_szTip;\r
1694                                         }\r
1695                                         else\r
1696                                         {\r
1697                                                 pTTTW->lpszText = app.m_wszTip;\r
1698                                                 ::MultiByteToWideChar( CP_ACP , 0, msg.c_str(), min(msg.size(), MAX_LOG_LENGTH*2), app.m_wszTip, MAX_LOG_LENGTH*2);\r
1699                                                 app.StringExpand(app.m_wszTip);\r
1700                                         }\r
1701                                 }\r
1702                         }\r
1703                         break;\r
1704                 }\r
1705                 return 0;\r
1706         case WM_DESTROY:\r
1707                 break;\r
1708         case WM_CLOSE:\r
1709                 return 0;\r
1710         case WM_MOUSELEAVE:\r
1711                 app.m_mouserev = -2;\r
1712                 app.m_mouseauthor.clear();\r
1713                 app.ttVisible = FALSE;\r
1714                 SendMessage(app.hwndTT, TTM_TRACKACTIVATE, FALSE, 0);\r
1715                 ::InvalidateRect(app.wBlame, NULL, FALSE);\r
1716                 break;\r
1717         case WM_MOUSEMOVE:\r
1718                 {\r
1719                         mevt.cbSize = sizeof(TRACKMOUSEEVENT);\r
1720                         mevt.dwFlags = TME_LEAVE;\r
1721                         mevt.dwHoverTime = HOVER_DEFAULT;\r
1722                         mevt.hwndTrack = app.wBlame;\r
1723                         ::TrackMouseEvent(&mevt);\r
1724                         POINT pt = {((int)(short)LOWORD(lParam)), ((int)(short)HIWORD(lParam))};\r
1725                         ClientToScreen(app.wBlame, &pt);\r
1726                         pt.x += 15;\r
1727                         pt.y += 15;\r
1728                         SendMessage(app.hwndTT, TTM_TRACKPOSITION, 0, MAKELONG(pt.x, pt.y));\r
1729                         if (!app.ttVisible)\r
1730                         {\r
1731                                 TOOLINFO ti;\r
1732                                 ti.cbSize = sizeof(TOOLINFO);\r
1733                                 ti.hwnd = app.wBlame;\r
1734                                 ti.uId = 0;\r
1735                                 SendMessage(app.hwndTT, TTM_TRACKACTIVATE, TRUE, (LPARAM)&ti);\r
1736                         }\r
1737                         int y = ((int)(short)HIWORD(lParam));\r
1738                         LONG_PTR line = app.SendEditor(SCI_GETFIRSTVISIBLELINE);\r
1739                         LONG_PTR height = app.SendEditor(SCI_TEXTHEIGHT);\r
1740                         line = line + (y/height);\r
1741                         app.ttVisible = (line < (LONG)app.revs.size());\r
1742                         if ( app.ttVisible )\r
1743                         {\r
1744                                 if (app.authors[line].compare(app.m_mouseauthor) != 0)\r
1745                                 {\r
1746                                         app.m_mouseauthor = app.authors[line];\r
1747                                 }\r
1748                                 if (app.revs[line] != app.m_mouserev)\r
1749                                 {\r
1750                                         app.m_mouserev = app.revs[line];\r
1751                                         ::InvalidateRect(app.wBlame, NULL, FALSE);\r
1752                                         SendMessage(app.hwndTT, TTM_UPDATE, 0, 0);\r
1753                                 }\r
1754                         }\r
1755                 }\r
1756                 break;\r
1757         case WM_RBUTTONDOWN:\r
1758                 // fall through\r
1759         case WM_LBUTTONDOWN:\r
1760                 {\r
1761                         int y = ((int)(short)HIWORD(lParam));\r
1762                         LONG_PTR line = app.SendEditor(SCI_GETFIRSTVISIBLELINE);\r
1763                         LONG_PTR height = app.SendEditor(SCI_TEXTHEIGHT);\r
1764                         line = line + (y/height);\r
1765                         if (line < (LONG)app.revs.size())\r
1766                         {\r
1767                                 app.SetSelectedLine(line);\r
1768                                 if (app.revs[line] != app.m_selectedrev)\r
1769                                 {\r
1770                                         app.m_selectedrev = app.revs[line];\r
1771                                         app.m_selectedorigrev = app.origrevs[line];\r
1772                                         app.m_selectedauthor = app.authors[line];\r
1773                                         app.m_selecteddate = app.dates[line];\r
1774                                 }\r
1775                                 else\r
1776                                 {\r
1777                                         app.m_selectedauthor.clear();\r
1778                                         app.m_selecteddate.clear();\r
1779                                         app.m_selectedrev = -2;\r
1780                                         app.m_selectedorigrev = -2;\r
1781                                 }\r
1782                                 ::InvalidateRect(app.wBlame, NULL, FALSE);\r
1783                         }\r
1784                         else\r
1785                         {\r
1786                                 app.SetSelectedLine(-1);\r
1787                         }\r
1788                 }\r
1789                 break;\r
1790         case WM_SETFOCUS:\r
1791                 ::SetFocus(app.wBlame);\r
1792                 app.SendEditor(SCI_GRABFOCUS);\r
1793                 break;\r
1794         case WM_CONTEXTMENU:\r
1795                 {\r
1796                         if (app.m_selectedrev <= 0)\r
1797                                 break;;\r
1798                         int xPos = GET_X_LPARAM(lParam);\r
1799                         int yPos = GET_Y_LPARAM(lParam);\r
1800                         if ((xPos < 0)||(yPos < 0))\r
1801                         {\r
1802                                 // requested from keyboard, not mouse pointer\r
1803                                 // use the center of the window\r
1804                                 RECT rect;\r
1805                                 GetClientRect(app.wBlame, &rect);\r
1806                                 xPos = rect.right-rect.left;\r
1807                                 yPos = rect.bottom-rect.top;\r
1808                         }\r
1809                         HMENU hMenu = LoadMenu(app.hResource, MAKEINTRESOURCE(IDR_BLAMEPOPUP));\r
1810                         HMENU hPopMenu = GetSubMenu(hMenu, 0);\r
1811 \r
1812                         if ( _tcslen(szOrigPath)==0 )\r
1813                         {\r
1814                                 // Without knowing the original path we cannot blame the previous revision\r
1815                                 // because we don't know which filename to pass to tortoiseproc.\r
1816                                 EnableMenuItem(hPopMenu,ID_BLAME_PREVIOUS_REVISION, MF_DISABLED|MF_GRAYED);\r
1817                         }\r
1818                         \r
1819                         TrackPopupMenu(hPopMenu, TPM_LEFTALIGN | TPM_RIGHTBUTTON, xPos, yPos, 0, app.wBlame, NULL); \r
1820                         DestroyMenu(hMenu);\r
1821                 }\r
1822                 break;\r
1823         default:\r
1824                 return DefWindowProc(hWnd, message, wParam, lParam);\r
1825         }\r
1826         return 0;\r
1827 }\r
1828 \r
1829 LRESULT CALLBACK WndHeaderProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)\r
1830 {\r
1831         PAINTSTRUCT ps;\r
1832         HDC hDC;\r
1833         switch (message) \r
1834         {\r
1835         case WM_CREATE:\r
1836                 return 0;\r
1837         case WM_PAINT:\r
1838                 hDC = BeginPaint(app.wHeader, &ps);\r
1839                 app.DrawHeader(hDC);\r
1840                 EndPaint(app.wHeader, &ps);\r
1841                 break;\r
1842         case WM_COMMAND:\r
1843                 break;\r
1844         case WM_DESTROY:\r
1845                 break;\r
1846         case WM_CLOSE:\r
1847                 return 0;\r
1848         default:\r
1849                 return DefWindowProc(hWnd, message, wParam, lParam);\r
1850         }\r
1851         return 0;\r
1852 }\r
1853 \r
1854 LRESULT CALLBACK WndLocatorProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)\r
1855 {\r
1856         PAINTSTRUCT ps;\r
1857         HDC hDC;\r
1858         switch (message) \r
1859         {\r
1860         case WM_PAINT:\r
1861                 hDC = BeginPaint(app.wLocator, &ps);\r
1862                 app.DrawLocatorBar(hDC);\r
1863                 EndPaint(app.wLocator, &ps);\r
1864                 break;\r
1865         case WM_LBUTTONDOWN:\r
1866         case WM_MOUSEMOVE:\r
1867                 if (wParam & MK_LBUTTON)\r
1868                 {\r
1869                         RECT rect;\r
1870                         ::GetClientRect(hWnd, &rect); \r
1871                         int nLine = HIWORD(lParam)*app.revs.size()/(rect.bottom-rect.top);\r
1872 \r
1873                         if (nLine < 0)\r
1874                                 nLine = 0;\r
1875                         app.ScrollToLine(nLine);\r
1876                 }\r
1877                 break;\r
1878         default:\r
1879                 return DefWindowProc(hWnd, message, wParam, lParam);\r
1880         }\r
1881         return 0;\r
1882 }\r
1883 \r
1884 #pragma warning(pop)\r