This week only. Pastebin PRO Accounts Christmas Special! Don't miss out!Want more features on Pastebin? Sign Up, it's FREE!
Guest

Untitled

By: a guest on Sep 20th, 2011  |  syntax: None  |  size: 186.14 KB  |  views: 170  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  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.  };
clone this paste RAW Paste Data