Advertisement
Guest User

Untitled

a guest
Sep 20th, 2011
199
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 186.14 KB | None | 0 0
  1. Index: src/CommitMonitor.cpp
  2. ===================================================================
  3. --- src/CommitMonitor.cpp (revision 605)
  4. +++ src/CommitMonitor.cpp (working copy)
  5. @@ -33,6 +33,9 @@
  6. #define STRUCT_IOVEC_DEFINED
  7. #include "sasl.h"
  8.  
  9. +#include "Http.h"
  10. +#include "ReviewBoardXml.h"
  11. +
  12. // Global Variables:
  13. HINSTANCE hInst; // current instance
  14. HANDLE g_mutex = 0;
  15. @@ -47,6 +50,8 @@
  16. UNREFERENCED_PARAMETER(hPrevInstance);
  17. UNREFERENCED_PARAMETER(lpCmdLine);
  18. UNREFERENCED_PARAMETER(nCmdShow);
  19. + gHTTP.Init("reviews.reviewboard.org");
  20. + gReviewBoard;
  21.  
  22. SetDllDirectory(L"");
  23. ::OleInitialize(NULL);
  24. @@ -116,7 +121,7 @@
  25. else
  26. {
  27. //only one instance of this application part allowed
  28. - g_mutex = ::CreateMutex(NULL, FALSE, APPNAME_MUTEX);
  29. + g_mutex = ::CreateMutexW(NULL, FALSE, APPNAME_MUTEX);
  30.  
  31. if (g_mutex != NULL)
  32. {
  33. @@ -141,6 +146,8 @@
  34.  
  35. if (hiddenWindow.RegisterAndCreateWindow())
  36. {
  37. + gReviewBoard.Load();
  38. +
  39. if ((snarlIface.GetVersionEx() != Snarl::M_FAILED)&&(Snarl::SnarlInterface::GetSnarlWindow() != NULL))
  40. {
  41. wstring imgPath = CAppUtils::GetAppDataDir()+L"\\CM.png";
  42. @@ -189,7 +196,8 @@
  43. // unregister with Snarl
  44. snarlIface.UnregisterApp();
  45. }
  46. -
  47. + gReviewBoard_Cleanup;
  48. + gHTTP_Cleanup;
  49. return (int) msg.wParam;
  50. }
  51.  
  52. Index: src/CommitMonitor.vcxproj
  53. ===================================================================
  54. --- src/CommitMonitor.vcxproj (revision 605)
  55. +++ src/CommitMonitor.vcxproj (working copy)
  56. @@ -51,7 +51,7 @@
  57. </PreBuildEvent>
  58. <ClCompile>
  59. <Optimization>Disabled</Optimization>
  60. - <AdditionalIncludeDirectories>..\ext\apr\include;..\ext\Subversion\Subversion\include;..\ext\apr-util\include;Resources;..\ext\scintilla\win32;..\ext\scintilla\src;..\ext\scintilla\lexers;..\ext\scintilla\lexlib;..\ext\scintilla\include;..\ext\cyrus-sasl\include;..\ext\snarl;..\ext\BlowFish;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
  61. + <AdditionalIncludeDirectories>J:\dev\poco\include;..\ext\rapidxml;..\ext\apr\include;..\ext\Subversion\Subversion\include;..\ext\apr-util\include;Resources;..\ext\scintilla\win32;..\ext\scintilla\src;..\ext\scintilla\lexers;..\ext\scintilla\lexlib;..\ext\scintilla\include;..\ext\cyrus-sasl\include;..\ext\snarl;..\ext\BlowFish;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
  62. <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;SVN_DEBUG;APR_DECLARE_STATIC;APU_DECLARE_STATIC;STATIC_BUILD;SCI_LEXER;%(PreprocessorDefinitions)</PreprocessorDefinitions>
  63. <MinimalRebuild>true</MinimalRebuild>
  64. <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
  65. @@ -65,7 +65,7 @@
  66. <ResourceOutputFileName>$(IntDir)%(Filename).res</ResourceOutputFileName>
  67. </ResourceCompile>
  68. <Link>
  69. - <AdditionalDependencies>Ws2_32.lib;Mswsock.lib;Rpcrt4.lib;Imm32.lib;Secur32.lib;Crypt32.lib;..\ext\apr\debug_win32\libapr.lib;..\ext\apr-util\debug_win32\libaprutil.lib;..\ext\neon\debug_win32\libneon.lib;..\ext\Subversion\debug_win32\libsvn_client.lib;..\ext\Subversion\debug_win32\libsvn_delta.lib;..\ext\Subversion\debug_win32\libsvn_diff.lib;..\ext\Subversion\debug_win32\libsvn_ra.lib;..\ext\Subversion\debug_win32\libsvn_ra_neon.lib;..\ext\Subversion\debug_win32\libsvn_ra_serf.lib;..\ext\Subversion\debug_win32\libsvn_ra_svn.lib;..\ext\Subversion\debug_win32\libsvn_repos.lib;..\ext\Subversion\debug_win32\libsvn_subr.lib;..\ext\Subversion\debug_win32\libsvn_wc.lib;..\ext\cyrus-sasl\debug_win32\libsasls.lib;..\ext\serf\debug_win32\libserf.lib;..\ext\sqlite\debug_win32\sqlite.lib;%(AdditionalDependencies)</AdditionalDependencies>
  70. + <AdditionalDependencies>PocoFoundationmtd.lib;PocoNetmtd.lib;PocoUtilmtd.lib;Ws2_32.lib;Mswsock.lib;Rpcrt4.lib;Imm32.lib;Secur32.lib;Crypt32.lib;..\ext\apr\debug_win32\libapr.lib;..\ext\apr-util\debug_win32\libaprutil.lib;..\ext\neon\debug_win32\libneon.lib;..\ext\Subversion\debug_win32\libsvn_client.lib;..\ext\Subversion\debug_win32\libsvn_delta.lib;..\ext\Subversion\debug_win32\libsvn_diff.lib;..\ext\Subversion\debug_win32\libsvn_ra.lib;..\ext\Subversion\debug_win32\libsvn_ra_neon.lib;..\ext\Subversion\debug_win32\libsvn_ra_serf.lib;..\ext\Subversion\debug_win32\libsvn_ra_svn.lib;..\ext\Subversion\debug_win32\libsvn_repos.lib;..\ext\Subversion\debug_win32\libsvn_subr.lib;..\ext\Subversion\debug_win32\libsvn_wc.lib;..\ext\cyrus-sasl\debug_win32\libsasls.lib;..\ext\serf\debug_win32\libserf.lib;..\ext\sqlite\debug_win32\sqlite.lib;%(AdditionalDependencies)</AdditionalDependencies>
  71. <IgnoreSpecificDefaultLibraries>winspool.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
  72. <GenerateDebugInformation>true</GenerateDebugInformation>
  73. <SubSystem>Windows</SubSystem>
  74. @@ -73,6 +73,7 @@
  75. <DataExecutionPrevention>
  76. </DataExecutionPrevention>
  77. <TargetMachine>MachineX86</TargetMachine>
  78. + <AdditionalLibraryDirectories>J:\dev\poco\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
  79. </Link>
  80. </ItemDefinitionGroup>
  81. <ItemDefinitionGroup Condition="&#039;$(Configuration)|$(Platform)&#039;==&#039;Release|Win32&#039;">
  82. @@ -94,7 +95,7 @@
  83. <AdditionalIncludeDirectories>..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
  84. </ResourceCompile>
  85. <Link>
  86. - <AdditionalDependencies>Ws2_32.lib;Mswsock.lib;Rpcrt4.lib;Imm32.lib;Secur32.lib;Crypt32.lib;..\ext\apr\release_win32\libapr.lib;..\ext\apr-util\release_win32\libaprutil.lib;..\ext\neon\release_win32\libneon.lib;..\ext\Subversion\release_win32\libsvn_client.lib;..\ext\Subversion\release_win32\libsvn_delta.lib;..\ext\Subversion\release_win32\libsvn_diff.lib;..\ext\Subversion\release_win32\libsvn_ra.lib;..\ext\Subversion\release_win32\libsvn_ra_neon.lib;..\ext\Subversion\release_win32\libsvn_ra_serf.lib;..\ext\Subversion\release_win32\libsvn_ra_svn.lib;..\ext\Subversion\release_win32\libsvn_repos.lib;..\ext\Subversion\release_win32\libsvn_subr.lib;..\ext\Subversion\release_win32\libsvn_wc.lib;..\ext\cyrus-sasl\release_win32\libsasls.lib;..\ext\serf\release_win32\libserf.lib;..\ext\sqlite\debug_win32\sqlite.lib;%(AdditionalDependencies)</AdditionalDependencies>
  87. + <AdditionalDependencies>PocoFoundationmt.lib;PocoNetmt.lib;PocoUtilmt.lib;Ws2_32.lib;Mswsock.lib;Rpcrt4.lib;Imm32.lib;Secur32.lib;Crypt32.lib;..\ext\apr\release_win32\libapr.lib;..\ext\apr-util\release_win32\libaprutil.lib;..\ext\neon\release_win32\libneon.lib;..\ext\Subversion\release_win32\libsvn_client.lib;..\ext\Subversion\release_win32\libsvn_delta.lib;..\ext\Subversion\release_win32\libsvn_diff.lib;..\ext\Subversion\release_win32\libsvn_ra.lib;..\ext\Subversion\release_win32\libsvn_ra_neon.lib;..\ext\Subversion\release_win32\libsvn_ra_serf.lib;..\ext\Subversion\release_win32\libsvn_ra_svn.lib;..\ext\Subversion\release_win32\libsvn_repos.lib;..\ext\Subversion\release_win32\libsvn_subr.lib;..\ext\Subversion\release_win32\libsvn_wc.lib;..\ext\cyrus-sasl\release_win32\libsasls.lib;..\ext\serf\release_win32\libserf.lib;..\ext\sqlite\debug_win32\sqlite.lib;%(AdditionalDependencies)</AdditionalDependencies>
  88. <IgnoreSpecificDefaultLibraries>winspool.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
  89. <GenerateDebugInformation>true</GenerateDebugInformation>
  90. <SubSystem>Windows</SubSystem>
  91. @@ -323,10 +324,12 @@
  92. <ClCompile Include="DiffViewer.cpp" />
  93. <ClCompile Include="FindBar.cpp" />
  94. <ClCompile Include="HiddenWindow.cpp" />
  95. + <ClCompile Include="Http.cpp" />
  96. <ClCompile Include="MainDlg.cpp" />
  97. <ClCompile Include="OptionsDlg.cpp" />
  98. <ClCompile Include="PasswordDlg.cpp" />
  99. <ClCompile Include="ProgressDlg.cpp" />
  100. + <ClCompile Include="ReviewBoardXml.cpp" />
  101. <ClCompile Include="SCCS.cpp" />
  102. <ClCompile Include="SerializeUtils.cpp" />
  103. <ClCompile Include="StatusBarMsgWnd.cpp" />
  104. @@ -336,6 +339,7 @@
  105. </ClCompile>
  106. <ClCompile Include="SVN.cpp" />
  107. <ClCompile Include="SVNPool.cpp" />
  108. + <ClCompile Include="timesupport.cc" />
  109. <ClCompile Include="UpdateDlg.cpp" />
  110. <ClCompile Include="URLDlg.cpp" />
  111. <ClCompile Include="UrlInfo.cpp" />
  112. @@ -410,20 +414,25 @@
  113. <ClInclude Include="Accurev.h" />
  114. <ClInclude Include="Callback.h" />
  115. <ClInclude Include="CommitMonitor.h" />
  116. + <ClInclude Include="Convert.h" />
  117. <ClInclude Include="DiffViewer.h" />
  118. <ClInclude Include="FindBar.h" />
  119. <ClInclude Include="HiddenWindow.h" />
  120. + <ClInclude Include="Http.h" />
  121. <ClInclude Include="MainDlg.h" />
  122. <ClInclude Include="OptionsDlg.h" />
  123. <ClInclude Include="PasswordDlg.h" />
  124. <ClInclude Include="ProgressDlg.h" />
  125. <ClInclude Include="Resources\resource.h" />
  126. + <ClInclude Include="ReviewBoardXml.h" />
  127. <ClInclude Include="SCCS.h" />
  128. <ClInclude Include="SerializeUtils.h" />
  129. + <ClInclude Include="Singleton.h" />
  130. <ClInclude Include="StatusBarMsgWnd.h" />
  131. <ClInclude Include="stdafx.h" />
  132. <ClInclude Include="SVN.h" />
  133. <ClInclude Include="SVNPool.h" />
  134. + <ClInclude Include="timesupport.h" />
  135. <ClInclude Include="UpdateDlg.h" />
  136. <ClInclude Include="URLDlg.h" />
  137. <ClInclude Include="UrlInfo.h" />
  138. Index: src/CommitMonitor.vcxproj.filters
  139. ===================================================================
  140. --- src/CommitMonitor.vcxproj.filters (revision 605)
  141. +++ src/CommitMonitor.vcxproj.filters (working copy)
  142. @@ -243,6 +243,15 @@
  143. <ClCompile Include="..\ext\scintilla\win32\PlatWin.cxx">
  144. <Filter>Scintilla</Filter>
  145. </ClCompile>
  146. + <ClCompile Include="Http.cpp">
  147. + <Filter>Source Files</Filter>
  148. + </ClCompile>
  149. + <ClCompile Include="ReviewBoardXml.cpp">
  150. + <Filter>Source Files</Filter>
  151. + </ClCompile>
  152. + <ClCompile Include="timesupport.cc">
  153. + <Filter>Source Files</Filter>
  154. + </ClCompile>
  155. </ItemGroup>
  156. <ItemGroup>
  157. <ClInclude Include="AboutDlg.h">
  158. @@ -479,6 +488,21 @@
  159. <ClInclude Include="..\ext\scintilla\lexlib\LexerModule.h">
  160. <Filter>Scintilla</Filter>
  161. </ClInclude>
  162. + <ClInclude Include="Http.h">
  163. + <Filter>Header Files</Filter>
  164. + </ClInclude>
  165. + <ClInclude Include="ReviewBoardXml.h">
  166. + <Filter>Header Files</Filter>
  167. + </ClInclude>
  168. + <ClInclude Include="Convert.h">
  169. + <Filter>Header Files</Filter>
  170. + </ClInclude>
  171. + <ClInclude Include="Singleton.h">
  172. + <Filter>Header Files</Filter>
  173. + </ClInclude>
  174. + <ClInclude Include="timesupport.h">
  175. + <Filter>Header Files</Filter>
  176. + </ClInclude>
  177. </ItemGroup>
  178. <ItemGroup>
  179. <None Include="Resources\about.ico">
  180. Index: src/Convert.h
  181. ===================================================================
  182. --- src/Convert.h (revision 0)
  183. +++ src/Convert.h (revision 0)
  184. @@ -0,0 +1,164 @@
  185. +#ifndef CONVERT_H
  186. +#define CONVERT_H
  187. +
  188. +#include "stdafx.h"
  189. +#include <string>
  190. +#include <sstream>
  191. +
  192. +template <class out_type, class in_value>
  193. +class Converter
  194. +{
  195. +public:
  196. + static out_type convert(const in_value &t)
  197. + {
  198. + std::wstringstream stream;
  199. + stream << t;
  200. +
  201. + out_type result;
  202. + stream >> result;
  203. + return result;
  204. + }
  205. +};
  206. +
  207. +template <>
  208. +class Converter<std::wstring, std::wstring>
  209. +{
  210. +public:
  211. + static std::wstring convert(const std::wstring &t)
  212. + {
  213. + return t;
  214. + }
  215. +};
  216. +
  217. +template <class out_type, class in_value>
  218. +out_type convert(const in_value &t)
  219. +{
  220. + return Converter<out_type, in_value>::convert(t);
  221. +}
  222. +
  223. +#define BOM8A 0xEF
  224. +#define BOM8B 0xBB
  225. +#define BOM8C 0xBF
  226. +
  227. +/*
  228. +* Copyright (c) 2009, Helios (helios.vmg@gmail.com)
  229. +* All rights reserved.
  230. +*
  231. +* Redistribution and use in source and binary forms, with or without
  232. +* modification, are permitted provided that the following conditions are met:
  233. +* * Redistributions of source code must retain the above copyright notice,
  234. +* this list of conditions and the following disclaimer.
  235. +* * Redistributions in binary form must reproduce the above copyright
  236. +* notice, this list of conditions and the following disclaimer in the
  237. +* documentation and/or other materials provided with the distribution.
  238. +*
  239. +* THIS SOFTWARE IS PROVIDED BY HELIOS "AS IS" AND ANY EXPRESS OR IMPLIED
  240. +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  241. +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
  242. +* EVENT SHALL HELIOS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  243. +* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  244. +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  245. +* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  246. +* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  247. +* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
  248. +* OF THE POSSIBILITY OF SUCH DAMAGE.
  249. +*/
  250. +
  251. +typedef unsigned char uchar;
  252. +
  253. +/*
  254. +string: a UTF-8-encoded C string (nul terminated)
  255. +Return value: a wchar_t C string.
  256. +
  257. +The function handles memory allocation on its own.
  258. +
  259. +Limitations: Only handles the range [U+0000;U+FFFF], higher code points are
  260. +changed to &#039;?&#039;.
  261. +
  262. +Assumptions: sizeof(wchar_t)>=2
  263. +*/
  264. +wchar_t *UTF8_to_WChar(const char *string){
  265. + long b=0,
  266. + c=0;
  267. + if ((uchar)string[0]==BOM8A && (uchar)string[1]==BOM8B && (uchar)string[2]==BOM8C)
  268. + string+=3;
  269. + for (const char *a=string;*a;a++)
  270. + if (((uchar)*a)<128 || (*a&192)==192)
  271. + c++;
  272. + wchar_t *res=new wchar_t[c+1];
  273. + res[c]=0;
  274. + for (uchar *a=(uchar*)string;*a;a++){
  275. + if (!(*a&128))
  276. + //Byte represents an ASCII character. Direct copy will do.
  277. + res[b]=*a;
  278. + else if ((*a&192)==128)
  279. + //Byte is the middle of an encoded character. Ignore.
  280. + continue;
  281. + else if ((*a&224)==192)
  282. + //Byte represents the start of an encoded character in the range
  283. + //U+0080 to U+07FF
  284. + res[b]=((*a&31)<<6)|a[1]&63;
  285. + else if ((*a&240)==224)
  286. + //Byte represents the start of an encoded character in the range
  287. + //U+07FF to U+FFFF
  288. + res[b]=((*a&15)<<12)|((a[1]&63)<<6)|a[2]&63;
  289. + else if ((*a&248)==240){
  290. + //Byte represents the start of an encoded character beyond the
  291. + //U+FFFF limit of 16-bit integers
  292. + res[b]=&#039;?&#039;;
  293. + }
  294. + b++;
  295. + }
  296. + return res;
  297. +}
  298. +
  299. +//Do not call me.
  300. +long getUTF8size(const wchar_t *string){
  301. + if (!string)
  302. + return 0;
  303. + long res=0;
  304. + for (;*string;string++){
  305. + if (*string<0x80)
  306. + res++;
  307. + else if (*string<0x800)
  308. + res+=2;
  309. + else
  310. + res+=3;
  311. + }
  312. + return res;
  313. +}
  314. +
  315. +/*
  316. +string: a wchar_t C string (nul terminated)
  317. +Return value: a UTF-8-encoded C string.
  318. +
  319. +The function handles memory allocation on its own.
  320. +
  321. +Limitations: Only handles the range [U+0000;U+FFFF], higher code points are
  322. +changed to &#039;?&#039;.
  323. +
  324. +Assumptions: sizeof(wchar_t)>=2
  325. +*/
  326. +char *WChar_to_UTF8(const wchar_t *string){
  327. + long fSize=getUTF8size(string);
  328. + char *res=new char[fSize+1];
  329. + res[fSize]=0;
  330. + if (!string)
  331. + return res;
  332. + long b=0;
  333. + for (;*string;string++,b++){
  334. + if (*string<0x80)
  335. + res[b]=(char)*string;
  336. + else if (*string<0x800){
  337. + res[b++]=(*string>>6)|192;
  338. + res[b]=*string&63|128;
  339. + }else{
  340. + res[b++]=(*string>>12)|224;
  341. + res[b++]=((*string&4095)>>6)|128;
  342. + res[b]=*string&63|128;
  343. + }
  344. + }
  345. + return res;
  346. +}
  347. +
  348. +#endif
  349. Index: src/HiddenWindow.cpp
  350. ===================================================================
  351. --- src/HiddenWindow.cpp (revision 605)
  352. +++ src/HiddenWindow.cpp (working copy)
  353. @@ -34,7 +34,7 @@
  354. #include "OptionsDlg.h"
  355. #include "version.h"
  356. #include "SnarlInterface.h"
  357. -
  358. +#include "ReviewBoardXml.h"
  359. #include <cctype>
  360. #include <regex>
  361. using namespace std;
  362. @@ -345,6 +345,7 @@
  363. {
  364. // find the number of unread items
  365. int nNewCommits = 0;
  366. + int nNewReviews = gReviewBoard.GetUnreadReviews();
  367. const map<wstring, CUrlInfo> * pRead = m_UrlInfos.GetReadOnlyData();
  368. for (map<wstring, CUrlInfo>::const_iterator it = pRead->begin(); it != pRead->end(); ++it)
  369. {
  370. @@ -360,12 +361,39 @@
  371. m_SystemTray.hWnd = *this;
  372. m_SystemTray.uFlags = NIF_MESSAGE | NIF_TIP;
  373. m_SystemTray.uCallbackMessage = COMMITMONITOR_TASKBARCALLBACK;
  374. - if (nNewCommits)
  375. + if (nNewCommits || nNewReviews)
  376. {
  377. - if (nNewCommits == 1)
  378. - _stprintf_s(m_SystemTray.szTip, _countof(m_SystemTray.szTip), _T("CommitMonitor - %d new commit"), nNewCommits);
  379. + if(nNewReviews == 0)
  380. + {
  381. + if (nNewCommits == 1)
  382. + _stprintf_s(m_SystemTray.szTip, _countof(m_SystemTray.szTip), _T("CommitMonitor - %d new commit"), nNewCommits);
  383. + else
  384. + _stprintf_s(m_SystemTray.szTip, _countof(m_SystemTray.szTip), _T("CommitMonitor - %d new commits"), nNewCommits);
  385. + }
  386. else
  387. - _stprintf_s(m_SystemTray.szTip, _countof(m_SystemTray.szTip), _T("CommitMonitor - %d new commits"), nNewCommits);
  388. + {
  389. + if (nNewCommits == 0)
  390. + {
  391. + if (nNewReviews == 1)
  392. + _stprintf_s(m_SystemTray.szTip, _countof(m_SystemTray.szTip), _T("CommitMonitor - %d new review"), nNewReviews);
  393. + else
  394. + _stprintf_s(m_SystemTray.szTip, _countof(m_SystemTray.szTip), _T("CommitMonitor - %d new reviews"), nNewReviews);
  395. + }
  396. + else if(nNewCommits == 1)
  397. + {
  398. + if (nNewReviews == 1)
  399. + _stprintf_s(m_SystemTray.szTip, _countof(m_SystemTray.szTip), _T("CommitMonitor - %d new commit, %d new review"), nNewCommits, nNewReviews);
  400. + else
  401. + _stprintf_s(m_SystemTray.szTip, _countof(m_SystemTray.szTip), _T("CommitMonitor - %d new commit, %d new reviews"), nNewCommits, nNewReviews);
  402. + }
  403. + else
  404. + {
  405. + if (nNewReviews == 1)
  406. + _stprintf_s(m_SystemTray.szTip, _countof(m_SystemTray.szTip), _T("CommitMonitor - %d new commits, %d new review"), nNewCommits, nNewReviews);
  407. + else
  408. + _stprintf_s(m_SystemTray.szTip, _countof(m_SystemTray.szTip), _T("CommitMonitor - %d new commits, %d new reviews"), nNewCommits, nNewReviews);
  409. + }
  410. + }
  411. }
  412. else
  413. _tcscpy_s(m_SystemTray.szTip, _countof(m_SystemTray.szTip), _T("CommitMonitor"));
  414. @@ -679,7 +707,8 @@
  415. // won&#039;t block anything for too long.
  416. map<wstring,CUrlInfo> urlinfoReadOnly = *m_UrlInfos.GetReadOnlyData();
  417. m_UrlInfos.ReleaseReadOnlyData();
  418. -
  419. + bool needReviewUpdate = false;
  420. + int newreviews = 0;
  421. TCHAR infotextbuf[1024];
  422. TRACE(_T("monitor thread started\n"));
  423. const map<wstring,CUrlInfo> * pUrlInfoReadOnly = &urlinfoReadOnly;
  424. @@ -695,6 +724,13 @@
  425. }
  426. if (((it->second.monitored)&&((it->second.lastchecked + (mit*60)) < currenttime))||(m_UrlToWorkOn.size()))
  427. {
  428. + if(!needReviewUpdate && gReviewBoard.IsUrlMonitored(it->second.url))
  429. + {
  430. + needReviewUpdate = true;
  431. + // do update here, we&#039;re not currently blocking anything (update will ofc...)
  432. + newreviews = gReviewBoard.DoUpdate();
  433. + }
  434. +
  435. m_UrlToWorkOn.clear();
  436. if ((it->second.errNr == SVN_ERR_RA_NOT_AUTHORIZED)&&(!it->second.error.empty()))
  437. continue; // don&#039;t check if the last error was &#039;not authorized&#039;
  438. @@ -740,9 +776,9 @@
  439. }
  440. if (!m_bRun)
  441. continue;
  442. - if (headrev > it->second.lastcheckedrev)
  443. + if (headrev > it->second.lastcheckedrev || gReviewBoard.GetNewReviewsForUrl(it->second.url))
  444. {
  445. - TRACE(_T("%s has updates! Last checked revision was %ld, HEAD revision is %ld\n"), it->first.c_str(), it->second.lastcheckedrev, headrev);
  446. + TRACE(_T("%s has updates! Last checked revision was %ld, HEAD revision is %ld or new reviews\n"), it->first.c_str(), it->second.lastcheckedrev, headrev);
  447. if (m_hMainDlg)
  448. {
  449. _stprintf_s(infotextbuf, 1024, _T("getting log for %s"), it->first.c_str());
  450. @@ -750,7 +786,7 @@
  451. }
  452. int nNewCommits = 0; // commits without ignored ones
  453. int nTotalNewCommits = 0; // all commits, including ignored ones
  454. - if (pSCCS->GetLog(it->second.accurevRepo, it->first, headrev, it->second.lastcheckedrev + 1))
  455. + if ((headrev <= it->second.lastcheckedrev && gReviewBoard.GetNewReviewsForUrl(it->second.url)) || pSCCS->GetLog(it->second.accurevRepo, it->first, headrev, it->second.lastcheckedrev + 1))
  456. {
  457. TRACE(_T("log fetched for %s\n"), it->first.c_str());
  458. if (!m_bRun)
  459. @@ -772,112 +808,115 @@
  460.  
  461. wstring sPopupText;
  462. bool hadError = !it->second.error.empty();
  463. - for (map<svn_revnum_t,SCCSLogEntry>::iterator logit = pSCCS->m_logs.begin(); logit != pSCCS->m_logs.end(); ++logit)
  464. + if(headrev > it->second.lastcheckedrev)
  465. {
  466. - // again, only block for a short time
  467. - map<wstring,CUrlInfo> * pWrite = m_UrlInfos.GetWriteData();
  468. - map<wstring,CUrlInfo>::iterator writeIt = pWrite->find(it->first);
  469. - bool bIgnore = false;
  470. - bool bEntryExists = false;
  471. - if (writeIt != pWrite->end())
  472. + for (map<svn_revnum_t,SCCSLogEntry>::iterator logit = pSCCS->m_logs.begin(); logit != pSCCS->m_logs.end(); ++logit)
  473. {
  474. - map<svn_revnum_t,SCCSLogEntry>::iterator existIt = writeIt->second.logentries.find(logit->first);
  475. - bEntryExists = existIt != writeIt->second.logentries.end();
  476. - bool readState = false;
  477. - if (bEntryExists)
  478. - readState = existIt->second.read;
  479. - logit->second.read = readState;
  480. + // again, only block for a short time
  481. + map<wstring,CUrlInfo> * pWrite = m_UrlInfos.GetWriteData();
  482. + map<wstring,CUrlInfo>::iterator writeIt = pWrite->find(it->first);
  483. + bool bIgnore = false;
  484. + bool bEntryExists = false;
  485. + if (writeIt != pWrite->end())
  486. + {
  487. + map<svn_revnum_t,SCCSLogEntry>::iterator existIt = writeIt->second.logentries.find(logit->first);
  488. + bEntryExists = existIt != writeIt->second.logentries.end();
  489. + bool readState = false;
  490. + if (bEntryExists)
  491. + readState = existIt->second.read;
  492. + logit->second.read = readState;
  493.  
  494. - writeIt->second.logentries[logit->first] = logit->second;
  495. + writeIt->second.logentries[logit->first] = logit->second;
  496.  
  497. - if (!bEntryExists)
  498. - {
  499. - wstring author1 = logit->second.author;
  500. - std::transform(author1.begin(), author1.end(), author1.begin(), std::tolower);
  501. + if (!bEntryExists)
  502. + {
  503. + wstring author1 = logit->second.author;
  504. + std::transform(author1.begin(), author1.end(), author1.begin(), std::tolower);
  505.  
  506. - if (writeIt->second.includeUsers.size() > 0)
  507. - {
  508. - wstring s1 = writeIt->second.includeUsers;
  509. + if (writeIt->second.includeUsers.size() > 0)
  510. + {
  511. + wstring s1 = writeIt->second.includeUsers;
  512. + std::transform(s1.begin(), s1.end(), s1.begin(), std::tolower);
  513. + CAppUtils::SearchReplace(s1, _T("\r\n"), _T("\n"));
  514. + vector<wstring> includeVector = CAppUtils::tokenize_str(s1, _T("\n"));
  515. + bool bInclude = false;
  516. + for (vector<wstring>::iterator it = includeVector.begin(); it != includeVector.end(); ++it)
  517. + {
  518. + if (author1.compare(*it) == 0)
  519. + {
  520. + bInclude = true;
  521. + break;
  522. + }
  523. + }
  524. + bIgnore = !bInclude;
  525. + }
  526. +
  527. + wstring s1 = writeIt->second.ignoreUsers;
  528. std::transform(s1.begin(), s1.end(), s1.begin(), std::tolower);
  529. CAppUtils::SearchReplace(s1, _T("\r\n"), _T("\n"));
  530. - vector<wstring> includeVector = CAppUtils::tokenize_str(s1, _T("\n"));
  531. - bool bInclude = false;
  532. - for (vector<wstring>::iterator it = includeVector.begin(); it != includeVector.end(); ++it)
  533. + vector<wstring> ignoreVector = CAppUtils::tokenize_str(s1, _T("\n"));
  534. + for (vector<wstring>::iterator it = ignoreVector.begin(); it != ignoreVector.end(); ++it)
  535. {
  536. if (author1.compare(*it) == 0)
  537. {
  538. - bInclude = true;
  539. + bIgnore = true;
  540. break;
  541. }
  542. }
  543. - bIgnore = !bInclude;
  544. - }
  545. -
  546. - wstring s1 = writeIt->second.ignoreUsers;
  547. - std::transform(s1.begin(), s1.end(), s1.begin(), std::tolower);
  548. - CAppUtils::SearchReplace(s1, _T("\r\n"), _T("\n"));
  549. - vector<wstring> ignoreVector = CAppUtils::tokenize_str(s1, _T("\n"));
  550. - for (vector<wstring>::iterator it = ignoreVector.begin(); it != ignoreVector.end(); ++it)
  551. - {
  552. - if (author1.compare(*it) == 0)
  553. + nTotalNewCommits++;
  554. + if (!bIgnore)
  555. {
  556. - bIgnore = true;
  557. - break;
  558. + bNewEntries = true;
  559. + nNewCommits++;
  560. }
  561. + else
  562. + // set own commit as already read
  563. + writeIt->second.logentries[logit->first].read = true;
  564. }
  565. - nTotalNewCommits++;
  566. - if (!bIgnore)
  567. - {
  568. - bNewEntries = true;
  569. - nNewCommits++;
  570. - }
  571. - else
  572. - // set own commit as already read
  573. - writeIt->second.logentries[logit->first].read = true;
  574. + writeIt->second.error.clear();
  575. }
  576. - writeIt->second.error.clear();
  577. - }
  578. - bNeedsSaving = true;
  579. - m_UrlInfos.ReleaseWriteData();
  580. - if (!m_bRun)
  581. - continue;
  582. - // popup info text
  583. - if ((!bIgnore)&&(!bEntryExists))
  584. - {
  585. - if (!sPopupText.empty())
  586. - sPopupText += _T(", ");
  587. - sPopupText += logit->second.author;
  588. - }
  589. - if ((!it->second.disallowdiffs)&&(it->second.fetchdiffs))
  590. - {
  591. - TCHAR buf[4096];
  592. - // first, find a name where to store the diff for that revision
  593. - _stprintf_s(buf, 4096, _T("%s_%ld.diff"), it->second.name.c_str(), logit->first);
  594. - wstring diffFileName = CAppUtils::GetAppDataDir();
  595. - diffFileName += _T("/");
  596. - diffFileName += wstring(buf);
  597. - // do we already have that diff?
  598. - if (!PathFileExists(diffFileName.c_str()))
  599. + bNeedsSaving = true;
  600. + m_UrlInfos.ReleaseWriteData();
  601. + if (!m_bRun)
  602. + continue;
  603. + // popup info text
  604. + if ((!bIgnore)&&(!bEntryExists))
  605. {
  606. - // get the diff
  607. - if (m_hMainDlg)
  608. + if (!sPopupText.empty())
  609. + sPopupText += _T(", ");
  610. + sPopupText += logit->second.author;
  611. + }
  612. + if ((!it->second.disallowdiffs)&&(it->second.fetchdiffs))
  613. + {
  614. + TCHAR buf[4096];
  615. + // first, find a name where to store the diff for that revision
  616. + _stprintf_s(buf, 4096, _T("%s_%ld.diff"), it->second.name.c_str(), logit->first);
  617. + wstring diffFileName = CAppUtils::GetAppDataDir();
  618. + diffFileName += _T("/");
  619. + diffFileName += wstring(buf);
  620. + // do we already have that diff?
  621. + if (!PathFileExists(diffFileName.c_str()))
  622. {
  623. - _stprintf_s(infotextbuf, 1024, _T("getting diff for %s, revision %ld"), it->first.c_str(), logit->first);
  624. - SendMessage(*this, COMMITMONITOR_INFOTEXT, 0, (LPARAM)infotextbuf);
  625. + // get the diff
  626. + if (m_hMainDlg)
  627. + {
  628. + _stprintf_s(infotextbuf, 1024, _T("getting diff for %s, revision %ld"), it->first.c_str(), logit->first);
  629. + SendMessage(*this, COMMITMONITOR_INFOTEXT, 0, (LPARAM)infotextbuf);
  630. + }
  631. + if (!pSCCS->Diff(it->first, logit->first, logit->first-1, logit->first, true, true, false, wstring(), false, diffFileName, wstring()))
  632. + {
  633. + TRACE(_T("Diff not fetched for %s, revision %ld because of an error\n"), it->first.c_str(), logit->first);
  634. + DeleteFile(diffFileName.c_str());
  635. + }
  636. + else
  637. + TRACE(_T("Diff fetched for %s, revision %ld\n"), it->first.c_str(), logit->first);
  638. + if (!m_bRun)
  639. + break;
  640. }
  641. - if (!pSCCS->Diff(it->first, logit->first, logit->first-1, logit->first, true, true, false, wstring(), false, diffFileName, wstring()))
  642. - {
  643. - TRACE(_T("Diff not fetched for %s, revision %ld because of an error\n"), it->first.c_str(), logit->first);
  644. - DeleteFile(diffFileName.c_str());
  645. - }
  646. - else
  647. - TRACE(_T("Diff fetched for %s, revision %ld\n"), it->first.c_str(), logit->first);
  648. - if (!m_bRun)
  649. - break;
  650. }
  651. }
  652. }
  653. - if ((it->second.lastcheckedrobots + (60*60*24*2)) < currenttime)
  654. + if ((headrev > it->second.lastcheckedrev) && (it->second.lastcheckedrobots + (60*60*24*2)) < currenttime)
  655. {
  656. wstring sRobotsURL = it->first;
  657. sRobotsURL += _T("/svnrobots.txt");
  658. @@ -984,7 +1023,8 @@
  659. m_UrlInfos.ReleaseWriteData();
  660. }
  661. // prepare notification strings
  662. - if ((bNewEntries)||(!hadError && !it->second.error.empty()))
  663. + int nNewReviews = gReviewBoard.GetNewReviewsForUrl(it->second.url);
  664. + if ((nNewReviews > 0)||(bNewEntries)||(!hadError && !it->second.error.empty()))
  665. {
  666. popupData data;
  667. TCHAR sTitle[1024] = {0};
  668. @@ -996,10 +1036,31 @@
  669. else
  670. {
  671. data.sProject = it->second.name;
  672. - if (nNewCommits == 1)
  673. - _stprintf_s(sTitle, 1024, _T("%s\nhas %d new commit"), it->second.name.c_str(), nNewCommits);
  674. - else
  675. - _stprintf_s(sTitle, 1024, _T("%s\nhas %d new commits"), it->second.name.c_str(), nNewCommits);
  676. + if (nNewCommits == 0)
  677. + {
  678. + if(nNewReviews == 1)
  679. + _stprintf_s(sTitle, 1024, _T("%s\nhas %d new review"), it->second.name.c_str(), nNewReviews);
  680. + else
  681. + _stprintf_s(sTitle, 1024, _T("%s\nhas %d new reviews"), it->second.name.c_str(), nNewReviews);
  682. + }
  683. + else if (nNewCommits == 1)
  684. + {
  685. + if(nNewReviews == 0)
  686. + _stprintf_s(sTitle, 1024, _T("%s\nhas %d new commit"), it->second.name.c_str(), nNewCommits);
  687. + else if(nNewReviews == 1)
  688. + _stprintf_s(sTitle, 1024, _T("%s\nhas %d new commit, %d new review"), it->second.name.c_str(), nNewCommits, nNewReviews);
  689. + else
  690. + _stprintf_s(sTitle, 1024, _T("%s\nhas %d new commit, %d new reviews"), it->second.name.c_str(), nNewCommits, nNewReviews);
  691. + }
  692. + else
  693. + {
  694. + if(nNewReviews == 0)
  695. + _stprintf_s(sTitle, 1024, _T("%s\nhas %d new commits"), it->second.name.c_str(), nNewCommits);
  696. + else if(nNewReviews == 1)
  697. + _stprintf_s(sTitle, 1024, _T("%s\nhas %d new commits, %d new review"), it->second.name.c_str(), nNewCommits, nNewReviews);
  698. + else
  699. + _stprintf_s(sTitle, 1024, _T("%s\nhas %d new commits, %d new reviews"), it->second.name.c_str(), nNewCommits, nNewReviews);
  700. + }
  701. }
  702. data.sText = sPopupText;
  703. data.sTitle = wstring(sTitle);
  704. Index: src/Http.cpp
  705. ===================================================================
  706. --- src/Http.cpp (revision 0)
  707. +++ src/Http.cpp (revision 0)
  708. @@ -0,0 +1,112 @@
  709. +#include "stdafx.h"
  710. +#include "Http.h"
  711. +
  712. +#pragma warning( push )
  713. +#pragma warning( disable: 4244 )
  714. +#include "Poco/Net/HTTPClientSession.h"
  715. +#include "Poco/Net/HTTPRequest.h"
  716. +#include "Poco/Net/HTTPResponse.h"
  717. +#include "Poco/StreamCopier.h"
  718. +#include "Poco/Path.h"
  719. +#include "Poco/URI.h"
  720. +#include "Poco/Exception.h"
  721. +#pragma warning( pop )
  722. +#include <string>
  723. +#include <sstream>
  724. +
  725. +using Poco::Net::HTTPClientSession;
  726. +using Poco::Net::HTTPRequest;
  727. +using Poco::Net::HTTPResponse;
  728. +using Poco::Net::HTTPMessage;
  729. +using Poco::StreamCopier;
  730. +using Poco::Path;
  731. +using Poco::URI;
  732. +using Poco::Exception;
  733. +
  734. +using namespace std;
  735. +
  736. +
  737. +Http::Http() : inited(false)
  738. +{
  739. +}
  740. +
  741. +Http::~Http()
  742. +{
  743. + if(inited)
  744. + {
  745. + }
  746. +}
  747. +
  748. +bool Http::Init(const string& host)
  749. +{
  750. + string url = "http://";
  751. + url += host;
  752. + url += "/";
  753. + URI uri(url);
  754. + std::string path(uri.getPathAndQuery());
  755. + if (path.empty()) path = "/";
  756. +
  757. +
  758. + session.setHost(uri.getHost());
  759. + session.setPort(uri.getPort());
  760. + inited = true;
  761. + return true;
  762. +}
  763. +
  764. +void Http::GetPageText(const string& path, stringstream& sstrm)
  765. +{
  766. + if(!inited)
  767. + return;
  768. + HTTPRequest req(HTTPRequest::HTTP_GET, path, HTTPMessage::HTTP_1_1);
  769. + req.set("Accept","text/x-patch");
  770. + session.sendRequest(req);
  771. + HTTPResponse res;
  772. + std::istream& rs = session.receiveResponse(res);
  773. + StreamCopier::copyStream(rs, sstrm);
  774. +}
  775. +
  776. +void Http::GetPageXml(const string& path, stringstream& sstrm)
  777. +{
  778. + if(!inited)
  779. + return;
  780. + HTTPRequest req(HTTPRequest::HTTP_GET, path, HTTPMessage::HTTP_1_1);
  781. + req.set("Accept","application/xml");
  782. + session.sendRequest(req);
  783. + HTTPResponse res;
  784. + std::istream& rs = session.receiveResponse(res);
  785. + StreamCopier::copyStream(rs, sstrm);
  786. +}
  787. +
  788. +void Http::GetPageXml(const string& path, string& str)
  789. +{
  790. + stringstream sstrm;
  791. + GetPageXml(path,sstrm);
  792. + str = sstrm.str();
  793. +}
  794. +
  795. +void Http::GetPageText(const string& path, string& str)
  796. +{
  797. + stringstream sstrm;
  798. + GetPageText(path,sstrm);
  799. + str = sstrm.str();
  800. +}
  801. +
  802. +void Http::GetPageXml(const string& path, char *& buf, int& len)
  803. +{
  804. + string str;
  805. + GetPageXml(path,str);
  806. + len = str.length();
  807. + buf = new char[len+1];
  808. + memcpy(buf,str.c_str(),len * sizeof(char));
  809. + buf[len]=0;
  810. +}
  811. +
  812. +void Http::GetPageText(const std::string& path, char *& buf, int& len)
  813. +{
  814. + string str;
  815. + GetPageText(path,str);
  816. + len = str.length();
  817. + buf = new char[len+1];
  818. + memcpy(buf,str.c_str(),len * sizeof(char));
  819. + buf[len]=0;
  820. +}
  821. Index: src/Http.h
  822. ===================================================================
  823. --- src/Http.h (revision 0)
  824. +++ src/Http.h (revision 0)
  825. @@ -0,0 +1,32 @@
  826. +#ifndef HTTP_H__
  827. +#define HTTP_H__
  828. +
  829. +#include "stdafx.h"
  830. +
  831. +#include "Poco/Net/HTTPClientSession.h"
  832. +#include <iosfwd>
  833. +#include "Singleton.h"
  834. +
  835. +class Http
  836. +{
  837. + friend class Singleton<Http>;
  838. + Poco::Net::HTTPClientSession session;
  839. + bool inited;
  840. +private:
  841. + Http();
  842. +public:
  843. + ~Http();
  844. + bool Init(const std::string& host);
  845. + void GetPageXml(const std::string& path, std::stringstream& sstrm);
  846. + void GetPageText(const std::string& path, std::stringstream& sstrm);
  847. + void GetPageXml(const std::string& path, std::string& str);
  848. + void GetPageText(const std::string& path, std::string& str);
  849. + void GetPageXml(const std::string& path, char *& buf_you_must_delete_this_when_done_with_processing, int& len);
  850. + void GetPageText(const std::string& path, char *& buf_you_must_delete_this_when_done_with_processing, int& len);
  851. +};
  852. +
  853. +
  854. +#define gHTTP (*(Singleton<Http>::GetInstance()))
  855. +#define gHTTP_Cleanup Singleton<Http>::Cleanup()
  856. +
  857. +#endif
  858. Index: src/MainDlg.cpp
  859. ===================================================================
  860. --- src/MainDlg.cpp (revision 605)
  861. +++ src/MainDlg.cpp (working copy)
  862. @@ -32,6 +32,8 @@
  863. #include <cctype>
  864. #include <regex>
  865.  
  866. +#include "ReviewBoardXml.h"
  867. +
  868. #pragma comment(lib, "uxtheme.lib")
  869.  
  870. #define FILTERBOXHEIGHT 20
  871. @@ -52,6 +54,7 @@
  872. , m_hImgList(NULL)
  873. , m_bNewerVersionAvailable(false)
  874. , m_refreshNeeded(false)
  875. + , m_commitsradio(true)
  876. {
  877. m_hParent = hParent;
  878. // use the default GUI font, create a copy of it and
  879. @@ -219,6 +222,11 @@
  880. m_hListControl = ::GetDlgItem(*this, IDC_MONITOREDURLS);
  881. m_hLogMsgControl = ::GetDlgItem(*this, IDC_LOGINFO);
  882. m_hFilterControl = ::GetDlgItem(*this, IDC_FILTERSTRING);
  883. + m_hCommitsRadio = ::GetDlgItem(*this, IDC_RADIO2);
  884. + m_hReviewsRadio = ::GetDlgItem(*this, IDC_RADIO3);
  885. +
  886. + ::CheckRadioButton(hwndDlg, IDC_RADIO2, IDC_RADIO3, m_commitsradio ? IDC_RADIO2 : IDC_RADIO3);
  887. +
  888. ::SendMessage(m_hTreeControl, TVM_SETUNICODEFORMAT, 1, 0);
  889.  
  890. SetWindowTheme(m_hListControl, L"Explorer", NULL);
  891. @@ -278,6 +286,9 @@
  892. GetClientRect(*this, &rect);
  893. m_bottommarg = rect.bottom - m_bottommarg;
  894.  
  895. + m_radiossize = 160;
  896. +
  897. +
  898. // subclass the tree view control to intercept the WM_SETFOCUS messages
  899. m_oldTreeWndProc = (WNDPROC)SetWindowLongPtr(m_hTreeControl, GWLP_WNDPROC, (LONG)TreeProc);
  900. SetWindowLongPtr(m_hTreeControl, GWLP_USERDATA, (LONG)this);
  901. @@ -446,7 +457,7 @@
  902. case WM_GETMINMAXINFO:
  903. {
  904. MINMAXINFO * mmi = (MINMAXINFO*)lParam;
  905. - mmi->ptMinTrackSize.x = m_xSliderPos + 100;
  906. + mmi->ptMinTrackSize.x = m_xSliderPos + 265;
  907. mmi->ptMinTrackSize.y = m_ySliderPos + 100;
  908. return 0;
  909. }
  910. @@ -503,7 +514,7 @@
  911. tv.hParent = FindParentTreeNode(it->first);
  912. tv.hInsertAfter = TVI_SORT;
  913. tv.itemex.mask = TVIF_TEXT|TVIF_PARAM|TVIF_STATE|TVIF_IMAGE|TVIF_SELECTEDIMAGE;
  914. - WCHAR * str = new WCHAR[it->second.name.size()+10];
  915. + WCHAR * str = new WCHAR[it->second.name.size()+30];
  916. // find out if there are some unread entries
  917. int unread = 0;
  918. for (map<svn_revnum_t,SCCSLogEntry>::const_iterator logit = it->second.logentries.begin(); logit != it->second.logentries.end(); ++logit)
  919. @@ -520,13 +531,14 @@
  920. tv.itemex.hItem = directItem;
  921. tv.itemex.stateMask = TVIS_SELECTED|TVIS_BOLD|TVIF_IMAGE|TVIF_SELECTEDIMAGE;
  922. tv.itemex.pszText = str;
  923. - tv.itemex.cchTextMax = it->second.name.size()+9;
  924. + tv.itemex.cchTextMax = it->second.name.size()+29;
  925. TreeView_GetItem(m_hTreeControl, &tv.itemex);
  926. wstring sTitle = wstring(str);
  927. bool bRequiresUpdate = false;
  928. - if (unread)
  929. + int unreadreviews = gReviewBoard.GetUnreadReviewsForUrl(it->second.url);
  930. + if (unread || unreadreviews)
  931. {
  932. - _stprintf_s(str, it->second.name.size()+10, _T("%s (%d)"), it->second.name.c_str(), unread);
  933. + _stprintf_s(str, it->second.name.size()+30, _T("%s (%d + %d)"), it->second.name.c_str(), unread, unreadreviews);
  934. tv.itemex.state |= TVIS_BOLD;
  935. tv.itemex.stateMask = TVIS_BOLD;
  936. }
  937. @@ -551,7 +563,7 @@
  938. tv.itemex.iImage = 4;
  939. tv.itemex.iSelectedImage = 4;
  940. }
  941. - else if (unread)
  942. + else if (unread || unreadreviews)
  943. {
  944. bRequiresUpdate = tv.itemex.iImage != 3;
  945. tv.itemex.iImage = 3;
  946. @@ -594,9 +606,10 @@
  947. }
  948. else
  949. {
  950. - if (unread)
  951. + int unreadreviews = gReviewBoard.GetUnreadReviewsForUrl(it->second.url);
  952. + if (unread || unreadreviews)
  953. {
  954. - _stprintf_s(str, it->second.name.size()+10, _T("%s (%d)"), it->second.name.c_str(), unread);
  955. + _stprintf_s(str, it->second.name.size()+30, _T("%s (%d + %d)"), it->second.name.c_str(), unread, unreadreviews);
  956. tv.itemex.state = TVIS_BOLD;
  957. tv.itemex.stateMask = TVIS_BOLD;
  958. }
  959. @@ -619,7 +632,7 @@
  960. tv.itemex.iImage = 4;
  961. tv.itemex.iSelectedImage = 4;
  962. }
  963. - else if (unread)
  964. + else if (unread || unreadreviews)
  965. {
  966. tv.itemex.iImage = 3;
  967. tv.itemex.iSelectedImage = 3;
  968. @@ -820,7 +833,7 @@
  969. {
  970. HMENU hMenu = NULL;
  971. wstring tsvninstalled = CAppUtils::GetTSVNPath();
  972. - if (tsvninstalled.empty())
  973. + if (tsvninstalled.empty() || !m_commitsradio)
  974. hMenu = ::LoadMenu(hResource, MAKEINTRESOURCE(IDR_LISTPOPUP));
  975. else
  976. hMenu = ::LoadMenu(hResource, MAKEINTRESOURCE(IDR_LISTPOPUPTSVN));
  977. @@ -882,48 +895,101 @@
  978. ListView_GetItem(m_hListControl, &item);
  979. if (item.state & LVIS_SELECTED)
  980. {
  981. - SCCSLogEntry * pLogEntry = (SCCSLogEntry*)item.lParam;
  982. - if (pLogEntry)
  983. + if(m_commitsradio)
  984. {
  985. - // set the entry as unread
  986. - if (pLogEntry->read)
  987. + SCCSLogEntry * pLogEntry = (SCCSLogEntry*)item.lParam;
  988. + if (pLogEntry)
  989. {
  990. - pLogEntry->read = false;
  991. - // refresh the name of the tree item to indicate the new
  992. - // number of unread log messages
  993. - // e.g. instead of &#039;TortoiseSVN (2)&#039;, show now &#039;TortoiseSVN (3)&#039;
  994. - if (pRead->find(*(wstring*)itemex.lParam) != pRead->end())
  995. + // set the entry as unread
  996. + if (pLogEntry->read)
  997. {
  998. - const CUrlInfo * uinfo = &pRead->find(*(wstring*)itemex.lParam)->second;
  999. - // count the number of unread messages
  1000. - int unread = 0;
  1001. - for (map<svn_revnum_t,SCCSLogEntry>::const_iterator it = uinfo->logentries.begin(); it != uinfo->logentries.end(); ++it)
  1002. + pLogEntry->read = false;
  1003. + // refresh the name of the tree item to indicate the new
  1004. + // number of unread log messages
  1005. + // e.g. instead of &#039;TortoiseSVN (2)&#039;, show now &#039;TortoiseSVN (3)&#039;
  1006. + if (pRead->find(*(wstring*)itemex.lParam) != pRead->end())
  1007. {
  1008. - if (!it->second.read)
  1009. - unread++;
  1010. + const CUrlInfo * uinfo = &pRead->find(*(wstring*)itemex.lParam)->second;
  1011. + // count the number of unread messages
  1012. + int unread = 0;
  1013. + for (map<svn_revnum_t,SCCSLogEntry>::const_iterator it = uinfo->logentries.begin(); it != uinfo->logentries.end(); ++it)
  1014. + {
  1015. + if (!it->second.read)
  1016. + unread++;
  1017. + }
  1018. + WCHAR * str = new WCHAR[uinfo->name.size()+30];
  1019. + int unreadreviews = gReviewBoard.GetUnreadReviewsForUrl(uinfo->url);
  1020. + if (unread || unreadreviews)
  1021. + {
  1022. + _stprintf_s(str, uinfo->name.size()+30, _T("%s (%d + %d)"), uinfo->name.c_str(), unread, unreadreviews);
  1023. + itemex.state = TVIS_BOLD;
  1024. + itemex.stateMask = TVIS_BOLD;
  1025. + itemex.iImage = 3;
  1026. + itemex.iSelectedImage = 3;
  1027. + }
  1028. + else
  1029. + {
  1030. + _stprintf_s(str, uinfo->name.size()+10, _T("%s"), uinfo->name.c_str());
  1031. + itemex.state = 0;
  1032. + itemex.stateMask = TVIS_BOLD;
  1033. + itemex.iImage = 2;
  1034. + itemex.iSelectedImage = 2;
  1035. + }
  1036. +
  1037. + itemex.pszText = str;
  1038. + itemex.mask = TVIF_TEXT|TVIF_STATE|TVIF_IMAGE|TVIF_SELECTEDIMAGE;
  1039. + m_refreshNeeded = true;
  1040. + TreeView_SetItem(m_hTreeControl, &itemex);
  1041. }
  1042. - WCHAR * str = new WCHAR[uinfo->name.size()+10];
  1043. - if (unread)
  1044. + }
  1045. + }
  1046. + }
  1047. + else
  1048. + {
  1049. + ReviewBoardReview * pLogEntry = (ReviewBoardReview*)item.lParam;
  1050. + if (pLogEntry)
  1051. + {
  1052. + // set the entry as unread
  1053. + if (pLogEntry->read)
  1054. + {
  1055. + gReviewBoard.ReviewSetRead(pLogEntry->reviewid,false);
  1056. + // refresh the name of the tree item to indicate the new
  1057. + // number of unread log messages
  1058. + // e.g. instead of &#039;TortoiseSVN (2)&#039;, show now &#039;TortoiseSVN (3)&#039;
  1059. + if (pRead->find(*(wstring*)itemex.lParam) != pRead->end())
  1060. {
  1061. - _stprintf_s(str, uinfo->name.size()+10, _T("%s (%d)"), uinfo->name.c_str(), unread);
  1062. - itemex.state = TVIS_BOLD;
  1063. - itemex.stateMask = TVIS_BOLD;
  1064. - itemex.iImage = 3;
  1065. - itemex.iSelectedImage = 3;
  1066. + const CUrlInfo * uinfo = &pRead->find(*(wstring*)itemex.lParam)->second;
  1067. + // count the number of unread messages
  1068. + int unread = 0;
  1069. + for (map<svn_revnum_t,SCCSLogEntry>::const_iterator it = uinfo->logentries.begin(); it != uinfo->logentries.end(); ++it)
  1070. + {
  1071. + if (!it->second.read)
  1072. + unread++;
  1073. + }
  1074. + WCHAR * str = new WCHAR[uinfo->name.size()+30];
  1075. + int unreadreviews = gReviewBoard.GetUnreadReviewsForUrl(uinfo->url);
  1076. + if (unread || unreadreviews)
  1077. + {
  1078. + _stprintf_s(str, uinfo->name.size()+30, _T("%s (%d + %d)"), uinfo->name.c_str(), unread, unreadreviews);
  1079. + itemex.state = TVIS_BOLD;
  1080. + itemex.stateMask = TVIS_BOLD;
  1081. + itemex.iImage = 3;
  1082. + itemex.iSelectedImage = 3;
  1083. + }
  1084. + else
  1085. + {
  1086. + _stprintf_s(str, uinfo->name.size()+10, _T("%s"), uinfo->name.c_str());
  1087. + itemex.state = 0;
  1088. + itemex.stateMask = TVIS_BOLD;
  1089. + itemex.iImage = 2;
  1090. + itemex.iSelectedImage = 2;
  1091. + }
  1092. +
  1093. + itemex.pszText = str;
  1094. + itemex.mask = TVIF_TEXT|TVIF_STATE|TVIF_IMAGE|TVIF_SELECTEDIMAGE;
  1095. + m_refreshNeeded = true;
  1096. + TreeView_SetItem(m_hTreeControl, &itemex);
  1097. }
  1098. - else
  1099. - {
  1100. - _stprintf_s(str, uinfo->name.size()+10, _T("%s"), uinfo->name.c_str());
  1101. - itemex.state = 0;
  1102. - itemex.stateMask = TVIS_BOLD;
  1103. - itemex.iImage = 2;
  1104. - itemex.iSelectedImage = 2;
  1105. - }
  1106. -
  1107. - itemex.pszText = str;
  1108. - itemex.mask = TVIF_TEXT|TVIF_STATE|TVIF_IMAGE|TVIF_SELECTEDIMAGE;
  1109. - m_refreshNeeded = true;
  1110. - TreeView_SetItem(m_hTreeControl, &itemex);
  1111. }
  1112. }
  1113. }
  1114. @@ -1094,13 +1160,36 @@
  1115. if (pRead->find(*(wstring*)itemex.lParam) != pRead->end())
  1116. {
  1117. const CUrlInfo * info = &pRead->find(*(wstring*)itemex.lParam)->second;
  1118. - if ((info)&&(!info->webviewer.empty()))
  1119. + if (!m_commitsradio || ((info)&&(!info->webviewer.empty())))
  1120. {
  1121. // replace "%revision" with the new HEAD revision
  1122. wstring tag(_T("%revision"));
  1123. wstring commandline = info->webviewer;
  1124. + wstring reviewurl;
  1125. + if(!m_commitsradio)
  1126. + {
  1127. + // find the revision
  1128. + LVITEM item = {0};
  1129. + int nItemCount = ListView_GetItemCount(m_hListControl);
  1130. + for (int i=0; i<nItemCount; ++i)
  1131. + {
  1132. + item.mask = LVIF_PARAM|LVIF_STATE;
  1133. + item.stateMask = LVIS_SELECTED;
  1134. + item.iItem = i;
  1135. + ListView_GetItem(m_hListControl, &item);
  1136. + if (item.state & LVIS_SELECTED)
  1137. + {
  1138. + ReviewBoardReview * pLogEntry = (ReviewBoardReview*)item.lParam;
  1139. + if (pLogEntry)
  1140. + {
  1141. + reviewurl = pLogEntry->Url();
  1142. + break;
  1143. + }
  1144. + }
  1145. + }
  1146. + }
  1147. wstring::iterator it_begin = search(commandline.begin(), commandline.end(), tag.begin(), tag.end());
  1148. - if (it_begin != commandline.end())
  1149. + if (m_commitsradio && it_begin != commandline.end())
  1150. {
  1151. // find the revision
  1152. LVITEM item = {0};
  1153. @@ -1143,8 +1232,19 @@
  1154. wstring::iterator it_end= it_begin + tag.size();
  1155. commandline.replace(it_begin, it_end, info->name);
  1156. }
  1157. - if (!commandline.empty())
  1158. + if (!m_commitsradio && !reviewurl.empty())
  1159. {
  1160. + tag = _T("api/review-requests");
  1161. + it_begin = search(reviewurl.begin(), reviewurl.end(), tag.begin(), tag.end());
  1162. + if (it_begin != reviewurl.end())
  1163. + {
  1164. + wstring::iterator it_end= it_begin + tag.size();
  1165. + reviewurl.replace(it_begin, it_end, TEXT("r"));
  1166. + }
  1167. + ShellExecute(*this, _T("open"), reviewurl.c_str(), NULL, NULL, SW_SHOWNORMAL);
  1168. + }
  1169. + else if (!commandline.empty())
  1170. + {
  1171. ShellExecute(*this, _T("open"), commandline.c_str(), NULL, NULL, SW_SHOWNORMAL);
  1172. }
  1173. }
  1174. @@ -1168,12 +1268,15 @@
  1175. {
  1176. dlg.SetInfo(&pRead->find(*(wstring*)itemex.lParam)->second);
  1177. wstring origurl = dlg.GetInfo()->url;
  1178. + dlg.SetReviewMonitored(gReviewBoard.IsUrlMonitored(origurl));
  1179. if (id == ID_POPUP_ADDPROJECTWITHTEMPLATE)
  1180. dlg.ClearForTemplate();
  1181. m_pURLInfos->ReleaseReadOnlyData();
  1182. if (dlg.DoModal(hResource, IDD_URLCONFIG, *this) == IDOK)
  1183. {
  1184. CUrlInfo * inf = dlg.GetInfo();
  1185. + bool monitor = dlg.GetReviewMonitored();
  1186. + bool needsaverev = false;
  1187. if ((inf)&&inf->name.size())
  1188. {
  1189. inf->errNr = 0;
  1190. @@ -1182,9 +1285,25 @@
  1191. if ((inf) && (inf->url.size()) && ((origurl.compare(inf->url)) || (id == ID_MAIN_EDIT)))
  1192. {
  1193. if (id == ID_MAIN_EDIT)
  1194. + {
  1195. pWrite->erase(*(wstring*)itemex.lParam);
  1196. + gReviewBoard.RemoveBranchToMonitor(origurl);
  1197. + needsaverev = true;
  1198. + }
  1199. (*pWrite)[inf->url] = *inf;
  1200. }
  1201. + if(monitor && !gReviewBoard.IsUrlMonitored(inf->url))
  1202. + {
  1203. + gReviewBoard.AddBranchToMonitor(inf->url);
  1204. + needsaverev = true;
  1205. + }
  1206. + else if(!monitor && gReviewBoard.IsUrlMonitored(inf->url))
  1207. + {
  1208. + gReviewBoard.RemoveBranchToMonitor(inf->url);
  1209. + needsaverev = true;
  1210. + }
  1211. + if(needsaverev)
  1212. + gReviewBoard.Save();
  1213. m_pURLInfos->Save();
  1214. m_pURLInfos->ReleaseWriteData();
  1215. RefreshURLTree(false);
  1216. @@ -1291,52 +1410,106 @@
  1217. ListView_GetItem(m_hListControl, &item);
  1218. if (item.state & LVIS_SELECTED)
  1219. {
  1220. - SCCSLogEntry * pLogEntry = (SCCSLogEntry*)item.lParam;
  1221. - if (pLogEntry)
  1222. + if(m_commitsradio)
  1223. {
  1224. - // get the info to put on the clipboard
  1225. - _stprintf_s(tempBuf, 1024, _T("Revision: %ld\nAuthor: %s\nDate: %s\nMessage:\n"),
  1226. - pLogEntry->revision,
  1227. - pLogEntry->author.c_str(),
  1228. - CAppUtils::ConvertDate(pLogEntry->date).c_str());
  1229. - sClipboardData += tempBuf;
  1230. - sClipboardData += pLogEntry->message;
  1231. - sClipboardData += _T("\n-------------------------------\n");
  1232. - // now add all changed paths, one path per line
  1233. - for (map<std::wstring, SCCSLogChangedPaths>::const_iterator it = pLogEntry->m_changedPaths.begin(); it != pLogEntry->m_changedPaths.end(); ++it)
  1234. + SCCSLogEntry * pLogEntry = (SCCSLogEntry*)item.lParam;
  1235. + if (pLogEntry)
  1236. {
  1237. - // action
  1238. - sClipboardData += it->second.action;
  1239. - bool mods = false;
  1240. - if ((it->second.text_modified == svn_tristate_true)||(it->second.props_modified == svn_tristate_true))
  1241. + // get the info to put on the clipboard
  1242. + _stprintf_s(tempBuf, 1024, _T("Revision: %ld\nAuthor: %s\nDate: %s\nMessage:\n"),
  1243. + pLogEntry->revision,
  1244. + pLogEntry->author.c_str(),
  1245. + CAppUtils::ConvertDate(pLogEntry->date).c_str());
  1246. + sClipboardData += tempBuf;
  1247. + sClipboardData += pLogEntry->message;
  1248. + sClipboardData += _T("\n-------------------------------\n");
  1249. + // now add all changed paths, one path per line
  1250. + for (map<std::wstring, SCCSLogChangedPaths>::const_iterator it = pLogEntry->m_changedPaths.begin(); it != pLogEntry->m_changedPaths.end(); ++it)
  1251. {
  1252. - mods = true;
  1253. + // action
  1254. + sClipboardData += it->second.action;
  1255. + bool mods = false;
  1256. + if ((it->second.text_modified == svn_tristate_true)||(it->second.props_modified == svn_tristate_true))
  1257. + {
  1258. + mods = true;
  1259. + }
  1260. + if (mods)
  1261. + sClipboardData += L"(";
  1262. + if (it->second.text_modified == svn_tristate_true)
  1263. + sClipboardData += L"T";
  1264. + else if (mods)
  1265. + sClipboardData += L" ";
  1266. + if (it->second.props_modified == svn_tristate_true)
  1267. + sClipboardData += L"P";
  1268. + else if (mods)
  1269. + sClipboardData += L" ";
  1270. + if (mods)
  1271. + sClipboardData += L")";
  1272. + sClipboardData += _T(" : ");
  1273. + sClipboardData += it->first;
  1274. + sClipboardData += _T(" ");
  1275. + if (!it->second.copyfrom_path.empty())
  1276. + {
  1277. + sClipboardData += _T("(copied from: ");
  1278. + sClipboardData += it->second.copyfrom_path;
  1279. + sClipboardData += _T(", revision ");
  1280. + _stprintf_s(tempBuf, 1024, _T("%ld)\n"), it->second.copyfrom_revision);
  1281. + sClipboardData += wstring(tempBuf);
  1282. + }
  1283. + else
  1284. + sClipboardData += _T("\n\n");
  1285. }
  1286. - if (mods)
  1287. - sClipboardData += L"(";
  1288. - if (it->second.text_modified == svn_tristate_true)
  1289. - sClipboardData += L"T";
  1290. - else if (mods)
  1291. - sClipboardData += L" ";
  1292. - if (it->second.props_modified == svn_tristate_true)
  1293. - sClipboardData += L"P";
  1294. - else if (mods)
  1295. - sClipboardData += L" ";
  1296. - if (mods)
  1297. - sClipboardData += L")";
  1298. - sClipboardData += _T(" : ");
  1299. - sClipboardData += it->first;
  1300. - sClipboardData += _T(" ");
  1301. - if (!it->second.copyfrom_path.empty())
  1302. + }
  1303. + }
  1304. + else
  1305. + {
  1306. + ReviewBoardReview * pLogEntry = (ReviewBoardReview*)item.lParam;
  1307. + if (pLogEntry)
  1308. + {
  1309. + // get the info to put on the clipboard
  1310. + _stprintf_s(tempBuf, 1024, _T("Review id: %s\nAuthor: %s\nDate: %s\nMessage:\n"),
  1311. + pLogEntry->Reviewid().c_str(),
  1312. + pLogEntry->Author().c_str(),
  1313. + pLogEntry->Date().c_str());
  1314. + sClipboardData += tempBuf;
  1315. + sClipboardData += pLogEntry->Message();
  1316. + sClipboardData += _T("\n-------------------------------\n");
  1317. +/* // now add all changed paths, one path per line
  1318. + for (map<std::wstring, SCCSLogChangedPaths>::const_iterator it = pLogEntry->m_changedPaths.begin(); it != pLogEntry->m_changedPaths.end(); ++it)
  1319. {
  1320. - sClipboardData += _T("(copied from: ");
  1321. - sClipboardData += it->second.copyfrom_path;
  1322. - sClipboardData += _T(", revision ");
  1323. - _stprintf_s(tempBuf, 1024, _T("%ld)\n"), it->second.copyfrom_revision);
  1324. - sClipboardData += wstring(tempBuf);
  1325. - }
  1326. - else
  1327. - sClipboardData += _T("\n\n");
  1328. + // action
  1329. + sClipboardData += it->second.action;
  1330. + bool mods = false;
  1331. + if ((it->second.text_modified == svn_tristate_true)||(it->second.props_modified == svn_tristate_true))
  1332. + {
  1333. + mods = true;
  1334. + }
  1335. + if (mods)
  1336. + sClipboardData += L"(";
  1337. + if (it->second.text_modified == svn_tristate_true)
  1338. + sClipboardData += L"T";
  1339. + else if (mods)
  1340. + sClipboardData += L" ";
  1341. + if (it->second.props_modified == svn_tristate_true)
  1342. + sClipboardData += L"P";
  1343. + else if (mods)
  1344. + sClipboardData += L" ";
  1345. + if (mods)
  1346. + sClipboardData += L")";
  1347. + sClipboardData += _T(" : ");
  1348. + sClipboardData += it->first;
  1349. + sClipboardData += _T(" ");
  1350. + if (!it->second.copyfrom_path.empty())
  1351. + {
  1352. + sClipboardData += _T("(copied from: ");
  1353. + sClipboardData += it->second.copyfrom_path;
  1354. + sClipboardData += _T(", revision ");
  1355. + _stprintf_s(tempBuf, 1024, _T("%ld)\n"), it->second.copyfrom_revision);
  1356. + sClipboardData += wstring(tempBuf);
  1357. + }
  1358. + else
  1359. + sClipboardData += _T("\n\n");
  1360. + }*/
  1361. }
  1362. }
  1363. }
  1364. @@ -1391,6 +1564,13 @@
  1365. }
  1366. }
  1367. break;
  1368. + case IDC_RADIO2:
  1369. + case IDC_RADIO3:
  1370. + {
  1371. + m_commitsradio = !m_commitsradio;
  1372. + TreeItemSelected(m_hTreeControl, TreeView_GetSelection(m_hTreeControl));
  1373. + }
  1374. + break;
  1375. default:
  1376. return 0;
  1377. }
  1378. @@ -1447,222 +1627,298 @@
  1379. ListView_GetItem(m_hListControl, &item);
  1380. if (item.state & LVIS_SELECTED)
  1381. {
  1382. - SCCSLogEntry * pLogEntry = (SCCSLogEntry*)item.lParam;
  1383. + if(m_commitsradio)
  1384. + {
  1385. + SCCSLogEntry * pLogEntry = (SCCSLogEntry*)item.lParam;
  1386.  
  1387. - // Switch how the diff is done in SVN / Accurev
  1388. - switch(pUrlInfo->sccs) {
  1389. - default:
  1390. - case CUrlInfo::SCCS_SVN:
  1391. - {
  1392. - // find the diff name
  1393. - const CUrlInfo * pInfo = &pRead->find(*(wstring*)itemex.lParam)->second;
  1394. - // in case the project name has &#039;path&#039; chars in it, we have to remove those first
  1395. - _stprintf_s(buf, 4096, _T("%s_%ld.diff"), CAppUtils::ConvertName(pInfo->name).c_str(), pLogEntry->revision);
  1396. - wstring diffFileName = CAppUtils::GetDataDir();
  1397. - diffFileName += _T("\\");
  1398. - diffFileName += wstring(buf);
  1399. - // construct a title for the diff viewer
  1400. - _stprintf_s(buf, 4096, _T("%s, revision %ld"), pInfo->name.c_str(), pLogEntry->revision);
  1401. - wstring title = wstring(buf);
  1402. - // start the diff viewer
  1403. - wstring cmd;
  1404. - wstring tsvninstalled = CAppUtils::GetTSVNPath();
  1405. - wstring sVer = CAppUtils::GetVersionStringFromExe(tsvninstalled.c_str());
  1406. - if ((bUseTSVN)&&(!tsvninstalled.empty())&&(_tstoi(sVer.substr(3, 4).c_str()) > 4))
  1407. - {
  1408. - // yes, we have TSVN installed
  1409. - // call TortoiseProc to do the diff for us
  1410. - cmd = _T("\"");
  1411. - cmd += wstring(tsvninstalled);
  1412. - cmd += _T("\" /command:diff /path:\"");
  1413. - cmd += pInfo->url;
  1414. - cmd += _T("\" /startrev:");
  1415. + // Switch how the diff is done in SVN / Accurev
  1416. + switch(pUrlInfo->sccs) {
  1417. + default:
  1418. + case CUrlInfo::SCCS_SVN:
  1419. + {
  1420. + // find the diff name
  1421. + const CUrlInfo * pInfo = &pRead->find(*(wstring*)itemex.lParam)->second;
  1422. + // in case the project name has &#039;path&#039; chars in it, we have to remove those first
  1423. + _stprintf_s(buf, 4096, _T("%s_%ld.diff"), CAppUtils::ConvertName(pInfo->name).c_str(), pLogEntry->revision);
  1424. + wstring diffFileName = CAppUtils::GetDataDir();
  1425. + diffFileName += _T("\\");
  1426. + diffFileName += wstring(buf);
  1427. + // construct a title for the diff viewer
  1428. + _stprintf_s(buf, 4096, _T("%s, revision %ld"), pInfo->name.c_str(), pLogEntry->revision);
  1429. + wstring title = wstring(buf);
  1430. + // start the diff viewer
  1431. + wstring cmd;
  1432. + wstring tsvninstalled = CAppUtils::GetTSVNPath();
  1433. + wstring sVer = CAppUtils::GetVersionStringFromExe(tsvninstalled.c_str());
  1434. + if ((bUseTSVN)&&(!tsvninstalled.empty())&&(_tstoi(sVer.substr(3, 4).c_str()) > 4))
  1435. + {
  1436. + // yes, we have TSVN installed
  1437. + // call TortoiseProc to do the diff for us
  1438. + cmd = _T("\"");
  1439. + cmd += wstring(tsvninstalled);
  1440. + cmd += _T("\" /command:diff /path:\"");
  1441. + cmd += pInfo->url;
  1442. + cmd += _T("\" /startrev:");
  1443.  
  1444. - TCHAR numBuf[100] = {0};
  1445. - _stprintf_s(numBuf, 100, _T("%ld"), pLogEntry->revision-1);
  1446. - cmd += numBuf;
  1447. - cmd += _T(" /endrev:");
  1448. - _stprintf_s(numBuf, 100, _T("%ld"), pLogEntry->revision);
  1449. - cmd += numBuf;
  1450. - CAppUtils::LaunchApplication(cmd);
  1451. - }
  1452. - else
  1453. - {
  1454. - TCHAR apppath[4096];
  1455. - GetModuleFileName(NULL, apppath, 4096);
  1456. - CRegStdString diffViewer = CRegStdString(_T("Software\\CommitMonitor\\DiffViewer"));
  1457. - if (wstring(diffViewer).empty())
  1458. - {
  1459. - cmd = apppath;
  1460. - cmd += _T(" /patchfile:\"");
  1461. + TCHAR numBuf[100] = {0};
  1462. + _stprintf_s(numBuf, 100, _T("%ld"), pLogEntry->revision-1);
  1463. + cmd += numBuf;
  1464. + cmd += _T(" /endrev:");
  1465. + _stprintf_s(numBuf, 100, _T("%ld"), pLogEntry->revision);
  1466. + cmd += numBuf;
  1467. + CAppUtils::LaunchApplication(cmd);
  1468. }
  1469. else
  1470. {
  1471. - cmd = (wstring)diffViewer;
  1472. - cmd += _T(" \"");
  1473. - }
  1474. - cmd += diffFileName;
  1475. - cmd += _T("\"");
  1476. - if (wstring(diffViewer).empty())
  1477. - {
  1478. - cmd += _T(" /title:\"");
  1479. - cmd += title;
  1480. - cmd += _T("\"");
  1481. - }
  1482. - // Check if the diff file exists. If it doesn&#039;t, we have to fetch
  1483. - // the diff first
  1484. - if (!PathFileExists(diffFileName.c_str()))
  1485. - {
  1486. - // fetch the diff
  1487. - SVN svn;
  1488. - svn.SetAuthInfo(pInfo->username, pInfo->password);
  1489. - CProgressDlg progDlg;
  1490. - svn.SetAndClearProgressInfo(&progDlg);
  1491. - progDlg.SetTitle(_T("Fetching Diff"));
  1492. - TCHAR dispbuf[MAX_PATH] = {0};
  1493. - _stprintf_s(dispbuf, MAX_PATH, _T("fetching diff of revision %ld"), pLogEntry->revision);
  1494. - progDlg.SetLine(1, dispbuf);
  1495. - progDlg.SetShowProgressBar(false);
  1496. - progDlg.ShowModeless(*this);
  1497. - progDlg.SetLine(1, dispbuf);
  1498. - progDlg.SetProgress(3, 100); // set some dummy progress
  1499. - CRegStdString diffParams = CRegStdString(_T("Software\\CommitMonitor\\DiffParameters"));
  1500. - if (!svn.Diff(pInfo->url, pLogEntry->revision, pLogEntry->revision-1, pLogEntry->revision, true, true, false, diffParams, false, diffFileName, wstring()))
  1501. + TCHAR apppath[4096];
  1502. + GetModuleFileName(NULL, apppath, 4096);
  1503. + CRegStdString diffViewer = CRegStdString(_T("Software\\CommitMonitor\\DiffViewer"));
  1504. + if (wstring(diffViewer).empty())
  1505. {
  1506. - progDlg.Stop();
  1507. - if (svn.Err->apr_err != SVN_ERR_CANCELLED)
  1508. - ::MessageBox(*this, svn.GetLastErrorMsg().c_str(), _T("CommitMonitor"), MB_ICONERROR);
  1509. - DeleteFile(diffFileName.c_str());
  1510. + cmd = apppath;
  1511. + cmd += _T(" /patchfile:\"");
  1512. }
  1513. else
  1514. {
  1515. - TRACE(_T("Diff fetched for %s, revision %ld\n"), pInfo->url.c_str(), pLogEntry->revision);
  1516. - progDlg.Stop();
  1517. + cmd = (wstring)diffViewer;
  1518. + cmd += _T(" \"");
  1519. }
  1520. + cmd += diffFileName;
  1521. + cmd += _T("\"");
  1522. + if (wstring(diffViewer).empty())
  1523. + {
  1524. + cmd += _T(" /title:\"");
  1525. + cmd += title;
  1526. + cmd += _T("\"");
  1527. + }
  1528. + // Check if the diff file exists. If it doesn&#039;t, we have to fetch
  1529. + // the diff first
  1530. + if (!PathFileExists(diffFileName.c_str()))
  1531. + {
  1532. + // fetch the diff
  1533. + SVN svn;
  1534. + svn.SetAuthInfo(pInfo->username, pInfo->password);
  1535. + CProgressDlg progDlg;
  1536. + svn.SetAndClearProgressInfo(&progDlg);
  1537. + progDlg.SetTitle(_T("Fetching Diff"));
  1538. + TCHAR dispbuf[MAX_PATH] = {0};
  1539. + _stprintf_s(dispbuf, MAX_PATH, _T("fetching diff of revision %ld"), pLogEntry->revision);
  1540. + progDlg.SetLine(1, dispbuf);
  1541. + progDlg.SetShowProgressBar(false);
  1542. + progDlg.ShowModeless(*this);
  1543. + progDlg.SetLine(1, dispbuf);
  1544. + progDlg.SetProgress(3, 100); // set some dummy progress
  1545. + CRegStdString diffParams = CRegStdString(_T("Software\\CommitMonitor\\DiffParameters"));
  1546. + if (!svn.Diff(pInfo->url, pLogEntry->revision, pLogEntry->revision-1, pLogEntry->revision, true, true, false, diffParams, false, diffFileName, wstring()))
  1547. + {
  1548. + progDlg.Stop();
  1549. + if (svn.Err->apr_err != SVN_ERR_CANCELLED)
  1550. + ::MessageBox(*this, svn.GetLastErrorMsg().c_str(), _T("CommitMonitor"), MB_ICONERROR);
  1551. + DeleteFile(diffFileName.c_str());
  1552. + }
  1553. + else
  1554. + {
  1555. + TRACE(_T("Diff fetched for %s, revision %ld\n"), pInfo->url.c_str(), pLogEntry->revision);
  1556. + progDlg.Stop();
  1557. + }
  1558. + }
  1559. + if (PathFileExists(diffFileName.c_str()))
  1560. + CAppUtils::LaunchApplication(cmd);
  1561. }
  1562. - if (PathFileExists(diffFileName.c_str()))
  1563. - CAppUtils::LaunchApplication(cmd);
  1564. - }
  1565. - }
  1566. - break;
  1567. + }
  1568. + break;
  1569.  
  1570.  
  1571. - case CUrlInfo::SCCS_ACCUREV:
  1572. - {
  1573. - /* Accurev &#039;diff&#039; cannot be used as it mutex locks itself to only allow diffing of one
  1574. - * file at a time... how typical. Therefore we &#039;pop&#039; (get copies) of the correct versions
  1575. - * of each file and then diff the directories :)
  1576. - * TODO: Somehow hold onto and delete the temporary dirs when commit monitor is closed */
  1577. - CRegStdString accurevExe = CRegStdString(_T("Software\\CommitMonitor\\AccurevExe"));
  1578. + case CUrlInfo::SCCS_ACCUREV:
  1579. + {
  1580. + /* Accurev &#039;diff&#039; cannot be used as it mutex locks itself to only allow diffing of one
  1581. + * file at a time... how typical. Therefore we &#039;pop&#039; (get copies) of the correct versions
  1582. + * of each file and then diff the directories :)
  1583. + * TODO: Somehow hold onto and delete the temporary dirs when commit monitor is closed */
  1584. + CRegStdString accurevExe = CRegStdString(_T("Software\\CommitMonitor\\AccurevExe"));
  1585.  
  1586. - wchar_t transactionNo[64];
  1587. - _itow_s(pLogEntry->revision, transactionNo, 10);
  1588. + wchar_t transactionNo[64];
  1589. + _itow_s(pLogEntry->revision, transactionNo, 10);
  1590.  
  1591. - wstring uuid;
  1592. - CAppUtils::CreateUUIDString(uuid);
  1593. + wstring uuid;
  1594. + CAppUtils::CreateUUIDString(uuid);
  1595.  
  1596. - wstring newTempPath = wstring(origTempPath);
  1597. - newTempPath.append(_T("\\"));
  1598. - newTempPath.append(uuid);
  1599. - wstring sLatestRev(transactionNo);
  1600. - wstring sBasisRev = _T("BASIS");
  1601. - wstring latestDir(newTempPath + _T("\\") + sLatestRev);
  1602. - wstring basisDir(newTempPath + _T("\\") + sBasisRev);
  1603. - CreateDirectory(newTempPath.c_str(), NULL);
  1604. - CreateDirectory(latestDir.c_str(), NULL);
  1605. - CreateDirectory(basisDir.c_str(), NULL);
  1606. + wstring newTempPath = wstring(origTempPath);
  1607. + newTempPath.append(_T("\\"));
  1608. + newTempPath.append(uuid);
  1609. + wstring sLatestRev(transactionNo);
  1610. + wstring sBasisRev = _T("BASIS");
  1611. + wstring latestDir(newTempPath + _T("\\") + sLatestRev);
  1612. + wstring basisDir(newTempPath + _T("\\") + sBasisRev);
  1613. + CreateDirectory(newTempPath.c_str(), NULL);
  1614. + CreateDirectory(latestDir.c_str(), NULL);
  1615. + CreateDirectory(basisDir.c_str(), NULL);
  1616.  
  1617. - // For each file that should be diffed
  1618. - for (map<std::wstring,SCCSLogChangedPaths>::const_iterator it = pLogEntry->m_changedPaths.begin(); it != pLogEntry->m_changedPaths.end(); ++it)
  1619. - {
  1620. - // Parse the file and file revision from the stored URL
  1621. - wstring rawPath = it->first;
  1622. + // For each file that should be diffed
  1623. + for (map<std::wstring,SCCSLogChangedPaths>::const_iterator it = pLogEntry->m_changedPaths.begin(); it != pLogEntry->m_changedPaths.end(); ++it)
  1624. + {
  1625. + // Parse the file and file revision from the stored URL
  1626. + wstring rawPath = it->first;
  1627.  
  1628. - int lastBracket = rawPath.rfind(L" (");
  1629. - rawPath.erase(lastBracket, wstring::npos);
  1630. - int lastForwardSlash = rawPath.rfind(L"/");
  1631. - wstring sLatestAccuRevision(rawPath);
  1632. - sLatestAccuRevision.erase(0, lastForwardSlash+1);
  1633. - int iAccuRevision = _wtoi(sLatestAccuRevision.c_str());
  1634. - wchar_t basisRevisionNo[64];
  1635. - _itow_s(iAccuRevision-1, basisRevisionNo, 10);
  1636. - wstring sBasisAccuRevision(basisRevisionNo);
  1637. + int lastBracket = rawPath.rfind(L" (");
  1638. + rawPath.erase(lastBracket, wstring::npos);
  1639. + int lastForwardSlash = rawPath.rfind(L"/");
  1640. + wstring sLatestAccuRevision(rawPath);
  1641. + sLatestAccuRevision.erase(0, lastForwardSlash+1);
  1642. + int iAccuRevision = _wtoi(sLatestAccuRevision.c_str());
  1643. + wchar_t basisRevisionNo[64];
  1644. + _itow_s(iAccuRevision-1, basisRevisionNo, 10);
  1645. + wstring sBasisAccuRevision(basisRevisionNo);
  1646.  
  1647. - wstring finalPath(rawPath);
  1648. - int lastSpace = rawPath.rfind(L" ");
  1649. - finalPath.erase(lastSpace, wstring::npos);
  1650. + wstring finalPath(rawPath);
  1651. + int lastSpace = rawPath.rfind(L" ");
  1652. + finalPath.erase(lastSpace, wstring::npos);
  1653.  
  1654. - // Can&#039;t diff unless there is a version to diff against :)
  1655. - if (iAccuRevision >= 1) {
  1656. + // Can&#039;t diff unless there is a version to diff against :)
  1657. + if (iAccuRevision >= 1) {
  1658.  
  1659. - // Check out the latest file
  1660. - // Build the accurev command line
  1661. - for (int i=0; i<2; i++) {
  1662. - wstring accurevPopCmd;
  1663. - wstring rev;
  1664. - wstring dir;
  1665. + // Check out the latest file
  1666. + // Build the accurev command line
  1667. + for (int i=0; i<2; i++) {
  1668. + wstring accurevPopCmd;
  1669. + wstring rev;
  1670. + wstring dir;
  1671.  
  1672. - switch (i) {
  1673. - default:
  1674. - case 0:
  1675. - rev = sLatestAccuRevision;
  1676. - dir = latestDir;
  1677. - break;
  1678. - case 1:
  1679. - rev = sBasisAccuRevision;
  1680. - dir = basisDir;
  1681. - break;
  1682. - }
  1683. + switch (i) {
  1684. + default:
  1685. + case 0:
  1686. + rev = sLatestAccuRevision;
  1687. + dir = latestDir;
  1688. + break;
  1689. + case 1:
  1690. + rev = sBasisAccuRevision;
  1691. + dir = basisDir;
  1692. + break;
  1693. + }
  1694.  
  1695. - /* If this is the basis version, and there is none, since the file was added, then break
  1696. - * so we only check out the new version. This will then be shown in the directory compare :) */
  1697. - if ((i == 1) && (iAccuRevision == 1)) break;
  1698. + /* If this is the basis version, and there is none, since the file was added, then break
  1699. + * so we only check out the new version. This will then be shown in the directory compare :) */
  1700. + if ((i == 1) && (iAccuRevision == 1)) break;
  1701.  
  1702. - accurevPopCmd.append(_T("\""));
  1703. - accurevPopCmd.append(wstring(accurevExe));
  1704. - accurevPopCmd.append(_T("\" pop -O -R -v "));
  1705. - accurevPopCmd.append(pUrlInfo->url);
  1706. - accurevPopCmd.append(_T("/"));
  1707. - accurevPopCmd.append(rev);
  1708. - accurevPopCmd.append(_T(" -L \""));
  1709. - accurevPopCmd.append(dir);
  1710. - accurevPopCmd.append(_T("\" \""));
  1711. - accurevPopCmd.append(finalPath);
  1712. - accurevPopCmd.append(_T("\""));
  1713. + accurevPopCmd.append(_T("\""));
  1714. + accurevPopCmd.append(wstring(accurevExe));
  1715. + accurevPopCmd.append(_T("\" pop -O -R -v "));
  1716. + accurevPopCmd.append(pUrlInfo->url);
  1717. + accurevPopCmd.append(_T("/"));
  1718. + accurevPopCmd.append(rev);
  1719. + accurevPopCmd.append(_T(" -L \""));
  1720. + accurevPopCmd.append(dir);
  1721. + accurevPopCmd.append(_T("\" \""));
  1722. + accurevPopCmd.append(finalPath);
  1723. + accurevPopCmd.append(_T("\""));
  1724.  
  1725. - // Run accurev to perform the pop command
  1726. - CAppUtils::LaunchApplication(accurevPopCmd, true, true, true);
  1727. + // Run accurev to perform the pop command
  1728. + CAppUtils::LaunchApplication(accurevPopCmd, true, true, true);
  1729. + }
  1730. +
  1731. + }
  1732. }
  1733.  
  1734. - }
  1735. - }
  1736. + CRegStdString diffCmd = CRegStdString(_T("Software\\CommitMonitor\\AccurevDiffCmd"));
  1737. + wstring finalDiffCmd;
  1738.  
  1739. - CRegStdString diffCmd = CRegStdString(_T("Software\\CommitMonitor\\AccurevDiffCmd"));
  1740. - wstring finalDiffCmd;
  1741. + // Build the final diff command
  1742. + finalDiffCmd.append(wstring(diffCmd));
  1743.  
  1744. - // Build the final diff command
  1745. - finalDiffCmd.append(wstring(diffCmd));
  1746. + // Find and replace "%OLD"
  1747. + int pos = finalDiffCmd.find(_T("%OLD"));
  1748. + finalDiffCmd.replace(pos, 4, sBasisRev, 0, sBasisRev.size());
  1749.  
  1750. - // Find and replace "%OLD"
  1751. - int pos = finalDiffCmd.find(_T("%OLD"));
  1752. - finalDiffCmd.replace(pos, 4, sBasisRev, 0, sBasisRev.size());
  1753. + // Find and replace "%NEW"
  1754. + pos = finalDiffCmd.find(_T("%NEW"));
  1755. + finalDiffCmd.replace(pos, 4, sLatestRev, 0, sLatestRev.size());
  1756.  
  1757. - // Find and replace "%NEW"
  1758. - pos = finalDiffCmd.find(_T("%NEW"));
  1759. - finalDiffCmd.replace(pos, 4, sLatestRev, 0, sLatestRev.size());
  1760. + // Find and replace "%1"
  1761. + pos = finalDiffCmd.find(_T("%1"));
  1762. + finalDiffCmd.replace(pos, 2, basisDir, 0, basisDir.size());
  1763.  
  1764. - // Find and replace "%1"
  1765. - pos = finalDiffCmd.find(_T("%1"));
  1766. - finalDiffCmd.replace(pos, 2, basisDir, 0, basisDir.size());
  1767. + // Find and replace "%2"
  1768. + pos = finalDiffCmd.find(_T("%2"));
  1769. + finalDiffCmd.replace(pos, 2, latestDir, 0, latestDir.size());
  1770.  
  1771. - // Find and replace "%2"
  1772. - pos = finalDiffCmd.find(_T("%2"));
  1773. - finalDiffCmd.replace(pos, 2, latestDir, 0, latestDir.size());
  1774. + // Run accurev to perform the diff command
  1775. + CAppUtils::LaunchApplication(finalDiffCmd, true, false, false);
  1776. + }
  1777. + break;
  1778. + }
  1779. + }
  1780. + else
  1781. + {
  1782. + ReviewBoardReview * pLogEntry = (ReviewBoardReview*)item.lParam;
  1783. +
  1784. + // Switch how the diff is done in SVN / Accurev
  1785. + switch(pUrlInfo->sccs) {
  1786. + default:
  1787. + case CUrlInfo::SCCS_SVN:
  1788. + {
  1789. + // find the diff name
  1790. + const CUrlInfo * pInfo = &pRead->find(*(wstring*)itemex.lParam)->second;
  1791. + // in case the project name has &#039;path&#039; chars in it, we have to remove those first
  1792. + _stprintf_s(buf, 4096, _T("%s_review_%s.diff"), CAppUtils::ConvertName(pInfo->name).c_str(), pLogEntry->Reviewid().c_str());
  1793. + wstring diffFileName = CAppUtils::GetDataDir();
  1794. + diffFileName += _T("\\");
  1795. + diffFileName += wstring(buf);
  1796. + // construct a title for the diff viewer
  1797. + _stprintf_s(buf, 4096, _T("%s, review id %s"), pInfo->name.c_str(), pLogEntry->Reviewid().c_str());
  1798. + wstring title = wstring(buf);
  1799. + // start the diff viewer
  1800. + wstring cmd;
  1801.  
  1802. - // Run accurev to perform the diff command
  1803. - CAppUtils::LaunchApplication(finalDiffCmd, true, false, false);
  1804. + TCHAR apppath[4096];
  1805. + GetModuleFileName(NULL, apppath, 4096);
  1806. + CRegStdString diffViewer = CRegStdString(_T("Software\\CommitMonitor\\DiffViewer"));
  1807. + if (wstring(diffViewer).empty())
  1808. + {
  1809. + cmd = apppath;
  1810. + cmd += _T(" /patchfile:\"");
  1811. + }
  1812. + else
  1813. + {
  1814. + cmd = (wstring)diffViewer;
  1815. + cmd += _T(" \"");
  1816. + }
  1817. + cmd += diffFileName;
  1818. + cmd += _T("\"");
  1819. + if (wstring(diffViewer).empty())
  1820. + {
  1821. + cmd += _T(" /title:\"");
  1822. + cmd += title;
  1823. + cmd += _T("\"");
  1824. + }
  1825. + // Check if the diff file exists. If it doesn&#039;t, we have to fetch
  1826. + // the diff first
  1827. + if (!PathFileExists(diffFileName.c_str()))
  1828. + {
  1829. + // fetch the diff
  1830. + SVN svn;
  1831. + svn.SetAuthInfo(pInfo->username, pInfo->password);
  1832. + CProgressDlg progDlg;
  1833. + svn.SetAndClearProgressInfo(&progDlg);
  1834. + progDlg.SetTitle(_T("Fetching Diff"));
  1835. + TCHAR dispbuf[MAX_PATH] = {0};
  1836. + _stprintf_s(dispbuf, MAX_PATH, _T("fetching diff of review id %s"), pLogEntry->Reviewid().c_str());
  1837. + progDlg.SetLine(1, dispbuf);
  1838. + progDlg.SetShowProgressBar(false);
  1839. + progDlg.ShowModeless(*this);
  1840. + progDlg.SetLine(1, dispbuf);
  1841. + progDlg.SetProgress(3, 100); // set some dummy progress
  1842. + CRegStdString diffParams = CRegStdString(_T("Software\\CommitMonitor\\DiffParameters"));
  1843. + ReviewBoardReviewUnifiedDiff unidiff;
  1844. + unidiff.Open(pLogEntry->reviewid.c_str());
  1845. + unidiff.Save(diffFileName);
  1846. + TRACE(_T("Diff fetched for %s, review id %s\n"), pInfo->url.c_str(), pLogEntry->Reviewid().c_str());
  1847. + progDlg.Stop();
  1848. + }
  1849. + if (PathFileExists(diffFileName.c_str()))
  1850. + CAppUtils::LaunchApplication(cmd);
  1851. + }
  1852. + break;
  1853. }
  1854. - break;
  1855. }
  1856. }
  1857. }
  1858. @@ -1699,7 +1955,7 @@
  1859. tv.hParent = FindParentTreeNode(it->first);
  1860. tv.hInsertAfter = TVI_SORT;
  1861. tv.itemex.mask = TVIF_TEXT|TVIF_PARAM|TVIF_STATE|TVIF_IMAGE|TVIF_SELECTEDIMAGE;
  1862. - WCHAR * str = new WCHAR[it->second.name.size()+10];
  1863. + WCHAR * str = new WCHAR[it->second.name.size()+30];
  1864. // find out if there are some unread entries
  1865. int unread = 0;
  1866. for (map<svn_revnum_t,SCCSLogEntry>::const_iterator logit = it->second.logentries.begin(); logit != it->second.logentries.end(); ++logit)
  1867. @@ -1707,9 +1963,10 @@
  1868. if (!logit->second.read)
  1869. unread++;
  1870. }
  1871. - if (unread)
  1872. + int unreadreviews = gReviewBoard.GetUnreadReviewsForUrl(it->second.url);
  1873. + if (unread || unreadreviews)
  1874. {
  1875. - _stprintf_s(str, it->second.name.size()+10, _T("%s (%d)"), it->second.name.c_str(), unread);
  1876. + _stprintf_s(str, it->second.name.size()+30, _T("%s (%d + %d)"), it->second.name.c_str(), unread, unreadreviews);
  1877. tv.itemex.state = TVIS_BOLD;
  1878. tv.itemex.stateMask = TVIS_BOLD;
  1879. }
  1880. @@ -1747,7 +2004,7 @@
  1881. HTREEITEM hItem = TreeView_InsertItem(m_hTreeControl, &tv);
  1882. if ((!bShowLastUnread)&&(m_lastSelectedProject.compare(it->second.name) == 0))
  1883. tvToSel = hItem;
  1884. - if ((unread)&&(tvToSel == 0))
  1885. + if ((unread || unreadreviews)&&(tvToSel == 0))
  1886. tvToSel = hItem;
  1887. TreeView_Expand(m_hTreeControl, tv.hParent, TVE_EXPAND);
  1888. delete [] str;
  1889. @@ -1955,14 +2212,28 @@
  1890. lvc.mask = LVCF_TEXT;
  1891. lvc.fmt = LVCFMT_LEFT;
  1892. lvc.cx = -1;
  1893. - lvc.pszText = _T("revision");
  1894. - ListView_InsertColumn(m_hListControl, 0, &lvc);
  1895. - lvc.pszText = _T("date");
  1896. - ListView_InsertColumn(m_hListControl, 1, &lvc);
  1897. - lvc.pszText = _T("author");
  1898. - ListView_InsertColumn(m_hListControl, 2, &lvc);
  1899. - lvc.pszText = _T("log message");
  1900. - ListView_InsertColumn(m_hListControl, 3, &lvc);
  1901. + if(m_commitsradio)
  1902. + {
  1903. + lvc.pszText = _T("revision");
  1904. + ListView_InsertColumn(m_hListControl, 0, &lvc);
  1905. + lvc.pszText = _T("date");
  1906. + ListView_InsertColumn(m_hListControl, 1, &lvc);
  1907. + lvc.pszText = _T("author");
  1908. + ListView_InsertColumn(m_hListControl, 2, &lvc);
  1909. + lvc.pszText = _T("log message");
  1910. + ListView_InsertColumn(m_hListControl, 3, &lvc);
  1911. + }
  1912. + else
  1913. + {
  1914. + lvc.pszText = _T("review id");
  1915. + ListView_InsertColumn(m_hListControl, 0, &lvc);
  1916. + lvc.pszText = _T("date");
  1917. + ListView_InsertColumn(m_hListControl, 1, &lvc);
  1918. + lvc.pszText = _T("author");
  1919. + ListView_InsertColumn(m_hListControl, 2, &lvc);
  1920. + lvc.pszText = _T("log message");
  1921. + ListView_InsertColumn(m_hListControl, 3, &lvc);
  1922. + }
  1923.  
  1924. LVITEM item = {0};
  1925. TCHAR buf[1024];
  1926. @@ -1984,99 +2255,203 @@
  1927.  
  1928. delete [] buffer;
  1929.  
  1930. - for (map<svn_revnum_t,SCCSLogEntry>::const_iterator it = info->logentries.begin(); it != info->logentries.end(); ++it)
  1931. + if(m_commitsradio)
  1932. {
  1933. - // only add entries that match the filter string
  1934. - bool addEntry = true;
  1935. - bool useFilter = filterstringlower.size() != 0;
  1936. - bool bUseRegex = (filterstring.size() > 1)&&(filterstring[0] == &#039;\\&#039;);
  1937. + for (map<svn_revnum_t,SCCSLogEntry>::const_iterator it = info->logentries.begin(); it != info->logentries.end(); ++it)
  1938. + {
  1939. + // only add entries that match the filter string
  1940. + bool addEntry = true;
  1941. + bool useFilter = filterstringlower.size() != 0;
  1942. + bool bUseRegex = (filterstring.size() > 1)&&(filterstring[0] == &#039;\\&#039;);
  1943.  
  1944. - if (useFilter)
  1945. - {
  1946. - if (bUseRegex)
  1947. - {
  1948. - try
  1949. + if (useFilter)
  1950. {
  1951. - const tr1::wregex regCheck(filterstring.substr(1), tr1::regex_constants::icase | tr1::regex_constants::ECMAScript);
  1952. + if (bUseRegex)
  1953. + {
  1954. + try
  1955. + {
  1956. + const tr1::wregex regCheck(filterstring.substr(1), tr1::regex_constants::icase | tr1::regex_constants::ECMAScript);
  1957.  
  1958. - addEntry = tr1::regex_search(it->second.author, regCheck);
  1959. + addEntry = tr1::regex_search(it->second.author, regCheck);
  1960. + if (!addEntry)
  1961. + {
  1962. + addEntry = tr1::regex_search(it->second.message, regCheck);
  1963. + if (!addEntry)
  1964. + {
  1965. + _stprintf_s(buf, 1024, _T("%ld"), it->first);
  1966. + wstring s = wstring(buf);
  1967. + addEntry = tr1::regex_search(s, regCheck);
  1968. + }
  1969. + }
  1970. + }
  1971. + catch (exception)
  1972. + {
  1973. + bUseRegex = false;
  1974. + }
  1975. + if (bNegateFilter)
  1976. + addEntry = !addEntry;
  1977. + }
  1978. + if (!bUseRegex)
  1979. + {
  1980. + // search plain text
  1981. + // note: \Q...\E doesn&#039;t seem to work with tr1 - it still
  1982. + // throws an exception if the regex in between is not a valid regex :(
  1983. +
  1984. + wstring s = it->second.author;
  1985. + std::transform(s.begin(), s.end(), s.begin(), std::tolower);
  1986. + addEntry = s.find(filterstringlower) != wstring::npos;
  1987. +
  1988. if (!addEntry)
  1989. {
  1990. - addEntry = tr1::regex_search(it->second.message, regCheck);
  1991. + s = it->second.message;
  1992. + std::transform(s.begin(), s.end(), s.begin(), std::tolower);
  1993. + addEntry = s.find(filterstringlower) != wstring::npos;
  1994. if (!addEntry)
  1995. {
  1996. _stprintf_s(buf, 1024, _T("%ld"), it->first);
  1997. - wstring s = wstring(buf);
  1998. - addEntry = tr1::regex_search(s, regCheck);
  1999. + s = buf;
  2000. + addEntry = s.find(filterstringlower) != wstring::npos;
  2001. }
  2002. }
  2003. + if (bNegateFilter)
  2004. + addEntry = !addEntry;
  2005. }
  2006. - catch (exception)
  2007. + }
  2008. +
  2009. + if (!addEntry)
  2010. + continue;
  2011. +
  2012. + item.mask = LVIF_TEXT|LVIF_PARAM;
  2013. + item.iItem = 0;
  2014. + item.lParam = (LPARAM)&it->second;
  2015. + _stprintf_s(buf, 1024, _T("%ld"), it->first);
  2016. + item.pszText = buf;
  2017. + ListView_InsertItem(m_hListControl, &item);
  2018. + if (it->second.date)
  2019. + _tcscpy_s(buf, 1024, CAppUtils::ConvertDate(it->second.date).c_str());
  2020. + else
  2021. + _tcscpy_s(buf, 1024, _T("(no date)"));
  2022. + ListView_SetItemText(m_hListControl, 0, 1, buf);
  2023. + if (it->second.author.size())
  2024. + _tcscpy_s(buf, 1024, it->second.author.c_str());
  2025. + else
  2026. + _tcscpy_s(buf, 1024, _T("(no author)"));
  2027. + ListView_SetItemText(m_hListControl, 0, 2, buf);
  2028. + wstring msg = it->second.message;
  2029. + std::remove(msg.begin(), msg.end(), &#039;\r&#039;);
  2030. + std::replace(msg.begin(), msg.end(), &#039;\n&#039;, &#039; &#039;);
  2031. + std::replace(msg.begin(), msg.end(), &#039;\t&#039;, &#039; &#039;);
  2032. + _tcsncpy_s(buf, 1024, msg.c_str(), 1023);
  2033. + ListView_SetItemText(m_hListControl, 0, 3, buf);
  2034. +
  2035. + if ((iLastUnread < 0)&&(!it->second.read))
  2036. {
  2037. - bUseRegex = false;
  2038. + iLastUnread = 0;
  2039. }
  2040. - if (bNegateFilter)
  2041. - addEntry = !addEntry;
  2042. + if (iLastUnread >= 0)
  2043. + iLastUnread++;
  2044. }
  2045. - if (!bUseRegex)
  2046. + }
  2047. + else
  2048. + {
  2049. + ReviewBoard::WR2Rmap vec;
  2050. + gReviewBoard.GetReviewsForUrl(info->url,vec);
  2051. + gReviewBoard.GetReadLock();
  2052. + for (ReviewBoard::WR2Rmap::const_iterator it = vec.begin(); it != vec.end(); ++it)
  2053. {
  2054. - // search plain text
  2055. - // note: \Q...\E doesn&#039;t seem to work with tr1 - it still
  2056. - // throws an exception if the regex in between is not a valid regex :(
  2057. + // only add entries that match the filter string
  2058. + bool addEntry = true;
  2059. + bool useFilter = filterstringlower.size() != 0;
  2060. + bool bUseRegex = (filterstring.size() > 1)&&(filterstring[0] == &#039;\\&#039;);
  2061.  
  2062. - wstring s = it->second.author;
  2063. - std::transform(s.begin(), s.end(), s.begin(), std::tolower);
  2064. - addEntry = s.find(filterstringlower) != wstring::npos;
  2065. + if (useFilter)
  2066. + {
  2067. + if (bUseRegex)
  2068. + {
  2069. + try
  2070. + {
  2071. + const tr1::wregex regCheck(filterstring.substr(1), tr1::regex_constants::icase | tr1::regex_constants::ECMAScript);
  2072.  
  2073. - if (!addEntry)
  2074. + addEntry = tr1::regex_search(it->second->Author(), regCheck);
  2075. + if (!addEntry)
  2076. + {
  2077. + addEntry = tr1::regex_search(it->second->Message(), regCheck);
  2078. + if (!addEntry)
  2079. + {
  2080. + _stprintf_s(buf, 1024, _T("%ld"), it->first);
  2081. + wstring s = wstring(buf);
  2082. + addEntry = tr1::regex_search(s, regCheck);
  2083. + }
  2084. + }
  2085. + }
  2086. + catch (exception)
  2087. + {
  2088. + bUseRegex = false;
  2089. + }
  2090. + if (bNegateFilter)
  2091. + addEntry = !addEntry;
  2092. + }
  2093. + if (!bUseRegex)
  2094. {
  2095. - s = it->second.message;
  2096. + // search plain text
  2097. + // note: \Q...\E doesn&#039;t seem to work with tr1 - it still
  2098. + // throws an exception if the regex in between is not a valid regex :(
  2099. +
  2100. + wstring s = it->second->Author();
  2101. std::transform(s.begin(), s.end(), s.begin(), std::tolower);
  2102. addEntry = s.find(filterstringlower) != wstring::npos;
  2103. +
  2104. if (!addEntry)
  2105. {
  2106. - _stprintf_s(buf, 1024, _T("%ld"), it->first);
  2107. - s = buf;
  2108. - addEntry = s.find(filterstringlower) != wstring::npos;
  2109. + s = it->second->Message();
  2110. + std::transform(s.begin(), s.end(), s.begin(), std::tolower);
  2111. + addEntry = s.find(filterstringlower) != wstring::npos;
  2112. + if (!addEntry)
  2113. + {
  2114. + _stprintf_s(buf, 1024, _T("%ld"), it->first);
  2115. + s = buf;
  2116. + addEntry = s.find(filterstringlower) != wstring::npos;
  2117. + }
  2118. }
  2119. + if (bNegateFilter)
  2120. + addEntry = !addEntry;
  2121. }
  2122. - if (bNegateFilter)
  2123. - addEntry = !addEntry;
  2124. - }
  2125. - }
  2126. + }
  2127.  
  2128. - if (!addEntry)
  2129. - continue;
  2130. + if (!addEntry)
  2131. + continue;
  2132.  
  2133. - item.mask = LVIF_TEXT|LVIF_PARAM;
  2134. - item.iItem = 0;
  2135. - item.lParam = (LPARAM)&it->second;
  2136. - _stprintf_s(buf, 1024, _T("%ld"), it->first);
  2137. - item.pszText = buf;
  2138. - ListView_InsertItem(m_hListControl, &item);
  2139. - if (it->second.date)
  2140. - _tcscpy_s(buf, 1024, CAppUtils::ConvertDate(it->second.date).c_str());
  2141. - else
  2142. - _tcscpy_s(buf, 1024, _T("(no date)"));
  2143. - ListView_SetItemText(m_hListControl, 0, 1, buf);
  2144. - if (it->second.author.size())
  2145. - _tcscpy_s(buf, 1024, it->second.author.c_str());
  2146. - else
  2147. - _tcscpy_s(buf, 1024, _T("(no author)"));
  2148. - ListView_SetItemText(m_hListControl, 0, 2, buf);
  2149. - wstring msg = it->second.message;
  2150. - std::remove(msg.begin(), msg.end(), &#039;\r&#039;);
  2151. - std::replace(msg.begin(), msg.end(), &#039;\n&#039;, &#039; &#039;);
  2152. - std::replace(msg.begin(), msg.end(), &#039;\t&#039;, &#039; &#039;);
  2153. - _tcsncpy_s(buf, 1024, msg.c_str(), 1023);
  2154. - ListView_SetItemText(m_hListControl, 0, 3, buf);
  2155. + item.mask = LVIF_TEXT|LVIF_PARAM;
  2156. + item.iItem = 0;
  2157. + item.lParam = (LPARAM)it->second;
  2158. + _stprintf_s(buf, 1024, _T("%s"), it->first.c_str());
  2159. + item.pszText = buf;
  2160. + ListView_InsertItem(m_hListControl, &item);
  2161. + if (it->second->Date().size())
  2162. + _tcscpy_s(buf, 1024, it->second->Date().c_str());
  2163. + else
  2164. + _tcscpy_s(buf, 1024, _T("(no date)"));
  2165. + ListView_SetItemText(m_hListControl, 0, 1, buf);
  2166. + if (it->second->Author().size())
  2167. + _tcscpy_s(buf, 1024, it->second->Author().c_str());
  2168. + else
  2169. + _tcscpy_s(buf, 1024, _T("(no author)"));
  2170. + ListView_SetItemText(m_hListControl, 0, 2, buf);
  2171. + wstring msg = it->second->Message();
  2172. + std::remove(msg.begin(), msg.end(), &#039;\r&#039;);
  2173. + std::replace(msg.begin(), msg.end(), &#039;\n&#039;, &#039; &#039;);
  2174. + std::replace(msg.begin(), msg.end(), &#039;\t&#039;, &#039; &#039;);
  2175. + _tcsncpy_s(buf, 1024, msg.c_str(), 1023);
  2176. + ListView_SetItemText(m_hListControl, 0, 3, buf);
  2177.  
  2178. - if ((iLastUnread < 0)&&(!it->second.read))
  2179. - {
  2180. - iLastUnread = 0;
  2181. + if ((iLastUnread < 0)&&(!it->second->read))
  2182. + {
  2183. + iLastUnread = 0;
  2184. + }
  2185. + if (iLastUnread >= 0)
  2186. + iLastUnread++;
  2187. }
  2188. - if (iLastUnread >= 0)
  2189. - iLastUnread++;
  2190. + gReviewBoard.ReleaseReadOnlyData();
  2191. }
  2192. ListView_SetSelectionMark(m_hListControl, selMark);
  2193. m_bBlockListCtrlUI = false;
  2194. @@ -2110,6 +2485,7 @@
  2195. bChanged = true;
  2196. it->second.read = true;
  2197. }
  2198. + gReviewBoard.ReviewsSetRead(info->url);
  2199. // refresh the name of the tree item to indicate the new
  2200. // number of unread log messages
  2201. WCHAR * str = new WCHAR[info->name.size()+10];
  2202. @@ -2208,124 +2584,246 @@
  2203. return;
  2204. if ((lpNMListView->uOldState ^ lpNMListView->uNewState) & LVIS_SELECTED)
  2205. {
  2206. - const map<wstring,CUrlInfo> * pRead = m_pURLInfos->GetReadOnlyData();
  2207. LVITEM item = {0};
  2208. item.mask = LVIF_PARAM;
  2209. item.iItem = lpNMListView->iItem;
  2210. ListView_GetItem(m_hListControl, &item);
  2211. - SCCSLogEntry * pLogEntry = (SCCSLogEntry*)item.lParam;
  2212. - if (pLogEntry)
  2213. + if(m_commitsradio)
  2214. {
  2215. - HTREEITEM hSelectedItem = TreeView_GetSelection(m_hTreeControl);
  2216. - // get the url this entry refers to
  2217. - TVITEMEX itemex = {0};
  2218. - itemex.hItem = hSelectedItem;
  2219. - itemex.mask = TVIF_PARAM;
  2220. - TreeView_GetItem(m_hTreeControl, &itemex);
  2221. - if (itemex.lParam == 0)
  2222. + const map<wstring,CUrlInfo> * pRead = m_pURLInfos->GetReadOnlyData();
  2223. + SCCSLogEntry * pLogEntry = (SCCSLogEntry*)item.lParam;
  2224. + if (pLogEntry)
  2225. {
  2226. - m_pURLInfos->ReleaseReadOnlyData();
  2227. - return;
  2228. - }
  2229. - // set the entry as read
  2230. - if ((!pLogEntry->read)&&(lpNMListView->uNewState & LVIS_SELECTED))
  2231. - {
  2232. - pLogEntry->read = true;
  2233. - // refresh the name of the tree item to indicate the new
  2234. - // number of unread log messages
  2235. - // e.g. instead of &#039;TortoiseSVN (3)&#039;, show now &#039;TortoiseSVN (2)&#039;
  2236. - if (pRead->find(*(wstring*)itemex.lParam) != pRead->end())
  2237. + HTREEITEM hSelectedItem = TreeView_GetSelection(m_hTreeControl);
  2238. + // get the url this entry refers to
  2239. + TVITEMEX itemex = {0};
  2240. + itemex.hItem = hSelectedItem;
  2241. + itemex.mask = TVIF_PARAM;
  2242. + TreeView_GetItem(m_hTreeControl, &itemex);
  2243. + if (itemex.lParam == 0)
  2244. {
  2245. - const CUrlInfo * uinfo = &pRead->find(*(wstring*)itemex.lParam)->second;
  2246. - // count the number of unread messages
  2247. - int unread = 0;
  2248. - for (map<svn_revnum_t,SCCSLogEntry>::const_iterator it = uinfo->logentries.begin(); it != uinfo->logentries.end(); ++it)
  2249. + m_pURLInfos->ReleaseReadOnlyData();
  2250. + return;
  2251. + }
  2252. + // set the entry as read
  2253. + if ((!pLogEntry->read)&&(lpNMListView->uNewState & LVIS_SELECTED))
  2254. + {
  2255. + pLogEntry->read = true;
  2256. + // refresh the name of the tree item to indicate the new
  2257. + // number of unread log messages
  2258. + // e.g. instead of &#039;TortoiseSVN (3)&#039;, show now &#039;TortoiseSVN (2)&#039;
  2259. + if (pRead->find(*(wstring*)itemex.lParam) != pRead->end())
  2260. {
  2261. - if (!it->second.read)
  2262. - unread++;
  2263. + const CUrlInfo * uinfo = &pRead->find(*(wstring*)itemex.lParam)->second;
  2264. + // count the number of unread messages
  2265. + int unread = 0;
  2266. + for (map<svn_revnum_t,SCCSLogEntry>::const_iterator it = uinfo->logentries.begin(); it != uinfo->logentries.end(); ++it)
  2267. + {
  2268. + if (!it->second.read)
  2269. + unread++;
  2270. + }
  2271. + WCHAR * str = new WCHAR[uinfo->name.size()+30];
  2272. + int unreadreviews = gReviewBoard.GetUnreadReviewsForUrl(uinfo->url);
  2273. + if (unread || unreadreviews)
  2274. + {
  2275. + _stprintf_s(str, uinfo->name.size()+30, _T("%s (%d + %d)"), uinfo->name.c_str(), unread, unreadreviews);
  2276. + itemex.state = TVIS_BOLD;
  2277. + itemex.stateMask = TVIS_BOLD;
  2278. + itemex.iImage = 3;
  2279. + itemex.iSelectedImage = 3;
  2280. + }
  2281. + else
  2282. + {
  2283. + _stprintf_s(str, uinfo->name.size()+30, _T("%s"), uinfo->name.c_str());
  2284. + itemex.state = 0;
  2285. + itemex.stateMask = TVIS_BOLD;
  2286. + itemex.iImage = 2;
  2287. + itemex.iSelectedImage = 2;
  2288. + }
  2289. +
  2290. + itemex.pszText = str;
  2291. + itemex.mask = TVIF_TEXT|TVIF_STATE|TVIF_IMAGE|TVIF_SELECTEDIMAGE;
  2292. + TreeView_SetItem(m_hTreeControl, &itemex);
  2293. }
  2294. - WCHAR * str = new WCHAR[uinfo->name.size()+10];
  2295. - if (unread)
  2296. + // the icon in the system tray needs to be changed back
  2297. + // to &#039;normal&#039;
  2298. + ::SendMessage(m_hParent, COMMITMONITOR_CHANGEDINFO, (WPARAM)false, (LPARAM)0);
  2299. + }
  2300. + TCHAR buf[1024];
  2301. + wstring msg;
  2302. + if (ListView_GetSelectedCount(m_hListControl) > 1)
  2303. + {
  2304. + msg = _T("multiple log entries selected. Info for the last selected one:\n-------------------------------\n\n");
  2305. + }
  2306. + msg += pLogEntry->message.c_str();
  2307. + msg += _T("\n\n-------------------------------\n");
  2308. + // now add all changed paths, one path per line
  2309. + for (map<std::wstring, SCCSLogChangedPaths>::const_iterator it = pLogEntry->m_changedPaths.begin(); it != pLogEntry->m_changedPaths.end(); ++it)
  2310. + {
  2311. + // action
  2312. + msg += it->second.action;
  2313. + bool mods = false;
  2314. + if ((it->second.text_modified == svn_tristate_true)||(it->second.props_modified == svn_tristate_true))
  2315. {
  2316. - _stprintf_s(str, uinfo->name.size()+10, _T("%s (%d)"), uinfo->name.c_str(), unread);
  2317. - itemex.state = TVIS_BOLD;
  2318. - itemex.stateMask = TVIS_BOLD;
  2319. - itemex.iImage = 3;
  2320. - itemex.iSelectedImage = 3;
  2321. + mods = true;
  2322. }
  2323. - else
  2324. + if (mods)
  2325. + msg += L"(";
  2326. + if (it->second.text_modified == svn_tristate_true)
  2327. + msg += L"T";
  2328. + else if (mods)
  2329. + msg += L" ";
  2330. + if (it->second.props_modified == svn_tristate_true)
  2331. + msg += L"P";
  2332. + else if (mods)
  2333. + msg += L" ";
  2334. + if (mods)
  2335. + msg += L")";
  2336. + msg += _T(" : ");
  2337. + msg += it->first;
  2338. + msg += _T(" ");
  2339. + if (!it->second.copyfrom_path.empty())
  2340. {
  2341. - _stprintf_s(str, uinfo->name.size()+10, _T("%s"), uinfo->name.c_str());
  2342. - itemex.state = 0;
  2343. - itemex.stateMask = TVIS_BOLD;
  2344. - itemex.iImage = 2;
  2345. - itemex.iSelectedImage = 2;
  2346. + msg += _T("(copied from: ");
  2347. + msg += it->second.copyfrom_path;
  2348. + msg += _T(", revision ");
  2349. + _stprintf_s(buf, 1024, _T("%ld)\n"), it->second.copyfrom_revision);
  2350. + msg += wstring(buf);
  2351. }
  2352. + else
  2353. + msg += _T("\n");
  2354. + }
  2355.  
  2356. - itemex.pszText = str;
  2357. - itemex.mask = TVIF_TEXT|TVIF_STATE|TVIF_IMAGE|TVIF_SELECTEDIMAGE;
  2358. - TreeView_SetItem(m_hTreeControl, &itemex);
  2359. - }
  2360. - // the icon in the system tray needs to be changed back
  2361. - // to &#039;normal&#039;
  2362. - ::SendMessage(m_hParent, COMMITMONITOR_CHANGEDINFO, (WPARAM)false, (LPARAM)0);
  2363. + CAppUtils::SearchReplace(msg, _T("\n"), _T("\r\n"));
  2364. + SetWindowText(m_hLogMsgControl, msg.c_str());
  2365. +
  2366. + // find the diff name
  2367. + _stprintf_s(buf, 1024, _T("%s_%ld.diff"), pRead->find(*(wstring*)itemex.lParam)->second.name.c_str(), pLogEntry->revision);
  2368. + wstring diffFileName = CAppUtils::GetDataDir();
  2369. + diffFileName += _T("\\");
  2370. + diffFileName += wstring(buf);
  2371. + SendMessage(m_hwndToolbar, TB_ENABLEBUTTON, ID_MAIN_SHOWDIFFCHOOSE, MAKELONG(true, 0));
  2372. }
  2373. - TCHAR buf[1024];
  2374. - wstring msg;
  2375. - if (ListView_GetSelectedCount(m_hListControl) > 1)
  2376. + m_pURLInfos->ReleaseReadOnlyData();
  2377. + }
  2378. + else
  2379. + {
  2380. + ReviewBoardReview * pLogEntry = (ReviewBoardReview*)item.lParam; // conversion loses qualifiers, cast to non-const
  2381. + const map<wstring,CUrlInfo> * pRead = m_pURLInfos->GetReadOnlyData();
  2382. + if (pLogEntry)
  2383. {
  2384. - msg = _T("multiple log entries selected. Info for the last selected one:\n-------------------------------\n\n");
  2385. - }
  2386. - msg += pLogEntry->message.c_str();
  2387. - msg += _T("\n\n-------------------------------\n");
  2388. - // now add all changed paths, one path per line
  2389. - for (map<std::wstring, SCCSLogChangedPaths>::const_iterator it = pLogEntry->m_changedPaths.begin(); it != pLogEntry->m_changedPaths.end(); ++it)
  2390. - {
  2391. - // action
  2392. - msg += it->second.action;
  2393. - bool mods = false;
  2394. - if ((it->second.text_modified == svn_tristate_true)||(it->second.props_modified == svn_tristate_true))
  2395. + HTREEITEM hSelectedItem = TreeView_GetSelection(m_hTreeControl);
  2396. + // get the url this entry refers to
  2397. + TVITEMEX itemex = {0};
  2398. + itemex.hItem = hSelectedItem;
  2399. + itemex.mask = TVIF_PARAM;
  2400. + TreeView_GetItem(m_hTreeControl, &itemex);
  2401. + if (itemex.lParam == 0)
  2402. {
  2403. - mods = true;
  2404. + m_pURLInfos->ReleaseReadOnlyData();
  2405. + return;
  2406. }
  2407. - if (mods)
  2408. - msg += L"(";
  2409. - if (it->second.text_modified == svn_tristate_true)
  2410. - msg += L"T";
  2411. - else if (mods)
  2412. - msg += L" ";
  2413. - if (it->second.props_modified == svn_tristate_true)
  2414. - msg += L"P";
  2415. - else if (mods)
  2416. - msg += L" ";
  2417. - if (mods)
  2418. - msg += L")";
  2419. - msg += _T(" : ");
  2420. - msg += it->first;
  2421. - msg += _T(" ");
  2422. - if (!it->second.copyfrom_path.empty())
  2423. + // set the entry as read
  2424. + if ((!pLogEntry->read)&&(lpNMListView->uNewState & LVIS_SELECTED))
  2425. {
  2426. - msg += _T("(copied from: ");
  2427. - msg += it->second.copyfrom_path;
  2428. - msg += _T(", revision ");
  2429. - _stprintf_s(buf, 1024, _T("%ld)\n"), it->second.copyfrom_revision);
  2430. - msg += wstring(buf);
  2431. + gReviewBoard.ReviewSetRead(pLogEntry->reviewid);
  2432. + // refresh the name of the tree item to indicate the new
  2433. + // number of unread log messages
  2434. + // e.g. instead of &#039;TortoiseSVN (3)&#039;, show now &#039;TortoiseSVN (2)&#039;
  2435. + if (pRead->find(*(wstring*)itemex.lParam) != pRead->end())
  2436. + {
  2437. + const CUrlInfo * uinfo = &pRead->find(*(wstring*)itemex.lParam)->second;
  2438. + // count the number of unread messages
  2439. + int unread = 0;
  2440. + for (map<svn_revnum_t,SCCSLogEntry>::const_iterator it = uinfo->logentries.begin(); it != uinfo->logentries.end(); ++it)
  2441. + {
  2442. + if (!it->second.read)
  2443. + unread++;
  2444. + }
  2445. + WCHAR * str = new WCHAR[uinfo->name.size()+30];
  2446. + int unreadreviews = gReviewBoard.GetUnreadReviewsForUrl(uinfo->url);
  2447. + if (unread || unreadreviews)
  2448. + {
  2449. + _stprintf_s(str, uinfo->name.size()+30, _T("%s (%d + %d)"), uinfo->name.c_str(), unread, unreadreviews);
  2450. + itemex.state = TVIS_BOLD;
  2451. + itemex.stateMask = TVIS_BOLD;
  2452. + itemex.iImage = 3;
  2453. + itemex.iSelectedImage = 3;
  2454. + }
  2455. + else
  2456. + {
  2457. + _stprintf_s(str, uinfo->name.size()+30, _T("%s"), uinfo->name.c_str());
  2458. + itemex.state = 0;
  2459. + itemex.stateMask = TVIS_BOLD;
  2460. + itemex.iImage = 2;
  2461. + itemex.iSelectedImage = 2;
  2462. + }
  2463. +
  2464. + itemex.pszText = str;
  2465. + itemex.mask = TVIF_TEXT|TVIF_STATE|TVIF_IMAGE|TVIF_SELECTEDIMAGE;
  2466. + TreeView_SetItem(m_hTreeControl, &itemex);
  2467. + }
  2468. + // the icon in the system tray needs to be changed back
  2469. + // to &#039;normal&#039;
  2470. + ::SendMessage(m_hParent, COMMITMONITOR_CHANGEDINFO, (WPARAM)false, (LPARAM)0);
  2471. }
  2472. - else
  2473. - msg += _T("\n");
  2474. + TCHAR buf[1024];
  2475. + wstring msg;
  2476. + if (ListView_GetSelectedCount(m_hListControl) > 1)
  2477. + {
  2478. + msg = _T("multiple log entries selected. Info for the last selected one:\n-------------------------------\n\n");
  2479. + }
  2480. + msg += pLogEntry->Message().c_str();
  2481. + msg += _T("\n\n-------------------------------\n");
  2482. +/* // now add all changed paths, one path per line
  2483. + for (map<std::wstring, SCCSLogChangedPaths>::const_iterator it = pLogEntry->m_changedPaths.begin(); it != pLogEntry->m_changedPaths.end(); ++it)
  2484. + {
  2485. + // action
  2486. + msg += it->second.action;
  2487. + bool mods = false;
  2488. + if ((it->second.text_modified == svn_tristate_true)||(it->second.props_modified == svn_tristate_true))
  2489. + {
  2490. + mods = true;
  2491. + }
  2492. + if (mods)
  2493. + msg += L"(";
  2494. + if (it->second.text_modified == svn_tristate_true)
  2495. + msg += L"T";
  2496. + else if (mods)
  2497. + msg += L" ";
  2498. + if (it->second.props_modified == svn_tristate_true)
  2499. + msg += L"P";
  2500. + else if (mods)
  2501. + msg += L" ";
  2502. + if (mods)
  2503. + msg += L")";
  2504. + msg += _T(" : ");
  2505. + msg += it->first;
  2506. + msg += _T(" ");
  2507. + if (!it->second.copyfrom_path.empty())
  2508. + {
  2509. + msg += _T("(copied from: ");
  2510. + msg += it->second.copyfrom_path;
  2511. + msg += _T(", revision ");
  2512. + _stprintf_s(buf, 1024, _T("%ld)\n"), it->second.copyfrom_revision);
  2513. + msg += wstring(buf);
  2514. + }
  2515. + else
  2516. + msg += _T("\n");
  2517. + }
  2518. + */
  2519. + CAppUtils::SearchReplace(msg, _T("\n"), _T("\r\n"));
  2520. + SetWindowText(m_hLogMsgControl, msg.c_str());
  2521. +
  2522. + // find the diff name
  2523. + _stprintf_s(buf, 1024, _T("%s_review_%s.diff"), pRead->find(*(wstring*)itemex.lParam)->second.name.c_str(), pLogEntry->Reviewid().c_str());
  2524. + wstring diffFileName = CAppUtils::GetDataDir();
  2525. + diffFileName += _T("\\");
  2526. + diffFileName += wstring(buf);
  2527. + SendMessage(m_hwndToolbar, TB_ENABLEBUTTON, ID_MAIN_SHOWDIFFCHOOSE, MAKELONG(true, 0));
  2528. }
  2529. -
  2530. - CAppUtils::SearchReplace(msg, _T("\n"), _T("\r\n"));
  2531. - SetWindowText(m_hLogMsgControl, msg.c_str());
  2532. -
  2533. - // find the diff name
  2534. - _stprintf_s(buf, 1024, _T("%s_%ld.diff"), pRead->find(*(wstring*)itemex.lParam)->second.name.c_str(), pLogEntry->revision);
  2535. - wstring diffFileName = CAppUtils::GetDataDir();
  2536. - diffFileName += _T("\\");
  2537. - diffFileName += wstring(buf);
  2538. - SendMessage(m_hwndToolbar, TB_ENABLEBUTTON, ID_MAIN_SHOWDIFFCHOOSE, MAKELONG(true, 0));
  2539. + m_pURLInfos->ReleaseReadOnlyData();
  2540. }
  2541. - m_pURLInfos->ReleaseReadOnlyData();
  2542. }
  2543. }
  2544.  
  2545. @@ -2371,14 +2869,29 @@
  2546. break;
  2547. case CDDS_ITEMPREPAINT:
  2548. {
  2549. - SCCSLogEntry * pLogEntry = (SCCSLogEntry*)lpNMCustomDraw->nmcd.lItemlParam;
  2550. + if(m_commitsradio)
  2551. + {
  2552. + SCCSLogEntry * pLogEntry = (SCCSLogEntry*)lpNMCustomDraw->nmcd.lItemlParam;
  2553.  
  2554. - if (!pLogEntry->read)
  2555. + if (!pLogEntry->read)
  2556. + {
  2557. + SelectObject(lpNMCustomDraw->nmcd.hdc, m_boldFont);
  2558. + // We changed the font, so we&#039;re returning CDRF_NEWFONT. This
  2559. + // tells the control to recalculate the extent of the text.
  2560. + result = CDRF_NEWFONT;
  2561. + }
  2562. + }
  2563. + else
  2564. {
  2565. - SelectObject(lpNMCustomDraw->nmcd.hdc, m_boldFont);
  2566. - // We changed the font, so we&#039;re returning CDRF_NEWFONT. This
  2567. - // tells the control to recalculate the extent of the text.
  2568. - result = CDRF_NEWFONT;
  2569. + ReviewBoardReview * pLogEntry = (ReviewBoardReview*)lpNMCustomDraw->nmcd.lItemlParam;
  2570. +
  2571. + if (!pLogEntry->read)
  2572. + {
  2573. + SelectObject(lpNMCustomDraw->nmcd.hdc, m_boldFont);
  2574. + // We changed the font, so we&#039;re returning CDRF_NEWFONT. This
  2575. + // tells the control to recalculate the extent of the text.
  2576. + result = CDRF_NEWFONT;
  2577. + }
  2578. }
  2579. }
  2580. break;
  2581. @@ -2504,18 +3017,40 @@
  2582. ListView_GetItem(m_hListControl, &item);
  2583. if (item.state & LVIS_SELECTED)
  2584. {
  2585. - SCCSLogEntry * pLogEntry = (SCCSLogEntry*)item.lParam;
  2586. - // find the diff name
  2587. - _stprintf_s(buf, 4096, _T("%s_%ld.diff"), pWrite->find(*(wstring*)itemex.lParam)->second.name.c_str(), pLogEntry->revision);
  2588. - wstring diffFileName = CAppUtils::GetDataDir();
  2589. - diffFileName += _T("\\");
  2590. - diffFileName += wstring(buf);
  2591. - DeleteFile(diffFileName.c_str());
  2592. + if(m_commitsradio)
  2593. + {
  2594. + SCCSLogEntry * pLogEntry = (SCCSLogEntry*)item.lParam;
  2595. + // find the diff name
  2596. + _stprintf_s(buf, 4096, _T("%s_%ld.diff"), pWrite->find(*(wstring*)itemex.lParam)->second.name.c_str(), pLogEntry->revision);
  2597. + wstring diffFileName = CAppUtils::GetDataDir();
  2598. + diffFileName += _T("\\");
  2599. + diffFileName += wstring(buf);
  2600. + DeleteFile(diffFileName.c_str());
  2601.  
  2602. - pWrite->find((*(wstring*)itemex.lParam))->second.logentries.erase(pLogEntry->revision);
  2603. - ListView_DeleteItem(m_hListControl, i);
  2604. - if (nFirstDeleted < 0)
  2605. - nFirstDeleted = i;
  2606. + pWrite->find((*(wstring*)itemex.lParam))->second.logentries.erase(pLogEntry->revision);
  2607. + ListView_DeleteItem(m_hListControl, i);
  2608. + if (nFirstDeleted < 0)
  2609. + nFirstDeleted = i;
  2610. + }
  2611. + else
  2612. + {
  2613. + ReviewBoardReview * pLogEntry = (ReviewBoardReview*)item.lParam;
  2614. + if(pLogEntry)
  2615. + {
  2616. + // find the diff name
  2617. + _stprintf_s(buf, 1024, _T("%s_review_%s.diff"), pWrite->find(*(wstring*)itemex.lParam)->second.name.c_str(), pLogEntry->Reviewid().c_str());
  2618. + wstring diffFileName = CAppUtils::GetDataDir();
  2619. + diffFileName += _T("\\");
  2620. + diffFileName += wstring(buf);
  2621. + DeleteFile(diffFileName.c_str());
  2622. +
  2623. + gReviewBoard.DeleteReview(pLogEntry->reviewid);
  2624. + pLogEntry = 0;
  2625. + ListView_DeleteItem(m_hListControl, i);
  2626. + if (nFirstDeleted < 0)
  2627. + nFirstDeleted = i;
  2628. + }
  2629. + }
  2630. }
  2631. else
  2632. ++i;
  2633. @@ -2561,7 +3096,9 @@
  2634. HDWP hdwp = BeginDeferWindowPos(9);
  2635. hdwp = DeferWindowPos(hdwp, m_hwndToolbar, *this, 0, 0, width, m_topmarg, SWP_NOZORDER|SWP_NOACTIVATE);
  2636. hdwp = DeferWindowPos(hdwp, hFilterLabel, *this, m_xSliderPos+4, m_topmarg+5, FILTERLABELWIDTH, 12, SWP_NOZORDER|SWP_NOACTIVATE|SWP_FRAMECHANGED);
  2637. - hdwp = DeferWindowPos(hdwp, m_hFilterControl, *this, m_xSliderPos+4+FILTERLABELWIDTH, m_topmarg+1, width-m_xSliderPos-4-FILTERLABELWIDTH-4, FILTERBOXHEIGHT-1, SWP_NOZORDER|SWP_NOACTIVATE);
  2638. + hdwp = DeferWindowPos(hdwp, m_hFilterControl, *this, m_xSliderPos+4+FILTERLABELWIDTH, m_topmarg+1, width-m_xSliderPos-4-FILTERLABELWIDTH-4 - m_radiossize, FILTERBOXHEIGHT-1, SWP_NOZORDER|SWP_NOACTIVATE);
  2639. + hdwp = DeferWindowPos(hdwp, m_hCommitsRadio, *this, width - m_radiossize + 4, m_topmarg+1, 80, FILTERBOXHEIGHT-1, SWP_NOZORDER|SWP_NOACTIVATE);
  2640. + hdwp = DeferWindowPos(hdwp, m_hReviewsRadio, *this, width - m_radiossize + 84, m_topmarg+1, 80, FILTERBOXHEIGHT-1, SWP_NOZORDER|SWP_NOACTIVATE);
  2641. hdwp = DeferWindowPos(hdwp, m_hTreeControl, *this, 0, m_topmarg, m_xSliderPos, height-m_topmarg-m_bottommarg+FILTERBOXHEIGHT+4, SWP_NOZORDER|SWP_NOACTIVATE);
  2642. hdwp = DeferWindowPos(hdwp, m_hListControl, *this, m_xSliderPos+4, m_topmarg+FILTERBOXHEIGHT, width-m_xSliderPos-4, m_ySliderPos-m_topmarg+4, SWP_NOZORDER|SWP_NOACTIVATE);
  2643. hdwp = DeferWindowPos(hdwp, m_hLogMsgControl, *this, m_xSliderPos+4, m_ySliderPos+8+FILTERBOXHEIGHT, width-m_xSliderPos-4, height-m_bottommarg-m_ySliderPos-4, SWP_NOZORDER|SWP_NOACTIVATE);
  2644. @@ -2872,9 +3409,13 @@
  2645. loglist.left, treelist.top+5, 0, 0, SWP_NOACTIVATE|SWP_NOOWNERZORDER|SWP_NOZORDER|SWP_NOSIZE);
  2646.  
  2647. hdwp = DeferWindowPos(hdwp, m_hFilterControl, NULL,
  2648. - loglist.left+FILTERLABELWIDTH, treelist.top, loglist.right-FILTERLABELWIDTH, FILTERBOXHEIGHT,
  2649. + loglist.left+FILTERLABELWIDTH, treelist.top, loglist.right-loglist.left-FILTERLABELWIDTH - 160 - 4, FILTERBOXHEIGHT,
  2650. SWP_NOACTIVATE|SWP_NOOWNERZORDER|SWP_NOZORDER);
  2651.  
  2652. + hdwp = DeferWindowPos(hdwp, m_hCommitsRadio, NULL, loglist.right - m_radiossize + 4, treelist.top + 1, 80, FILTERBOXHEIGHT-1, SWP_NOACTIVATE|SWP_NOOWNERZORDER|SWP_NOZORDER);
  2653. + hdwp = DeferWindowPos(hdwp, m_hReviewsRadio, NULL, loglist.right - m_radiossize + 84, treelist.top + 1, 80, FILTERBOXHEIGHT-1, SWP_NOACTIVATE|SWP_NOOWNERZORDER|SWP_NOZORDER);
  2654. +
  2655. +
  2656. hdwp = DeferWindowPos(hdwp, m_hListControl, NULL,
  2657. loglist.left, treelist.top+FILTERBOXHEIGHT, loglist.right-loglist.left, loglist.bottom-treelist.top-FILTERBOXHEIGHT,
  2658. SWP_NOACTIVATE|SWP_NOOWNERZORDER|SWP_NOZORDER);
  2659. Index: src/MainDlg.h
  2660. ===================================================================
  2661. --- src/MainDlg.h (revision 605)
  2662. +++ src/MainDlg.h (working copy)
  2663. @@ -97,6 +97,8 @@
  2664. HWND m_hListControl;
  2665. HWND m_hLogMsgControl;
  2666. HWND m_hFilterControl;
  2667. + HWND m_hCommitsRadio;
  2668. + HWND m_hReviewsRadio;
  2669. HFONT m_font;
  2670.  
  2671. CListCtrl m_ListCtrl;
  2672. @@ -112,6 +114,8 @@
  2673. LONG m_xSliderPos;
  2674. LONG m_ySliderPos;
  2675. LONG m_bottommarg;
  2676. + LONG m_radiossize;
  2677. + bool m_commitsradio;
  2678.  
  2679. HFONT m_boldFont;
  2680.  
  2681. Index: src/Resources/CommitMonitor.rc
  2682. ===================================================================
  2683. --- src/Resources/CommitMonitor.rc (revision 605)
  2684. +++ src/Resources/CommitMonitor.rc (working copy)
  2685. @@ -21,7 +21,7 @@
  2686.  
  2687. #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_NEU)
  2688. LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
  2689. -#pragma code_page(1252)
  2690. +#pragma code_page(1250)
  2691.  
  2692. /////////////////////////////////////////////////////////////////////////////
  2693. //
  2694. @@ -103,8 +103,10 @@
  2695. LTEXT "",IDC_INFOLABEL,0,222,303,16
  2696. PUSHBUTTON "&Exit",IDC_EXIT,359,222,50,14
  2697. DEFPUSHBUTTON "&Hide",IDOK,304,222,50,14
  2698. - EDITTEXT IDC_FILTERSTRING,186,32,225,14,ES_AUTOHSCROLL
  2699. + EDITTEXT IDC_FILTERSTRING,186,32,141,14,ES_AUTOHSCROLL
  2700. LTEXT "Filter:",IDC_FILTERLABEL,158,34,26,8
  2701. + CONTROL "Commits",IDC_RADIO2,"Button",BS_AUTORADIOBUTTON,331,35,40,10
  2702. + CONTROL "Reviews",IDC_RADIO3,"Button",BS_AUTORADIOBUTTON,371,35,40,10
  2703. END
  2704.  
  2705. IDD_URLCONFIG DIALOGEX 0, 0, 336, 322
  2706. @@ -144,6 +146,7 @@
  2707. COMBOBOX IDC_SCCSCOMBO,89,23,110,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
  2708. LTEXT "Accurev Repository:",IDC_REPOLABEL,14,70,66,8
  2709. EDITTEXT IDC_ACCUREVREPO,89,67,233,14,ES_AUTOHSCROLL
  2710. + CONTROL "Check review requests",IDC_CHECKREVIEWS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,223,91,89,10
  2711. END
  2712.  
  2713. IDD_OPTIONS DIALOGEX 0, 0, 308, 293
  2714. @@ -270,6 +273,10 @@
  2715. BOTTOMMARGIN, 286
  2716. END
  2717.  
  2718. + IDD_FINDBAR, DIALOG
  2719. + BEGIN
  2720. + END
  2721. +
  2722. IDD_NEWERNOTIFYDLG, DIALOG
  2723. BEGIN
  2724. LEFTMARGIN, 7
  2725. @@ -365,7 +372,7 @@
  2726. POPUP "Popup"
  2727. BEGIN
  2728. MENUITEM "&Show Unified Diff", ID_MAIN_SHOWDIFF
  2729. - MENUITEM "&Open WebViewer", ID_POPUP_OPENWEBVIEWER, INACTIVE
  2730. + MENUITEM "&Open WebViewer", ID_POPUP_OPENWEBVIEWER
  2731. MENUITEM "&Remove", ID_MAIN_REMOVE
  2732. MENUITEM "&Mark as unread", ID_POPUP_MARKASUNREAD
  2733. MENUITEM "&Copy to clipboard", ID_MAIN_COPY
  2734. Index: src/Resources/resource.h
  2735. ===================================================================
  2736. --- src/Resources/resource.h (revision 605)
  2737. +++ src/Resources/resource.h (working copy)
  2738. @@ -104,6 +104,7 @@
  2739. #define IDC_NUMLOGS 1040
  2740. #define IDC_CHECK2 1041
  2741. #define IDC_NOTIFYCONNECTERROR 1041
  2742. +#define IDC_CHECKREVIEWS 1041
  2743. #define IDC_FILTERSTRING 1042
  2744. #define IDC_FILTERLABEL 1043
  2745. #define IDC_MAXLOGENTRIES 1044
  2746. @@ -130,6 +131,8 @@
  2747. #define IDC_ACCUDIFFCMDLABEL 1064
  2748. #define IDC_SCCSCOMBO 1065
  2749. #define IDC_EDIT2 1069
  2750. +#define IDC_RADIO2 1072
  2751. +#define IDC_RADIO3 1074
  2752. #define ID_FILE_OPENCOMMITMONITOR 32771
  2753. #define ID_MAIN_OPENCOMMITMONITOR 32772
  2754. #define ID_POPUP_OPENCOMMITMONITOR 32773
  2755. @@ -181,7 +184,7 @@
  2756. #define _APS_NO_MFC 1
  2757. #define _APS_NEXT_RESOURCE_VALUE 165
  2758. #define _APS_NEXT_COMMAND_VALUE 32836
  2759. -#define _APS_NEXT_CONTROL_VALUE 1070
  2760. +#define _APS_NEXT_CONTROL_VALUE 1073
  2761. #define _APS_NEXT_SYMED_VALUE 110
  2762. #endif
  2763. #endif
  2764. Index: src/ReviewBoardXml.cpp
  2765. ===================================================================
  2766. --- src/ReviewBoardXml.cpp (revision 0)
  2767. +++ src/ReviewBoardXml.cpp (revision 0)
  2768. @@ -0,0 +1,737 @@
  2769. +#include "stdafx.h"
  2770. +
  2771. +#include "ReviewBoardXml.h"
  2772. +#include "rapidxml.hpp"
  2773. +#include <fstream>
  2774. +#include <time.h>
  2775. +#include "timesupport.h"
  2776. +#include "Http.h"
  2777. +
  2778. +#include "UnicodeUtils.h"
  2779. +#include "AppUtils.h"
  2780. +
  2781. +using namespace rapidxml;
  2782. +using namespace std;
  2783. +
  2784. +#define GET_REVIEWS_BASE "/api/review-requests/"
  2785. +#define GET_REVIEWS "/api/review-requests/?status=all&max-results=30"
  2786. +#define GET_REVIEWS_FROM "&time-added-from="
  2787. +#define GET_REVIEW_ID "/api/review-requests/"
  2788. +#define GET_UNIDIFF_1 "/api/review-requests/"
  2789. +#define GET_UNIDIFF_2 "/diffs/1/"
  2790. +#define GET_FILES_1 "/api/review-requests/"
  2791. +#define GET_FILES_2 "/diffs/1/files/"
  2792. +
  2793. +
  2794. +
  2795. +time_t ReviewBoard::Now()
  2796. +{
  2797. + return time(NULL);
  2798. +}
  2799. +
  2800. +time_t ReviewBoard::FromISO8601(const std::string& timestr)
  2801. +{
  2802. + if(timestr == "0000-00-00T00:00:00")
  2803. + return (time_t)0;
  2804. + struct tm tm;
  2805. + if(NULL == strptime(timestr.c_str(),"%Y-%m-%dT%H:%M:%S",&tm))
  2806. + return (time_t)0;
  2807. + return mktime(&tm);
  2808. +}
  2809. +
  2810. +std::string ReviewBoard::ToISO8601(time_t tim)
  2811. +{
  2812. + if(tim == 0)
  2813. + return string("0000-00-00T00:00:00");
  2814. + char buf[128];
  2815. + memset(buf,0,128);
  2816. + strftime(buf,128,"%Y-%m-%dT%H:%M:%S",localtime(&tim));
  2817. + return string(buf);
  2818. +}
  2819. +
  2820. +ReviewBoardXmlNode::~ReviewBoardXmlNode()
  2821. +{
  2822. + while(!children.empty())
  2823. + {
  2824. + delete children.back();
  2825. + children.pop_back();
  2826. + }
  2827. +}
  2828. +
  2829. +void ReviewBoardXmlNode::Load(void* rapidnode, ReviewBoardXmlNode * paren)
  2830. +{
  2831. + parent = paren;
  2832. +
  2833. + xml_node<> *rnode = (xml_node<>*)rapidnode;
  2834. +
  2835. + name.insert(0,rnode->name(),rnode->name_size());
  2836. +/*
  2837. + the xml output from the reviewboard doesn&#039;t contain attributes
  2838. + for (xml_attribute<> *attr = rnode->first_attribute();
  2839. + attr; attr = attr->next_attribute())
  2840. + {
  2841. + }
  2842. +*/
  2843. + for(rnode = rnode->first_node();rnode;rnode = rnode->next_sibling())
  2844. + {
  2845. + if(rnode->value_size() != 0)
  2846. + {
  2847. + string name, val;
  2848. + name.insert(0,rnode->name(),rnode->name_size());
  2849. + val.insert(0,rnode->value(),rnode->value_size());
  2850. + attributes[name] = val;
  2851. + }
  2852. + else
  2853. + {
  2854. + ReviewBoardXmlNode * child = new ReviewBoardXmlNode();
  2855. + child->Load(rnode,this);
  2856. + children.push_back(child);
  2857. + }
  2858. + }
  2859. +}
  2860. +
  2861. +ReviewBoardXmlNode const * ReviewBoardXmlNode::GetNodeWithAttributeAndValue(const std::string& attr, const std::string& val) const
  2862. +{
  2863. + map<string,string>::const_iterator itr = attributes.find(attr);
  2864. + if(itr != attributes.end() && itr->second == val)
  2865. + return this;
  2866. +
  2867. + for(vector<ReviewBoardXmlNode*>::const_iterator it = children.begin(); it != children.end(); ++it)
  2868. + {
  2869. + ReviewBoardXmlNode const * ret = (*it)->GetNodeWithAttributeAndValue(attr,val);
  2870. + if(ret)
  2871. + return ret;
  2872. + }
  2873. +
  2874. + return 0;
  2875. +}
  2876. +
  2877. +ReviewBoardXmlNode const * ReviewBoardXmlNode::GetFirstNodeWithAttribute(const std::string& nam, const std::string& attr) const
  2878. +{
  2879. + map<string,string>::const_iterator itr = attributes.find(attr);
  2880. + if(name == nam && itr != attributes.end())
  2881. + return this;
  2882. +
  2883. + for(vector<ReviewBoardXmlNode*>::const_iterator it = children.begin(); it != children.end(); ++it)
  2884. + {
  2885. + ReviewBoardXmlNode const * ret = (*it)->GetFirstNodeWithAttribute(nam, attr);
  2886. + if(ret)
  2887. + return ret;
  2888. + }
  2889. +
  2890. + return 0;
  2891. +}
  2892. +
  2893. +void ReviewBoardXmlNode::GetNodesVectorWithAttribute(const std::string& attr, std::vector<ReviewBoardXmlNode const*>& nodes) const
  2894. +{
  2895. + if(attributes.find(attr) != attributes.end())
  2896. + nodes.push_back(this);
  2897. + for(vector<ReviewBoardXmlNode*>::const_iterator it = children.begin(); it != children.end(); ++it)
  2898. + (*it)->GetNodesVectorWithAttribute(attr,nodes);
  2899. +}
  2900. +
  2901. +
  2902. +void ReviewBoardXmlDocument::Cleanup()
  2903. +{
  2904. + if(root)
  2905. + delete root;
  2906. + root = 0;
  2907. +}
  2908. +
  2909. +
  2910. +ReviewBoardXmlDocument::~ReviewBoardXmlDocument()
  2911. +{
  2912. + Cleanup();
  2913. +}
  2914. +
  2915. +void ReviewBoardXmlDocument::LoadBuf(const string& buffer)
  2916. +{
  2917. + char * buf;
  2918. + int len;
  2919. +
  2920. + len = buffer.length();
  2921. + buf = new char[len+1];
  2922. + memcpy(buf,buffer.c_str(),len);
  2923. + buf[len] = 0;
  2924. +
  2925. + xml_document<> doc; // character type defaults to char
  2926. + doc.parse<0>(buf); // 0 means default parse flags
  2927. +
  2928. + xml_node<> * rapidnode = doc.first_node();
  2929. +
  2930. + root = new ReviewBoardXmlNode();
  2931. +
  2932. + if(rapidnode)
  2933. + root->Load(rapidnode,NULL);
  2934. +
  2935. + delete[] buf;
  2936. +
  2937. + value.write(buffer.c_str(),buffer.length());
  2938. +}
  2939. +
  2940. +void ReviewBoardXmlDocument::Load(const string& path)
  2941. +{
  2942. + Cleanup();
  2943. +
  2944. + stringstream sstrm;
  2945. + gHTTP.GetPageXml(path,sstrm);
  2946. +
  2947. + LoadBuf(sstrm.str());
  2948. +}
  2949. +
  2950. +void ReviewBoardTextDocument::Load(const string& path)
  2951. +{
  2952. + gHTTP.GetPageText(path,value);
  2953. +}
  2954. +
  2955. +void ReviewBoardReviewUnifiedDiff::Open(const char * review)
  2956. +{
  2957. + string path = GET_UNIDIFF_1;
  2958. + path += review;
  2959. + path += GET_UNIDIFF_2;
  2960. + Load(path);
  2961. +}
  2962. +
  2963. +void ReviewBoardAllReviews::Open(const char * from_time)
  2964. +{
  2965. + string path = GET_REVIEWS;
  2966. + if(from_time)
  2967. + {
  2968. + path += GET_REVIEWS_FROM;
  2969. + path += from_time;
  2970. + }
  2971. + Load(path);
  2972. +}
  2973. +
  2974. +void ReviewBoardReview::Open(const char * id)
  2975. +{
  2976. + string path = GET_REVIEW_ID;
  2977. + path += id;
  2978. + path += "/";
  2979. + Load(path);
  2980. +}
  2981. +
  2982. +void ReviewBoardReviewFiles::Open(const char * id)
  2983. +{
  2984. + string path = GET_FILES_1;
  2985. + path += id;
  2986. + path += GET_FILES_2;
  2987. + Load(path);
  2988. +}
  2989. +
  2990. +ReviewBoardXmlNode const * ReviewBoardXmlDocument::GetNodeWithAttributeAndValue(const std::string& attr, const std::string& val) const
  2991. +{
  2992. + if(root)
  2993. + return root->GetNodeWithAttributeAndValue(attr,val);
  2994. + return 0;
  2995. +}
  2996. +
  2997. +void ReviewBoardXmlDocument::GetNodesVectorWithAttribute(const std::string& attr, std::vector<ReviewBoardXmlNode const*>& nodes) const
  2998. +{
  2999. + if(root)
  3000. + return root->GetNodesVectorWithAttribute(attr,nodes);
  3001. +}
  3002. +
  3003. +ReviewBoardXmlNode const * ReviewBoardXmlDocument::GetFirstNodeWithAttribute(const std::string& name, const std::string& attr) const
  3004. +{
  3005. + if(root)
  3006. + return root->GetFirstNodeWithAttribute(name, attr);
  3007. + return 0;
  3008. +}
  3009. +
  3010. +
  3011. +typedef vector<ReviewBoardXmlNode const *> rbxvec;
  3012. +
  3013. +void ReviewBoard::GetAllNeededData_FirstPass(const char * from_time)
  3014. +{
  3015. + ReviewBoardAllReviews * rbd = new ReviewBoardAllReviews();
  3016. + rbd->Open(from_time);
  3017. + all_docs.push_back(rbd);
  3018. +
  3019. + rbxvec vec;
  3020. + rbd->GetNodesVectorWithAttribute("id",vec);
  3021. + for(rbxvec::const_iterator itr = vec.begin(); itr != vec.end(); ++itr)
  3022. + {
  3023. + string id = (*itr)->attributes.at("id");
  3024. + // if not cached already
  3025. + if(reviewid_to_files.find(id) == reviewid_to_files.end())
  3026. + {
  3027. + ReviewBoardReviewFiles * rbf = new ReviewBoardReviewFiles();
  3028. +
  3029. + rbf->Open(id.c_str());
  3030. + reviewid_to_files[id] = rbf;
  3031. +
  3032. + all_docs.push_back(rbf);
  3033. + }
  3034. + }
  3035. +}
  3036. +
  3037. +void ReviewBoard::GetSpecificReviewData_SecondPass(const std::vector<std::string>& branches)
  3038. +{
  3039. + for(R2Fmap::const_iterator itr = reviewid_to_files.begin(); itr != reviewid_to_files.end(); ++itr)
  3040. + {
  3041. + // if not cached already
  3042. + if(reviewid_to_review.find(itr->first) == reviewid_to_review.end())
  3043. + {
  3044. + if(itr->second->HasMatch(branches))
  3045. + {
  3046. + ReviewBoardReview * rbr = new ReviewBoardReview();
  3047. + rbr->Open(itr->first.c_str());
  3048. + rbr->reviewid = itr->first;
  3049. + reviewid_to_review[itr->first] = rbr;
  3050. + all_docs.push_back(rbr);
  3051. + }
  3052. + }
  3053. + }
  3054. +}
  3055. +
  3056. +bool ReviewBoardReviewFiles::HasMatch(const std::string& branch)
  3057. +{
  3058. + rbxvec vec;
  3059. + GetNodesVectorWithAttribute("source_file",vec);
  3060. + for(rbxvec::const_iterator it = vec.begin(); it != vec.end(); ++it)
  3061. + {
  3062. + const std::string& str = (*it)->attributes.at("source_file");
  3063. + if(str.find(branch) != string::npos)
  3064. + return true;
  3065. + }
  3066. + return false;
  3067. +}
  3068. +
  3069. +
  3070. +bool ReviewBoardReviewFiles::HasMatch(const std::vector<std::string>& branches)
  3071. +{
  3072. + rbxvec vec;
  3073. + GetNodesVectorWithAttribute("source_file",vec);
  3074. + for(rbxvec::const_iterator it = vec.begin(); it != vec.end(); ++it)
  3075. + {
  3076. + const std::string& str = (*it)->attributes.at("source_file");
  3077. + for(std::vector<std::string>::const_iterator itr = branches.begin(); itr != branches.end(); ++itr)
  3078. + {
  3079. + if(str.find(*itr) != string::npos)
  3080. + return true;
  3081. + }
  3082. + }
  3083. + return false;
  3084. +}
  3085. +
  3086. +ReviewBoard::~ReviewBoard()
  3087. +{
  3088. + // do not destroy while other threads are accessing the object
  3089. + guard.AcquireWriterLock();
  3090. + while(!all_docs.empty())
  3091. + {
  3092. + delete all_docs.back();
  3093. + all_docs.pop_back();
  3094. + }
  3095. +}
  3096. +
  3097. +vector<ReviewBoardXmlDocument*> const * ReviewBoard::GetReadOnlyData()
  3098. +{
  3099. + guard.AcquireReaderLock();
  3100. + return &all_docs;
  3101. +}
  3102. +
  3103. +void ReviewBoard::ReleaseReadOnlyData()
  3104. +{
  3105. + guard.ReleaseReaderLock();
  3106. +}
  3107. +
  3108. +void ReviewBoard::Save(const std::wstring& filename)
  3109. +{
  3110. + guard.AcquireReaderLock();
  3111. +
  3112. + if(filename != TEXT(""))
  3113. + savefilename = filename;
  3114. +
  3115. + if(savefilename == TEXT(""))
  3116. + savefilename = CAppUtils::GetDataDir() + TEXT("\\reviews");
  3117. +
  3118. + ofstream ofile(savefilename.c_str());
  3119. +
  3120. +
  3121. + ofile << ToISO8601(lastupdate) << endl;
  3122. + ofile << url_to_branch.size() << endl;
  3123. + for(U2Bmap::const_iterator itr = url_to_branch.begin(); itr != url_to_branch.end(); ++itr)
  3124. + {
  3125. + ofile << itr->first << endl;
  3126. + ofile << itr->second << endl;
  3127. + }
  3128. +
  3129. + ofile << reviewid_to_files.size() << endl;
  3130. +
  3131. + for(R2Fmap::const_iterator itr = reviewid_to_files.begin(); itr != reviewid_to_files.end(); ++itr)
  3132. + {
  3133. + ofile << itr->first << endl;
  3134. + ofile << itr->second->value.str().length() << endl;
  3135. + ofile.write(itr->second->value.str().c_str(),itr->second->value.str().length());
  3136. + }
  3137. +
  3138. + ofile << reviewid_to_review.size() << endl;
  3139. +
  3140. + for(R2Rmap::const_iterator itr = reviewid_to_review.begin(); itr != reviewid_to_review.end(); ++itr)
  3141. + {
  3142. + ofile << itr->first << endl;
  3143. + ofile << (itr->second->read ? "1" : "0") << endl;
  3144. + ofile << itr->second->value.str().length() << endl;
  3145. + ofile.write(itr->second->value.str().c_str(),itr->second->value.str().length());
  3146. + }
  3147. +
  3148. +
  3149. + ofile.close();
  3150. +
  3151. + guard.ReleaseReaderLock();
  3152. +}
  3153. +
  3154. +void ReviewBoard::Load(const std::wstring& filename)
  3155. +{
  3156. + guard.AcquireWriterLock();
  3157. +
  3158. + if(filename != TEXT(""))
  3159. + savefilename = filename;
  3160. +
  3161. + if(savefilename == TEXT(""))
  3162. + savefilename = CAppUtils::GetDataDir() + TEXT("\\reviews");
  3163. +
  3164. + ifstream ifile(savefilename.c_str());
  3165. +
  3166. +
  3167. + std::string timestr;
  3168. + ifile >> timestr;
  3169. + lastupdate = FromISO8601(timestr);
  3170. + int numbranches;
  3171. + ifile >> numbranches;
  3172. + for(int i = 0; i < numbranches; ++i)
  3173. + {
  3174. + string url, branch;
  3175. + ifile >> url;
  3176. + ifile >> branch;
  3177. + url_to_branch[url] = branch;
  3178. + }
  3179. +
  3180. + int filesnum;
  3181. + ifile >> filesnum;
  3182. +
  3183. + for(int i = 0; i < filesnum; ++i)
  3184. + {
  3185. + string review;
  3186. + ifile >> review;
  3187. + int len;
  3188. + ifile >> len;
  3189. + ifile.get();// eat eol
  3190. + string buf(len,0);
  3191. + int j = 0;
  3192. + while(j < len)
  3193. + {
  3194. + buf[j] = ifile.get();
  3195. + ++j;
  3196. + }
  3197. + ReviewBoardReviewFiles * r = new ReviewBoardReviewFiles();
  3198. + r->LoadBuf(buf);
  3199. + reviewid_to_files[review] = r;
  3200. + all_docs.push_back(r);
  3201. + }
  3202. +
  3203. + ifile >> filesnum;
  3204. +
  3205. + for(int i = 0; i < filesnum; ++i)
  3206. + {
  3207. + string review;
  3208. + ifile >> review;
  3209. + int read;
  3210. + ifile >> read;
  3211. + int len;
  3212. + ifile >> len;
  3213. + ifile.get();// eat eol
  3214. + string buf(len,0);
  3215. + int j = 0;
  3216. + while(j < len)
  3217. + {
  3218. + buf[j] = ifile.get();
  3219. + ++j;
  3220. + }
  3221. + ReviewBoardReview * r = new ReviewBoardReview();
  3222. + r->read = (read == 1);
  3223. + r->LoadBuf(buf);
  3224. + r->reviewid = review;
  3225. + reviewid_to_review[review] = r;
  3226. + all_docs.push_back(r);
  3227. + }
  3228. +
  3229. +
  3230. + ifile.close();
  3231. +
  3232. + guard.ReleaseWriterLock();
  3233. +}
  3234. +
  3235. +int ReviewBoard::DoUpdate()
  3236. +{
  3237. + guard.AcquireWriterLock();
  3238. +
  3239. + // set already dl-d reviews to old
  3240. + for(R2Rmap::iterator itr = reviewid_to_review.begin();itr != reviewid_to_review.end(); ++itr)
  3241. + itr->second->old = true;
  3242. +
  3243. + int filesnum = reviewid_to_files.size();
  3244. + if(lastupdate != 0)
  3245. + GetAllNeededData_FirstPass(ToISO8601(lastupdate).c_str());
  3246. + else
  3247. + GetAllNeededData_FirstPass();
  3248. +
  3249. + lastupdate = time(NULL);
  3250. + filesnum -= reviewid_to_files.size();
  3251. +
  3252. + std::vector<std::string> vec;
  3253. +
  3254. + for(U2Bmap::const_iterator itr = url_to_branch.begin(); itr != url_to_branch.end(); ++itr)
  3255. + {
  3256. + vec.push_back(itr->second);
  3257. + }
  3258. +
  3259. + int revsnum = reviewid_to_review.size();
  3260. + GetSpecificReviewData_SecondPass(vec);
  3261. + revsnum -= reviewid_to_review.size();
  3262. +
  3263. + guard.ReleaseWriterLock();
  3264. + if(filesnum < 0)
  3265. + Save();
  3266. +
  3267. + newreviews = -revsnum;
  3268. +
  3269. + return newreviews;
  3270. +}
  3271. +
  3272. +void ReviewBoard::AddBranchToMonitor(const string& url, const string& branch)
  3273. +{
  3274. + guard.AcquireWriterLock();
  3275. + url_to_branch[url] = branch;
  3276. + guard.ReleaseWriterLock();
  3277. +}
  3278. +
  3279. +void ReviewBoard::AddBranchToMonitor(const wstring& wurl)
  3280. +{
  3281. + std::string url = CUnicodeUtils::StdGetUTF8(wurl);
  3282. + std::string branch = url;
  3283. + size_t pos = branch.find("/code/");
  3284. + if(pos != string::npos)
  3285. + {
  3286. + branch = branch.substr(pos+6);
  3287. + }
  3288. + AddBranchToMonitor(url,branch);
  3289. +}
  3290. +
  3291. +
  3292. +void ReviewBoard::RemoveBranchToMonitor(const wstring& wurl)
  3293. +{
  3294. + RemoveBranchToMonitor(CUnicodeUtils::StdGetUTF8(wurl));
  3295. +}
  3296. +
  3297. +
  3298. +void ReviewBoard::RemoveBranchToMonitor(const string& url)
  3299. +{
  3300. + guard.AcquireWriterLock();
  3301. + U2Bmap::iterator itr = url_to_branch.find(url);
  3302. + if(itr != url_to_branch.end())
  3303. + url_to_branch.erase(url);
  3304. + guard.ReleaseWriterLock();
  3305. +}
  3306. +
  3307. +bool ReviewBoard::IsUrlMonitored(const std::string& url)
  3308. +{
  3309. + guard.AcquireReaderLock();
  3310. + U2Bmap::const_iterator itr = url_to_branch.find(url);
  3311. + if(itr != url_to_branch.end())
  3312. + {
  3313. + guard.ReleaseReaderLock();
  3314. + return true;
  3315. + }
  3316. + guard.ReleaseReaderLock();
  3317. + return false;
  3318. +}
  3319. +
  3320. +bool ReviewBoard::IsUrlMonitored(const std::wstring& wurl)
  3321. +{
  3322. + std::string url = CUnicodeUtils::StdGetUTF8(wurl);
  3323. + return IsUrlMonitored(url);
  3324. +}
  3325. +
  3326. +typedef std::vector<ReviewBoardReview const *> rbrvec;
  3327. +void ReviewBoard::GetReviewsForUrl(const std::wstring& wurl, WR2Rmap& vec)
  3328. +{
  3329. + std::string url = CUnicodeUtils::StdGetUTF8(wurl);
  3330. + guard.AcquireReaderLock();
  3331. + U2Bmap::const_iterator itr = url_to_branch.find(url);
  3332. + if(itr != url_to_branch.end())
  3333. + {
  3334. + for(R2Fmap::const_iterator filesitr = reviewid_to_files.begin(); filesitr != reviewid_to_files.end(); ++filesitr)
  3335. + {
  3336. + if(filesitr->second->HasMatch(itr->second))
  3337. + {
  3338. + R2Rmap::const_iterator ritr = reviewid_to_review.find(filesitr->first);
  3339. + if(ritr != reviewid_to_review.end())
  3340. + vec[UTF8ToWide(ritr->first)] = (const_cast<ReviewBoardReview const *>(ritr->second));
  3341. + }
  3342. + }
  3343. + }
  3344. + guard.ReleaseReaderLock();
  3345. +}
  3346. +
  3347. +int ReviewBoard::GetUnreadReviews()
  3348. +{
  3349. + guard.AcquireReaderLock();
  3350. + int ret = 0;
  3351. + for(R2Rmap::const_iterator ritr = reviewid_to_review.begin(); ritr != reviewid_to_review.end(); ++ritr)
  3352. + {
  3353. + if(!ritr->second->read)
  3354. + ++ret;
  3355. + }
  3356. + guard.ReleaseReaderLock();
  3357. + return ret;
  3358. +}
  3359. +
  3360. +int ReviewBoard::GetNewReviewsForUrl(const std::wstring& url)
  3361. +{
  3362. + WR2Rmap v;
  3363. + GetReviewsForUrl(url,v);
  3364. + int n = 0;
  3365. + for(WR2Rmap::const_iterator it = v.begin(); it != v.end(); ++it)
  3366. + {
  3367. + if(!it->second->old)
  3368. + n++;
  3369. + }
  3370. + return n;
  3371. +}
  3372. +
  3373. +int ReviewBoard::GetUnreadReviewsForUrl(const std::wstring& url)
  3374. +{
  3375. + WR2Rmap v;
  3376. + GetReviewsForUrl(url,v);
  3377. + int n = 0;
  3378. + for(WR2Rmap::const_iterator it = v.begin(); it != v.end(); ++it)
  3379. + {
  3380. + if(!it->second->read)
  3381. + n++;
  3382. + }
  3383. + return n;
  3384. +}
  3385. +
  3386. +void ReviewBoard::ReviewSetRead(const std::string& rev, bool b)
  3387. +{
  3388. + guard.AcquireWriterLock();
  3389. + R2Rmap::iterator ritr = reviewid_to_review.find(rev);
  3390. + bool needsave = false;
  3391. + if(ritr != reviewid_to_review.end())
  3392. + {
  3393. + if(ritr->second->read != b)
  3394. + needsave = true;
  3395. + ritr->second->read = b;
  3396. + }
  3397. + guard.ReleaseWriterLock();
  3398. + if(needsave)
  3399. + Save();
  3400. +}
  3401. +
  3402. +void ReviewBoard::DeleteReview(const std::string& rev)
  3403. +{
  3404. + guard.AcquireWriterLock();
  3405. + R2Rmap::iterator ritr = reviewid_to_review.find(rev);
  3406. + bool needsave = false;
  3407. + if(ritr != reviewid_to_review.end())
  3408. + {
  3409. + std::vector<ReviewBoardXmlDocument*>::iterator it2 = all_docs.begin();
  3410. + while(it2 != all_docs.end())
  3411. + {
  3412. + if((*it2) == (ritr->second))
  3413. + break;
  3414. + it2++;
  3415. + }
  3416. +
  3417. + if(!ritr->second->old)
  3418. + newreviews--;
  3419. +
  3420. + delete (*it2);
  3421. +
  3422. + if(it2 != all_docs.end())
  3423. + all_docs.erase(it2);
  3424. +
  3425. + reviewid_to_review.erase(ritr);
  3426. + needsave = true;
  3427. + }
  3428. + guard.ReleaseWriterLock();
  3429. + if(needsave)
  3430. + Save();
  3431. +}
  3432. +
  3433. +
  3434. +void ReviewBoardTextDocument::Save(const wstring& filename)
  3435. +{
  3436. + ofstream ofile(filename.c_str());
  3437. + ofile.write(value.str().c_str(),value.str().length());
  3438. + ofile.close();
  3439. +}
  3440. +
  3441. +void ReviewBoard::ReviewsSetRead(const std::wstring& wurl)
  3442. +{
  3443. + std::string url = CUnicodeUtils::StdGetUTF8(wurl);
  3444. + guard.AcquireWriterLock();
  3445. + U2Bmap::const_iterator itr = url_to_branch.find(url);
  3446. + if(itr != url_to_branch.end())
  3447. + {
  3448. + for(R2Fmap::const_iterator filesitr = reviewid_to_files.begin(); filesitr != reviewid_to_files.end(); ++filesitr)
  3449. + {
  3450. + if(filesitr->second->HasMatch(itr->second))
  3451. + {
  3452. + R2Rmap::iterator ritr = reviewid_to_review.find(filesitr->first);
  3453. + if(ritr != reviewid_to_review.end())
  3454. + ritr->second->read = true;
  3455. + }
  3456. + }
  3457. + }
  3458. + guard.ReleaseWriterLock();
  3459. + Save();
  3460. +}
  3461. +
  3462. +wstring ReviewBoardReview::Author() const
  3463. +{
  3464. + ReviewBoardXmlNode const * n = GetFirstNodeWithAttribute("submitter","title");
  3465. + if(n)
  3466. + {
  3467. + return UTF8ToWide(n->attributes.at("title"));
  3468. + }
  3469. + return TEXT("no author");
  3470. +}
  3471. +
  3472. +wstring ReviewBoardReview::Date() const
  3473. +{
  3474. + ReviewBoardXmlNode const * n = GetFirstNodeWithAttribute("review_request","time_added");
  3475. + if(n)
  3476. + {
  3477. + return UTF8ToWide(n->attributes.at("time_added"));
  3478. + }
  3479. + return TEXT("(no date)");
  3480. +}
  3481. +
  3482. +wstring ReviewBoardReview::Url() const
  3483. +{
  3484. + ReviewBoardXmlNode const * n = GetFirstNodeWithAttribute("self","href");
  3485. + if(n)
  3486. + {
  3487. + return UTF8ToWide(n->attributes.at("href"));
  3488. + }
  3489. + return TEXT("");
  3490. +}
  3491. +
  3492. +wstring ReviewBoardReview::Message() const
  3493. +{
  3494. + ReviewBoardXmlNode const * n = GetFirstNodeWithAttribute("review_request","description");
  3495. + if(n)
  3496. + {
  3497. + return UTF8ToWide(n->attributes.at("description"));
  3498. + }
  3499. + return TEXT("(no description)");
  3500. +}
  3501. +
  3502. +wstring ReviewBoardReview::Reviewid() const
  3503. +{
  3504. + return UTF8ToWide(reviewid);
  3505. +}
  3506. Index: src/ReviewBoardXml.h
  3507. ===================================================================
  3508. --- src/ReviewBoardXml.h (revision 0)
  3509. +++ src/ReviewBoardXml.h (revision 0)
  3510. @@ -0,0 +1,167 @@
  3511. +#ifndef REVIEWBOARDXML_H
  3512. +#define REVIEWBOARDXML_H
  3513. +
  3514. +#include "stdafx.h"
  3515. +
  3516. +#include "ReaderWriterLock.h"
  3517. +#include "Singleton.h"
  3518. +
  3519. +#include <string>
  3520. +#include <sstream>
  3521. +#include <vector>
  3522. +#include <map>
  3523. +
  3524. +class ReviewBoardXmlNode
  3525. +{
  3526. +public:
  3527. + std::string name;
  3528. + ReviewBoardXmlNode * parent;
  3529. + std::map<std::string,std::string> attributes;
  3530. + std::vector<ReviewBoardXmlNode*> children;
  3531. +public:
  3532. + ReviewBoardXmlNode() : parent(0) {}
  3533. + ~ReviewBoardXmlNode();
  3534. + void Load(void* rapidnode, ReviewBoardXmlNode * parent);
  3535. + ReviewBoardXmlNode const * GetNodeWithAttributeAndValue(const std::string& attr, const std::string& val) const;
  3536. + void GetNodesVectorWithAttribute(const std::string& attr, std::vector<ReviewBoardXmlNode const*>& nodes) const;
  3537. + ReviewBoardXmlNode const * GetFirstNodeWithAttribute(const std::string& name, const std::string& attr) const;
  3538. +};
  3539. +
  3540. +enum ReviewBoardDocumentType
  3541. +{
  3542. + RBDT_BASE = 0,
  3543. + RBDT_XML,
  3544. + RBDT_TEXT,
  3545. + RBDT_UNIDIFF,
  3546. + RBDT_ALLREVIEWS,
  3547. + RBDT_REVIEW,
  3548. + RBDT_REVIEWFILES
  3549. +};
  3550. +
  3551. +class ReviewBoardDocument
  3552. +{
  3553. +public:
  3554. + std::stringstream value;
  3555. +public:
  3556. + virtual ReviewBoardDocumentType GetType() { return RBDT_BASE; }
  3557. +};
  3558. +
  3559. +class ReviewBoardXmlDocument : public ReviewBoardDocument
  3560. +{
  3561. +public:
  3562. + ReviewBoardXmlNode * root;
  3563. +public:
  3564. + ReviewBoardXmlDocument() : root(0) {}
  3565. + ~ReviewBoardXmlDocument();
  3566. + void Cleanup();
  3567. + void LoadBuf(const std::string& buf);
  3568. + void Load(const std::string& path);
  3569. + ReviewBoardXmlNode const * GetNodeWithAttributeAndValue(const std::string& attr, const std::string& val) const;
  3570. + void GetNodesVectorWithAttribute(const std::string& attr, std::vector<ReviewBoardXmlNode const*>& nodes) const;
  3571. + ReviewBoardXmlNode const * GetFirstNodeWithAttribute(const std::string& name, const std::string& attr) const;
  3572. + virtual ReviewBoardDocumentType GetType() { return RBDT_XML; }
  3573. +};
  3574. +
  3575. +class ReviewBoardTextDocument : public ReviewBoardDocument
  3576. +{
  3577. +public:
  3578. + void Load(const std::string& path);
  3579. + virtual ReviewBoardDocumentType GetType() { return RBDT_TEXT; }
  3580. + void Save(const std::wstring& filename);
  3581. +};
  3582. +
  3583. +class ReviewBoardReviewUnifiedDiff : public ReviewBoardTextDocument
  3584. +{
  3585. +public:
  3586. + std::string review;
  3587. +public:
  3588. + void Open(const char * reviewrequest);
  3589. + virtual ReviewBoardDocumentType GetType() { return RBDT_UNIDIFF; }
  3590. +};
  3591. +
  3592. +class ReviewBoardAllReviews : public ReviewBoardXmlDocument
  3593. +{
  3594. +public:
  3595. + void Open(const char * from_time = 0);
  3596. + virtual ReviewBoardDocumentType GetType() { return RBDT_ALLREVIEWS; }
  3597. +};
  3598. +
  3599. +class ReviewBoardReview : public ReviewBoardXmlDocument
  3600. +{
  3601. +public:
  3602. + std::string reviewid;
  3603. + bool read;
  3604. + bool old;
  3605. + ReviewBoardReview() : read(false), old(false) {}
  3606. +public:
  3607. + void Open(const char * reviewid);
  3608. + virtual ReviewBoardDocumentType GetType() { return RBDT_REVIEW; }
  3609. + std::wstring Author() const;
  3610. + std::wstring Message() const;
  3611. + std::wstring Date() const;
  3612. + std::wstring Reviewid() const;
  3613. + std::wstring Url() const;
  3614. +};
  3615. +
  3616. +class ReviewBoardReviewFiles : public ReviewBoardXmlDocument
  3617. +{
  3618. +public:
  3619. + void Open(const char * reviewid);
  3620. + virtual ReviewBoardDocumentType GetType() { return RBDT_REVIEWFILES; }
  3621. + bool HasMatch(const std::vector<std::string>& branches);
  3622. + bool HasMatch(const std::string& branch);
  3623. +};
  3624. +
  3625. +class ReviewBoard
  3626. +{
  3627. +private:
  3628. + friend class Singleton<ReviewBoard>;
  3629. + ReviewBoard() : lastupdate(0), savefilename(TEXT("")), newreviews(0) {}
  3630. + std::vector<ReviewBoardXmlDocument*> all_docs;
  3631. + typedef std::map<std::string/*review id*/,ReviewBoardReviewFiles*> R2Fmap;
  3632. + R2Fmap reviewid_to_files;
  3633. + typedef std::map<std::string/*review id*/,ReviewBoardReview*> R2Rmap;
  3634. + R2Rmap reviewid_to_review;
  3635. + CReaderWriterLock guard;
  3636. + typedef std::map<std::string,std::string> U2Bmap;
  3637. + U2Bmap url_to_branch;
  3638. + time_t lastupdate;
  3639. + time_t Now();
  3640. + time_t FromISO8601(const std::string& timestr);
  3641. + std::string ToISO8601(time_t time);
  3642. + int newreviews;
  3643. +public:
  3644. + std::wstring savefilename;
  3645. + void GetAllNeededData_FirstPass(const char * from_time = 0);
  3646. + void GetSpecificReviewData_SecondPass(const std::vector<std::string>& branches);
  3647. + std::stringstream GetReviewUnifiedDiff(const char * review);
  3648. + ~ReviewBoard();
  3649. +
  3650. + // need lock funcs
  3651. + std::vector<ReviewBoardXmlDocument*> const * GetReadOnlyData();
  3652. + void GetReadLock() { guard.AcquireReaderLock(); }
  3653. + void ReleaseReadOnlyData();
  3654. + bool IsUrlMonitored(const std::string& branch);
  3655. + bool IsUrlMonitored(const std::wstring& url);
  3656. + typedef std::map<std::wstring, ReviewBoardReview const *> WR2Rmap;
  3657. + void GetReviewsForUrl(const std::wstring& url, WR2Rmap& vec);
  3658. + int GetNewReviewsForUrl(const std::wstring& url);
  3659. + int GetUnreadReviewsForUrl(const std::wstring& url);
  3660. +
  3661. + void DeleteReview(const std::string& rev);
  3662. + void ReviewSetRead(const std::string& rev, bool read = true);
  3663. + void ReviewsSetRead(const std::wstring& url);
  3664. + void Load(const std::wstring& filepath = TEXT(""));
  3665. + void Save(const std::wstring& filepath = TEXT(""));
  3666. + void AddBranchToMonitor(const std::wstring& url);
  3667. + void AddBranchToMonitor(const std::string& url, const std::string& branch);
  3668. + void RemoveBranchToMonitor(const std::string& url);
  3669. + void RemoveBranchToMonitor(const std::wstring& url);
  3670. + int DoUpdate();
  3671. + int GetUnreadReviews();
  3672. +};
  3673. +
  3674. +#define gReviewBoard (*(Singleton<ReviewBoard>::GetInstance()))
  3675. +#define gReviewBoard_Cleanup Singleton<ReviewBoard>::Cleanup()
  3676. +
  3677. +#endif
  3678. Index: src/Singleton.h
  3679. ===================================================================
  3680. --- src/Singleton.h (revision 0)
  3681. +++ src/Singleton.h (revision 0)
  3682. @@ -0,0 +1,34 @@
  3683. +#ifndef SINGLETON_H__
  3684. +#define SINGLETON_H__
  3685. +
  3686. +
  3687. +template<typename T>
  3688. +class Singleton
  3689. +{
  3690. +private:
  3691. + static T * _instance;
  3692. + Singleton()
  3693. + {
  3694. + }
  3695. +public:
  3696. + static T * GetInstance()
  3697. + {
  3698. + if(!_instance)
  3699. + {
  3700. + _instance = new T();
  3701. + }
  3702. + return _instance;
  3703. + }
  3704. + static void Cleanup()
  3705. + {
  3706. + if(_instance)
  3707. + {
  3708. + delete _instance;
  3709. + _instance = 0;
  3710. + }
  3711. + }
  3712. +};
  3713. +
  3714. +template <typename T> T* Singleton<T>::_instance = 0;
  3715. +
  3716. +#endif
  3717. Index: src/timesupport.cc
  3718. ===================================================================
  3719. --- src/timesupport.cc (revision 0)
  3720. +++ src/timesupport.cc (revision 0)
  3721. @@ -0,0 +1,248 @@
  3722. +// Copyright 2009 Google Inc.
  3723. +//
  3724. +// Licensed under the Apache License, Version 2.0 (the "License");
  3725. +// you may not use this file except in compliance with the License.
  3726. +// You may obtain a copy of the License at
  3727. +//
  3728. +// http://www.apache.org/licenses/LICENSE-2.0
  3729. +//
  3730. +// Unless required by applicable law or agreed to in writing, software
  3731. +// distributed under the License is distributed on an "AS IS" BASIS,
  3732. +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  3733. +// See the License for the specific language governing permissions and
  3734. +// limitations under the License.
  3735. +
  3736. +
  3737. +#include "stdafx.h"
  3738. +
  3739. +#include "timesupport.h"
  3740. +
  3741. +#include <time.h>
  3742. +#include <ctype.h>
  3743. +#include <string.h>
  3744. +
  3745. +#ifdef WIN32
  3746. +// Implement strptime under windows
  3747. +static const char* kWeekFull[] = {
  3748. + "Sunday", "Monday", "Tuesday", "Wednesday",
  3749. + "Thursday", "Friday", "Saturday"
  3750. +};
  3751. +
  3752. +static const char* kWeekAbbr[] = {
  3753. + "Sun", "Mon", "Tue", "Wed",
  3754. + "Thu", "Fri", "Sat"
  3755. +};
  3756. +
  3757. +static const char* kMonthFull[] = {
  3758. + "January", "February", "March", "April", "May", "June",
  3759. + "July", "August", "September", "October", "November", "December"
  3760. +};
  3761. +
  3762. +static const char* kMonthAbbr[] = {
  3763. + "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  3764. + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
  3765. +};
  3766. +
  3767. +static const char* _parse_num(const char* s, int low, int high, int* value) {
  3768. + const char* p = s;
  3769. + for (*value = 0; *p != NULL && isdigit(*p); ++p) {
  3770. + *value = (*value) * 10 + static_cast<int>(*p) - static_cast<int>(&#039;0&#039;);
  3771. + }
  3772. +
  3773. + if (p == s || *value < low || *value > high) return NULL;
  3774. + return p;
  3775. +}
  3776. +
  3777. +static char* _strptime(const char *s, const char *format, struct tm *tm) {
  3778. + while (*format != NULL && *s != NULL) {
  3779. + if (*format != &#039;%&#039;) {
  3780. + if (*s != *format) return NULL;
  3781. +
  3782. + ++format;
  3783. + ++s;
  3784. + continue;
  3785. + }
  3786. +
  3787. + ++format;
  3788. + int len = 0;
  3789. + switch (*format) {
  3790. + // weekday name.
  3791. + case &#039;a&#039;:
  3792. + case &#039;A&#039;:
  3793. + tm->tm_wday = -1;
  3794. + for (int i = 0; i < 7; ++i) {
  3795. + len = static_cast<int>(strlen(kWeekAbbr[i]));
  3796. + if (strnicmp(kWeekAbbr[i], s, len) == 0) {
  3797. + tm->tm_wday = i;
  3798. + break;
  3799. + }
  3800. +
  3801. + len = static_cast<int>(strlen(kWeekFull[i]));
  3802. + if (strnicmp(kWeekFull[i], s, len) == 0) {
  3803. + tm->tm_wday = i;
  3804. + break;
  3805. + }
  3806. + }
  3807. + if (tm->tm_wday == -1) return NULL;
  3808. + s += len;
  3809. + break;
  3810. +
  3811. + // month name.
  3812. + case &#039;b&#039;:
  3813. + case &#039;B&#039;:
  3814. + case &#039;h&#039;:
  3815. + tm->tm_mon = -1;
  3816. + for (int i = 0; i < 12; ++i) {
  3817. + len = static_cast<int>(strlen(kMonthAbbr[i]));
  3818. + if (strnicmp(kMonthAbbr[i], s, len) == 0) {
  3819. + tm->tm_mon = i;
  3820. + break;
  3821. + }
  3822. +
  3823. + len = static_cast<int>(strlen(kMonthFull[i]));
  3824. + if (strnicmp(kMonthFull[i], s, len) == 0) {
  3825. + tm->tm_mon = i;
  3826. + break;
  3827. + }
  3828. + }
  3829. + if (tm->tm_mon == -1) return NULL;
  3830. + s += len;
  3831. + break;
  3832. +
  3833. + // month [1, 12].
  3834. + case &#039;m&#039;:
  3835. + s = _parse_num(s, 1, 12, &tm->tm_mon);
  3836. + if (s == NULL) return NULL;
  3837. + --tm->tm_mon;
  3838. + break;
  3839. +
  3840. + // day [1, 31].
  3841. + case &#039;d&#039;:
  3842. + case &#039;e&#039;:
  3843. + s = _parse_num(s, 1, 31, &tm->tm_mday);
  3844. + if (s == NULL) return NULL;
  3845. + break;
  3846. +
  3847. + // hour [0, 23].
  3848. + case &#039;H&#039;:
  3849. + s = _parse_num(s, 0, 23, &tm->tm_hour);
  3850. + if (s == NULL) return NULL;
  3851. + break;
  3852. +
  3853. + // minute [0, 59]
  3854. + case &#039;M&#039;:
  3855. + s = _parse_num(s, 0, 59, &tm->tm_min);
  3856. + if (s == NULL) return NULL;
  3857. + break;
  3858. +
  3859. + // seconds [0, 60]. 60 is for leap year.
  3860. + case &#039;S&#039;:
  3861. + s = _parse_num(s, 0, 60, &tm->tm_sec);
  3862. + if (s == NULL) return NULL;
  3863. + break;
  3864. +
  3865. + // year [1900, 9999].
  3866. + case &#039;Y&#039;:
  3867. + s = _parse_num(s, 1900, 9999, &tm->tm_year);
  3868. + if (s == NULL) return NULL;
  3869. + tm->tm_year -= 1900;
  3870. + break;
  3871. +
  3872. + // year [0, 99].
  3873. + case &#039;y&#039;:
  3874. + s = _parse_num(s, 0, 99, &tm->tm_year);
  3875. + if (s == NULL) return NULL;
  3876. + if (tm->tm_year <= 68) {
  3877. + tm->tm_year += 100;
  3878. + }
  3879. + break;
  3880. +
  3881. + // arbitray whitespace.
  3882. + case &#039;t&#039;:
  3883. + case &#039;n&#039;:
  3884. + while (isspace(*s)) ++s;
  3885. + break;
  3886. +
  3887. + // &#039;%&#039;.
  3888. + case &#039;%&#039;:
  3889. + if (*s != &#039;%&#039;) return NULL;
  3890. + ++s;
  3891. + break;
  3892. +
  3893. + // All the other format are not supported.
  3894. + default:
  3895. + return NULL;
  3896. + }
  3897. + ++format;
  3898. + }
  3899. +
  3900. + if (*format != NULL) {
  3901. + return NULL;
  3902. + } else {
  3903. + return const_cast<char*>(s);
  3904. + }
  3905. +}
  3906. +
  3907. +char* strptime(const char *buf, const char *fmt, struct tm *tm) {
  3908. + return _strptime(buf, fmt, tm);
  3909. +}
  3910. +#endif // WIN32
  3911. +
  3912. +#if defined(__linux__) || defined(__unix__)
  3913. +
  3914. +time_t _mkgmtime(const struct tm *tm) {
  3915. + // Month-to-day offset for non-leap-years.
  3916. + static const int month_day[12] =
  3917. + {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
  3918. +
  3919. + // Most of the calculation is easy; leap years are the main difficulty.
  3920. + int month = tm->tm_mon % 12;
  3921. + int year = tm->tm_year + tm->tm_mon / 12;
  3922. + if (month < 0) { // Negative values % 12 are still negative.
  3923. + month += 12;
  3924. + --year;
  3925. + }
  3926. +
  3927. + // This is the number of Februaries since 1900.
  3928. + const int year_for_leap = (month > 1) ? year + 1 : year;
  3929. +
  3930. + time_t rt = tm->tm_sec // Seconds
  3931. + + 60 * (tm->tm_min // Minute = 60 seconds
  3932. + + 60 * (tm->tm_hour // Hour = 60 minutes
  3933. + + 24 * (month_day[month] + tm->tm_mday - 1 // Day = 24 hours
  3934. + + 365 * (year - 70) // Year = 365 days
  3935. + + (year_for_leap - 69) / 4 // Every 4 years is leap...
  3936. + - (year_for_leap - 1) / 100 // Except centuries...
  3937. + + (year_for_leap + 299) / 400))); // Except 400s.
  3938. + return rt < 0 ? -1 : rt;
  3939. +}
  3940. +
  3941. +#endif
  3942. +
  3943. +bool ParseRfcTime(const char* buf, struct tm *tm) {
  3944. + memset(tm, 0, sizeof(*tm));
  3945. + return
  3946. + strptime(buf, "%a, %e %b %Y %H:%M:%S", tm) || // RFC 822/1123
  3947. + strptime(buf, "%a, %e-%b-%y %H:%M:%S", tm) || // RFC 850/1036
  3948. + strptime(buf, "%a %b %e %H:%M:%S %Y", tm); // asctime()
  3949. +}
  3950. +
  3951. +// Covert the time to W3C Datetime formats.
  3952. +// See http://www.w3.org/TR/NOTE-datetime
  3953. +const std::string FormatW3CTime(const time_t &time) {
  3954. + char buffer[80];
  3955. + strftime(buffer, 80,
  3956. + "%Y-%m-%dT%H:%M:%SZ", // like 2001-12-31T23:59:59Z
  3957. + gmtime(&time));
  3958. + return buffer;
  3959. +}
  3960. +
  3961. +std::string FormatHttpDate(time_t t) {
  3962. + // Convert &#039;time_t&#039; to struct tm (GMT)
  3963. + tm* gmt_p = gmtime(&t);
  3964. +
  3965. + // Convert struct tm to HTTP-date string
  3966. + char buff[256];
  3967. + strftime(buff, sizeof(buff), "%a, %d %b %Y %H:%M:%S GMT", gmt_p);
  3968. + return buff;
  3969. +}
  3970. Index: src/timesupport.h
  3971. ===================================================================
  3972. --- src/timesupport.h (revision 0)
  3973. +++ src/timesupport.h (revision 0)
  3974. @@ -0,0 +1,53 @@
  3975. +// Copyright 2009 Google Inc.
  3976. +//
  3977. +// Licensed under the Apache License, Version 2.0 (the "License");
  3978. +// you may not use this file except in compliance with the License.
  3979. +// You may obtain a copy of the License at
  3980. +//
  3981. +// http://www.apache.org/licenses/LICENSE-2.0
  3982. +//
  3983. +// Unless required by applicable law or agreed to in writing, software
  3984. +// distributed under the License is distributed on an "AS IS" BASIS,
  3985. +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  3986. +// See the License for the specific language governing permissions and
  3987. +// limitations under the License.
  3988. +
  3989. +
  3990. +// This files includes functions to support time related operations.
  3991. +// It defines common functions, which are only available on platforms.
  3992. +// It also provides functions to parse RFC times.
  3993. +
  3994. +#ifndef COMMON_TIMESUPPORT_H__
  3995. +#define COMMON_TIMESUPPORT_H__
  3996. +
  3997. +#include <time.h>
  3998. +#include <string>
  3999. +
  4000. +#ifdef WIN32
  4001. +// Convert a string representation time to a time tm structure.
  4002. +// It is the conversion function of strftime().
  4003. +// Linux provides this function.
  4004. +char *strptime(const char *buf, const char *fmt, struct tm *tm);
  4005. +#endif
  4006. +
  4007. +#if defined(__linux__) || defined(__unix__)
  4008. +// Convert GMT time to UTC time value.
  4009. +// It is like mktime(), but interprets the fields as GMT rather than local.
  4010. +// This is the inverse of gmtime().
  4011. +// Windows provides this function.
  4012. +time_t _mkgmtime(const struct tm *tm);
  4013. +#endif
  4014. +
  4015. +// Tries various rfc&#039;s to convert a buffer to a struct tm.
  4016. +// Warning: this function only handles a few cases and completely ignores
  4017. +// time zones!
  4018. +bool ParseRfcTime(const char* buf, struct tm *tm);
  4019. +
  4020. +// Covert the time to W3C Datetime formats.
  4021. +// See http://www.w3.org/TR/NOTE-datetime
  4022. +const std::string FormatW3CTime(const time_t &time);
  4023. +
  4024. +// A helper method used to format HttpDate.
  4025. +std::string FormatHttpDate(time_t t);
  4026. +
  4027. +#endif // COMMON_TIMESUPPORT_H__
  4028. Index: src/URLDlg.cpp
  4029. ===================================================================
  4030. --- src/URLDlg.cpp (revision 605)
  4031. +++ src/URLDlg.cpp (working copy)
  4032. @@ -31,6 +31,7 @@
  4033. {
  4034. sSCCS[0] = _T("SVN");
  4035. sSCCS[1] = _T("Accurev");
  4036. + reviewmonitored = false;
  4037. }
  4038.  
  4039. CURLDlg::~CURLDlg(void)
  4040. @@ -68,6 +69,7 @@
  4041. SetDlgItemText(*this, IDC_URLTOMONITORLABEL, _T("URL to monitor"));
  4042. SetDlgItemText(*this, IDC_URLGROUP, _T("SVN repository settings"));
  4043. ShowWindow(GetDlgItem(*this, IDC_ACCUREVREPO), SW_HIDE);
  4044. + ShowWindow(GetDlgItem(*this, IDC_CHECKREVIEWS), SW_SHOW);
  4045. SendMessage(GetDlgItem(*this, IDC_SCCSCOMBO), CB_SETCURSEL, (WPARAM)sccs, 0);
  4046. break;
  4047.  
  4048. @@ -77,6 +79,7 @@
  4049. SetDlgItemText(*this, IDC_URLTOMONITORLABEL, _T("Accurev stream"));
  4050. SetDlgItemText(*this, IDC_URLGROUP, _T("Accurev repository settings"));
  4051. ShowWindow(GetDlgItem(*this, IDC_ACCUREVREPO), SW_SHOW);
  4052. + ShowWindow(GetDlgItem(*this, IDC_CHECKREVIEWS), SW_HIDE);
  4053. SendMessage(GetDlgItem(*this, IDC_SCCSCOMBO), CB_SETCURSEL, (WPARAM)sccs, 1);
  4054. break;
  4055. }
  4056. @@ -103,6 +106,7 @@
  4057. AddToolTip(IDC_URLTOMONITOR, _T("URL to the repository, or the SVNParentPath URL"));
  4058. AddToolTip(IDC_SCCSCOMBO, _T("Source code control system to use"));
  4059. AddToolTip(IDC_ACCUREVREPO, _T("Accurev repository name"));
  4060. + AddToolTip(IDC_CHECKREVIEWS, _T("Check review requests for this repository"));
  4061. AddToolTip(IDC_IGNORESELF, _T("If enabled, commits from you won&#039;t show a notification"));
  4062. AddToolTip(IDC_SCRIPT, _T("enter here a command which gets called after new revisions were detected.\n\n%revision gets replaced with the new HEAD revision\n%url gets replaced with the url of the project\n%project gets replaced with the project name\n\nExample command line:\nTortoiseProc.exe /command:update /rev:%revision /path:\"path\\to\\working\\copy\""));
  4063. AddToolTip(IDC_WEBDIFF, _T("URL to a web viewer\n%revision gets replaced with the new HEAD revision\n%url gets replaced with the url of the project\n%project gets replaced with the project name"));
  4064. @@ -119,6 +123,7 @@
  4065. AddToolTip(IDC_CHECKTIME, _T("Interval for repository update checks"));
  4066.  
  4067. // initialize the controls
  4068. + SendMessage(GetDlgItem(*this, IDC_CHECKREVIEWS), BM_SETCHECK, reviewmonitored ? BST_CHECKED : BST_UNCHECKED, NULL);
  4069. SetDlgItemText(*this, IDC_ACCUREVREPO, info.accurevRepo.c_str());
  4070. SetDlgItemText(*this, IDC_URLTOMONITOR, info.url.c_str());
  4071. WCHAR buf[20];
  4072. @@ -294,6 +299,8 @@
  4073.  
  4074. info.noexecuteignored = !!SendMessage(GetDlgItem(*this, IDC_EXECUTEIGNORED), BM_GETCHECK, 0, NULL);
  4075.  
  4076. + reviewmonitored = !!SendMessage(GetDlgItem(*this, IDC_CHECKREVIEWS), BM_GETCHECK, 0, NULL);
  4077. +
  4078. // make sure this entry gets checked again as soon as the next timer fires
  4079. info.lastchecked = 0;
  4080. }
  4081. Index: src/URLDlg.h
  4082. ===================================================================
  4083. --- src/URLDlg.h (revision 605)
  4084. +++ src/URLDlg.h (working copy)
  4085. @@ -31,6 +31,8 @@
  4086. ~CURLDlg(void);
  4087.  
  4088. void SetInfo(const CUrlInfo * pURLInfo = NULL);
  4089. + void SetReviewMonitored(bool b) { reviewmonitored = b; }
  4090. + bool GetReviewMonitored() { return reviewmonitored; }
  4091. CUrlInfo * GetInfo() {return &info;}
  4092. void ClearForTemplate();
  4093.  
  4094. @@ -49,4 +51,5 @@
  4095. CUrlInfo info;
  4096. AeroControlBase m_aerocontrols;
  4097. wstring sSCCS[CUrlInfo::SCCS_LEN];
  4098. + bool reviewmonitored;
  4099. };
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement