Index: src/CommitMonitor.cpp
===================================================================
--- src/CommitMonitor.cpp (revision 605)
+++ src/CommitMonitor.cpp (working copy)
@@ -33,6 +33,9 @@
#define STRUCT_IOVEC_DEFINED
#include "sasl.h"
+#include "Http.h"
+#include "ReviewBoardXml.h"
+
// Global Variables:
HINSTANCE hInst; // current instance
HANDLE g_mutex = 0;
@@ -47,6 +50,8 @@
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
UNREFERENCED_PARAMETER(nCmdShow);
+ gHTTP.Init("reviews.reviewboard.org");
+ gReviewBoard;
SetDllDirectory(L"");
::OleInitialize(NULL);
@@ -116,7 +121,7 @@
else
{
//only one instance of this application part allowed
- g_mutex = ::CreateMutex(NULL, FALSE, APPNAME_MUTEX);
+ g_mutex = ::CreateMutexW(NULL, FALSE, APPNAME_MUTEX);
if (g_mutex != NULL)
{
@@ -141,6 +146,8 @@
if (hiddenWindow.RegisterAndCreateWindow())
{
+ gReviewBoard.Load();
+
if ((snarlIface.GetVersionEx() != Snarl::M_FAILED)&&(Snarl::SnarlInterface::GetSnarlWindow() != NULL))
{
wstring imgPath = CAppUtils::GetAppDataDir()+L"\\CM.png";
@@ -189,7 +196,8 @@
// unregister with Snarl
snarlIface.UnregisterApp();
}
-
+ gReviewBoard_Cleanup;
+ gHTTP_Cleanup;
return (int) msg.wParam;
}
Index: src/CommitMonitor.vcxproj
===================================================================
--- src/CommitMonitor.vcxproj (revision 605)
+++ src/CommitMonitor.vcxproj (working copy)
@@ -51,7 +51,7 @@
Disabled
- ..\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)
+ 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)
WIN32;_DEBUG;_WINDOWS;SVN_DEBUG;APR_DECLARE_STATIC;APU_DECLARE_STATIC;STATIC_BUILD;SCI_LEXER;%(PreprocessorDefinitions)
true
EnableFastChecks
@@ -65,7 +65,7 @@
$(IntDir)%(Filename).res
- 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)
+ 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)
winspool.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(IgnoreSpecificDefaultLibraries)
true
Windows
@@ -73,6 +73,7 @@
MachineX86
+ J:\dev\poco\lib;%(AdditionalLibraryDirectories)
@@ -94,7 +95,7 @@
..;%(AdditionalIncludeDirectories)
- 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)
+ 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)
winspool.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(IgnoreSpecificDefaultLibraries)
true
Windows
@@ -323,10 +324,12 @@
+
+
@@ -336,6 +339,7 @@
+
@@ -410,20 +414,25 @@
+
+
+
+
+
Index: src/CommitMonitor.vcxproj.filters
===================================================================
--- src/CommitMonitor.vcxproj.filters (revision 605)
+++ src/CommitMonitor.vcxproj.filters (working copy)
@@ -243,6 +243,15 @@
Scintilla
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
@@ -479,6 +488,21 @@
Scintilla
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
Index: src/Convert.h
===================================================================
--- src/Convert.h (revision 0)
+++ src/Convert.h (revision 0)
@@ -0,0 +1,164 @@
+#ifndef CONVERT_H
+#define CONVERT_H
+
+#include "stdafx.h"
+#include
+#include
+
+template
+class Converter
+{
+public:
+ static out_type convert(const in_value &t)
+ {
+ std::wstringstream stream;
+ stream << t;
+
+ out_type result;
+ stream >> result;
+ return result;
+ }
+};
+
+template <>
+class Converter
+{
+public:
+ static std::wstring convert(const std::wstring &t)
+ {
+ return t;
+ }
+};
+
+template
+out_type convert(const in_value &t)
+{
+ return Converter::convert(t);
+}
+
+#define BOM8A 0xEF
+#define BOM8B 0xBB
+#define BOM8C 0xBF
+
+/*
+* Copyright (c) 2009, Helios (helios.vmg@gmail.com)
+* All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are met:
+* * Redistributions of source code must retain the above copyright notice,
+* this list of conditions and the following disclaimer.
+* * Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer in the
+* documentation and/or other materials provided with the distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY HELIOS "AS IS" AND ANY EXPRESS OR IMPLIED
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+* EVENT SHALL HELIOS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+* OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+typedef unsigned char uchar;
+
+/*
+string: a UTF-8-encoded C string (nul terminated)
+Return value: a wchar_t C string.
+
+The function handles memory allocation on its own.
+
+Limitations: Only handles the range [U+0000;U+FFFF], higher code points are
+changed to '?'.
+
+Assumptions: sizeof(wchar_t)>=2
+*/
+wchar_t *UTF8_to_WChar(const char *string){
+ long b=0,
+ c=0;
+ if ((uchar)string[0]==BOM8A && (uchar)string[1]==BOM8B && (uchar)string[2]==BOM8C)
+ string+=3;
+ for (const char *a=string;*a;a++)
+ if (((uchar)*a)<128 || (*a&192)==192)
+ c++;
+ wchar_t *res=new wchar_t[c+1];
+ res[c]=0;
+ for (uchar *a=(uchar*)string;*a;a++){
+ if (!(*a&128))
+ //Byte represents an ASCII character. Direct copy will do.
+ res[b]=*a;
+ else if ((*a&192)==128)
+ //Byte is the middle of an encoded character. Ignore.
+ continue;
+ else if ((*a&224)==192)
+ //Byte represents the start of an encoded character in the range
+ //U+0080 to U+07FF
+ res[b]=((*a&31)<<6)|a[1]&63;
+ else if ((*a&240)==224)
+ //Byte represents the start of an encoded character in the range
+ //U+07FF to U+FFFF
+ res[b]=((*a&15)<<12)|((a[1]&63)<<6)|a[2]&63;
+ else if ((*a&248)==240){
+ //Byte represents the start of an encoded character beyond the
+ //U+FFFF limit of 16-bit integers
+ res[b]='?';
+ }
+ b++;
+ }
+ return res;
+}
+
+//Do not call me.
+long getUTF8size(const wchar_t *string){
+ if (!string)
+ return 0;
+ long res=0;
+ for (;*string;string++){
+ if (*string<0x80)
+ res++;
+ else if (*string<0x800)
+ res+=2;
+ else
+ res+=3;
+ }
+ return res;
+}
+
+/*
+string: a wchar_t C string (nul terminated)
+Return value: a UTF-8-encoded C string.
+
+The function handles memory allocation on its own.
+
+Limitations: Only handles the range [U+0000;U+FFFF], higher code points are
+changed to '?'.
+
+Assumptions: sizeof(wchar_t)>=2
+*/
+char *WChar_to_UTF8(const wchar_t *string){
+ long fSize=getUTF8size(string);
+ char *res=new char[fSize+1];
+ res[fSize]=0;
+ if (!string)
+ return res;
+ long b=0;
+ for (;*string;string++,b++){
+ if (*string<0x80)
+ res[b]=(char)*string;
+ else if (*string<0x800){
+ res[b++]=(*string>>6)|192;
+ res[b]=*string&63|128;
+ }else{
+ res[b++]=(*string>>12)|224;
+ res[b++]=((*string&4095)>>6)|128;
+ res[b]=*string&63|128;
+ }
+ }
+ return res;
+}
+
+#endif
Index: src/HiddenWindow.cpp
===================================================================
--- src/HiddenWindow.cpp (revision 605)
+++ src/HiddenWindow.cpp (working copy)
@@ -34,7 +34,7 @@
#include "OptionsDlg.h"
#include "version.h"
#include "SnarlInterface.h"
-
+#include "ReviewBoardXml.h"
#include
#include
using namespace std;
@@ -345,6 +345,7 @@
{
// find the number of unread items
int nNewCommits = 0;
+ int nNewReviews = gReviewBoard.GetUnreadReviews();
const map * pRead = m_UrlInfos.GetReadOnlyData();
for (map::const_iterator it = pRead->begin(); it != pRead->end(); ++it)
{
@@ -360,12 +361,39 @@
m_SystemTray.hWnd = *this;
m_SystemTray.uFlags = NIF_MESSAGE | NIF_TIP;
m_SystemTray.uCallbackMessage = COMMITMONITOR_TASKBARCALLBACK;
- if (nNewCommits)
+ if (nNewCommits || nNewReviews)
{
- if (nNewCommits == 1)
- _stprintf_s(m_SystemTray.szTip, _countof(m_SystemTray.szTip), _T("CommitMonitor - %d new commit"), nNewCommits);
+ if(nNewReviews == 0)
+ {
+ if (nNewCommits == 1)
+ _stprintf_s(m_SystemTray.szTip, _countof(m_SystemTray.szTip), _T("CommitMonitor - %d new commit"), nNewCommits);
+ else
+ _stprintf_s(m_SystemTray.szTip, _countof(m_SystemTray.szTip), _T("CommitMonitor - %d new commits"), nNewCommits);
+ }
else
- _stprintf_s(m_SystemTray.szTip, _countof(m_SystemTray.szTip), _T("CommitMonitor - %d new commits"), nNewCommits);
+ {
+ if (nNewCommits == 0)
+ {
+ if (nNewReviews == 1)
+ _stprintf_s(m_SystemTray.szTip, _countof(m_SystemTray.szTip), _T("CommitMonitor - %d new review"), nNewReviews);
+ else
+ _stprintf_s(m_SystemTray.szTip, _countof(m_SystemTray.szTip), _T("CommitMonitor - %d new reviews"), nNewReviews);
+ }
+ else if(nNewCommits == 1)
+ {
+ if (nNewReviews == 1)
+ _stprintf_s(m_SystemTray.szTip, _countof(m_SystemTray.szTip), _T("CommitMonitor - %d new commit, %d new review"), nNewCommits, nNewReviews);
+ else
+ _stprintf_s(m_SystemTray.szTip, _countof(m_SystemTray.szTip), _T("CommitMonitor - %d new commit, %d new reviews"), nNewCommits, nNewReviews);
+ }
+ else
+ {
+ if (nNewReviews == 1)
+ _stprintf_s(m_SystemTray.szTip, _countof(m_SystemTray.szTip), _T("CommitMonitor - %d new commits, %d new review"), nNewCommits, nNewReviews);
+ else
+ _stprintf_s(m_SystemTray.szTip, _countof(m_SystemTray.szTip), _T("CommitMonitor - %d new commits, %d new reviews"), nNewCommits, nNewReviews);
+ }
+ }
}
else
_tcscpy_s(m_SystemTray.szTip, _countof(m_SystemTray.szTip), _T("CommitMonitor"));
@@ -679,7 +707,8 @@
// won't block anything for too long.
map urlinfoReadOnly = *m_UrlInfos.GetReadOnlyData();
m_UrlInfos.ReleaseReadOnlyData();
-
+ bool needReviewUpdate = false;
+ int newreviews = 0;
TCHAR infotextbuf[1024];
TRACE(_T("monitor thread started\n"));
const map * pUrlInfoReadOnly = &urlinfoReadOnly;
@@ -695,6 +724,13 @@
}
if (((it->second.monitored)&&((it->second.lastchecked + (mit*60)) < currenttime))||(m_UrlToWorkOn.size()))
{
+ if(!needReviewUpdate && gReviewBoard.IsUrlMonitored(it->second.url))
+ {
+ needReviewUpdate = true;
+ // do update here, we're not currently blocking anything (update will ofc...)
+ newreviews = gReviewBoard.DoUpdate();
+ }
+
m_UrlToWorkOn.clear();
if ((it->second.errNr == SVN_ERR_RA_NOT_AUTHORIZED)&&(!it->second.error.empty()))
continue; // don't check if the last error was 'not authorized'
@@ -740,9 +776,9 @@
}
if (!m_bRun)
continue;
- if (headrev > it->second.lastcheckedrev)
+ if (headrev > it->second.lastcheckedrev || gReviewBoard.GetNewReviewsForUrl(it->second.url))
{
- TRACE(_T("%s has updates! Last checked revision was %ld, HEAD revision is %ld\n"), it->first.c_str(), it->second.lastcheckedrev, headrev);
+ 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);
if (m_hMainDlg)
{
_stprintf_s(infotextbuf, 1024, _T("getting log for %s"), it->first.c_str());
@@ -750,7 +786,7 @@
}
int nNewCommits = 0; // commits without ignored ones
int nTotalNewCommits = 0; // all commits, including ignored ones
- if (pSCCS->GetLog(it->second.accurevRepo, it->first, headrev, it->second.lastcheckedrev + 1))
+ if ((headrev <= it->second.lastcheckedrev && gReviewBoard.GetNewReviewsForUrl(it->second.url)) || pSCCS->GetLog(it->second.accurevRepo, it->first, headrev, it->second.lastcheckedrev + 1))
{
TRACE(_T("log fetched for %s\n"), it->first.c_str());
if (!m_bRun)
@@ -772,112 +808,115 @@
wstring sPopupText;
bool hadError = !it->second.error.empty();
- for (map::iterator logit = pSCCS->m_logs.begin(); logit != pSCCS->m_logs.end(); ++logit)
+ if(headrev > it->second.lastcheckedrev)
{
- // again, only block for a short time
- map * pWrite = m_UrlInfos.GetWriteData();
- map::iterator writeIt = pWrite->find(it->first);
- bool bIgnore = false;
- bool bEntryExists = false;
- if (writeIt != pWrite->end())
+ for (map::iterator logit = pSCCS->m_logs.begin(); logit != pSCCS->m_logs.end(); ++logit)
{
- map::iterator existIt = writeIt->second.logentries.find(logit->first);
- bEntryExists = existIt != writeIt->second.logentries.end();
- bool readState = false;
- if (bEntryExists)
- readState = existIt->second.read;
- logit->second.read = readState;
+ // again, only block for a short time
+ map * pWrite = m_UrlInfos.GetWriteData();
+ map::iterator writeIt = pWrite->find(it->first);
+ bool bIgnore = false;
+ bool bEntryExists = false;
+ if (writeIt != pWrite->end())
+ {
+ map::iterator existIt = writeIt->second.logentries.find(logit->first);
+ bEntryExists = existIt != writeIt->second.logentries.end();
+ bool readState = false;
+ if (bEntryExists)
+ readState = existIt->second.read;
+ logit->second.read = readState;
- writeIt->second.logentries[logit->first] = logit->second;
+ writeIt->second.logentries[logit->first] = logit->second;
- if (!bEntryExists)
- {
- wstring author1 = logit->second.author;
- std::transform(author1.begin(), author1.end(), author1.begin(), std::tolower);
+ if (!bEntryExists)
+ {
+ wstring author1 = logit->second.author;
+ std::transform(author1.begin(), author1.end(), author1.begin(), std::tolower);
- if (writeIt->second.includeUsers.size() > 0)
- {
- wstring s1 = writeIt->second.includeUsers;
+ if (writeIt->second.includeUsers.size() > 0)
+ {
+ wstring s1 = writeIt->second.includeUsers;
+ std::transform(s1.begin(), s1.end(), s1.begin(), std::tolower);
+ CAppUtils::SearchReplace(s1, _T("\r\n"), _T("\n"));
+ vector includeVector = CAppUtils::tokenize_str(s1, _T("\n"));
+ bool bInclude = false;
+ for (vector::iterator it = includeVector.begin(); it != includeVector.end(); ++it)
+ {
+ if (author1.compare(*it) == 0)
+ {
+ bInclude = true;
+ break;
+ }
+ }
+ bIgnore = !bInclude;
+ }
+
+ wstring s1 = writeIt->second.ignoreUsers;
std::transform(s1.begin(), s1.end(), s1.begin(), std::tolower);
CAppUtils::SearchReplace(s1, _T("\r\n"), _T("\n"));
- vector includeVector = CAppUtils::tokenize_str(s1, _T("\n"));
- bool bInclude = false;
- for (vector::iterator it = includeVector.begin(); it != includeVector.end(); ++it)
+ vector ignoreVector = CAppUtils::tokenize_str(s1, _T("\n"));
+ for (vector::iterator it = ignoreVector.begin(); it != ignoreVector.end(); ++it)
{
if (author1.compare(*it) == 0)
{
- bInclude = true;
+ bIgnore = true;
break;
}
}
- bIgnore = !bInclude;
- }
-
- wstring s1 = writeIt->second.ignoreUsers;
- std::transform(s1.begin(), s1.end(), s1.begin(), std::tolower);
- CAppUtils::SearchReplace(s1, _T("\r\n"), _T("\n"));
- vector ignoreVector = CAppUtils::tokenize_str(s1, _T("\n"));
- for (vector::iterator it = ignoreVector.begin(); it != ignoreVector.end(); ++it)
- {
- if (author1.compare(*it) == 0)
+ nTotalNewCommits++;
+ if (!bIgnore)
{
- bIgnore = true;
- break;
+ bNewEntries = true;
+ nNewCommits++;
}
+ else
+ // set own commit as already read
+ writeIt->second.logentries[logit->first].read = true;
}
- nTotalNewCommits++;
- if (!bIgnore)
- {
- bNewEntries = true;
- nNewCommits++;
- }
- else
- // set own commit as already read
- writeIt->second.logentries[logit->first].read = true;
+ writeIt->second.error.clear();
}
- writeIt->second.error.clear();
- }
- bNeedsSaving = true;
- m_UrlInfos.ReleaseWriteData();
- if (!m_bRun)
- continue;
- // popup info text
- if ((!bIgnore)&&(!bEntryExists))
- {
- if (!sPopupText.empty())
- sPopupText += _T(", ");
- sPopupText += logit->second.author;
- }
- if ((!it->second.disallowdiffs)&&(it->second.fetchdiffs))
- {
- TCHAR buf[4096];
- // first, find a name where to store the diff for that revision
- _stprintf_s(buf, 4096, _T("%s_%ld.diff"), it->second.name.c_str(), logit->first);
- wstring diffFileName = CAppUtils::GetAppDataDir();
- diffFileName += _T("/");
- diffFileName += wstring(buf);
- // do we already have that diff?
- if (!PathFileExists(diffFileName.c_str()))
+ bNeedsSaving = true;
+ m_UrlInfos.ReleaseWriteData();
+ if (!m_bRun)
+ continue;
+ // popup info text
+ if ((!bIgnore)&&(!bEntryExists))
{
- // get the diff
- if (m_hMainDlg)
+ if (!sPopupText.empty())
+ sPopupText += _T(", ");
+ sPopupText += logit->second.author;
+ }
+ if ((!it->second.disallowdiffs)&&(it->second.fetchdiffs))
+ {
+ TCHAR buf[4096];
+ // first, find a name where to store the diff for that revision
+ _stprintf_s(buf, 4096, _T("%s_%ld.diff"), it->second.name.c_str(), logit->first);
+ wstring diffFileName = CAppUtils::GetAppDataDir();
+ diffFileName += _T("/");
+ diffFileName += wstring(buf);
+ // do we already have that diff?
+ if (!PathFileExists(diffFileName.c_str()))
{
- _stprintf_s(infotextbuf, 1024, _T("getting diff for %s, revision %ld"), it->first.c_str(), logit->first);
- SendMessage(*this, COMMITMONITOR_INFOTEXT, 0, (LPARAM)infotextbuf);
+ // get the diff
+ if (m_hMainDlg)
+ {
+ _stprintf_s(infotextbuf, 1024, _T("getting diff for %s, revision %ld"), it->first.c_str(), logit->first);
+ SendMessage(*this, COMMITMONITOR_INFOTEXT, 0, (LPARAM)infotextbuf);
+ }
+ if (!pSCCS->Diff(it->first, logit->first, logit->first-1, logit->first, true, true, false, wstring(), false, diffFileName, wstring()))
+ {
+ TRACE(_T("Diff not fetched for %s, revision %ld because of an error\n"), it->first.c_str(), logit->first);
+ DeleteFile(diffFileName.c_str());
+ }
+ else
+ TRACE(_T("Diff fetched for %s, revision %ld\n"), it->first.c_str(), logit->first);
+ if (!m_bRun)
+ break;
}
- if (!pSCCS->Diff(it->first, logit->first, logit->first-1, logit->first, true, true, false, wstring(), false, diffFileName, wstring()))
- {
- TRACE(_T("Diff not fetched for %s, revision %ld because of an error\n"), it->first.c_str(), logit->first);
- DeleteFile(diffFileName.c_str());
- }
- else
- TRACE(_T("Diff fetched for %s, revision %ld\n"), it->first.c_str(), logit->first);
- if (!m_bRun)
- break;
}
}
}
- if ((it->second.lastcheckedrobots + (60*60*24*2)) < currenttime)
+ if ((headrev > it->second.lastcheckedrev) && (it->second.lastcheckedrobots + (60*60*24*2)) < currenttime)
{
wstring sRobotsURL = it->first;
sRobotsURL += _T("/svnrobots.txt");
@@ -984,7 +1023,8 @@
m_UrlInfos.ReleaseWriteData();
}
// prepare notification strings
- if ((bNewEntries)||(!hadError && !it->second.error.empty()))
+ int nNewReviews = gReviewBoard.GetNewReviewsForUrl(it->second.url);
+ if ((nNewReviews > 0)||(bNewEntries)||(!hadError && !it->second.error.empty()))
{
popupData data;
TCHAR sTitle[1024] = {0};
@@ -996,10 +1036,31 @@
else
{
data.sProject = it->second.name;
- if (nNewCommits == 1)
- _stprintf_s(sTitle, 1024, _T("%s\nhas %d new commit"), it->second.name.c_str(), nNewCommits);
- else
- _stprintf_s(sTitle, 1024, _T("%s\nhas %d new commits"), it->second.name.c_str(), nNewCommits);
+ if (nNewCommits == 0)
+ {
+ if(nNewReviews == 1)
+ _stprintf_s(sTitle, 1024, _T("%s\nhas %d new review"), it->second.name.c_str(), nNewReviews);
+ else
+ _stprintf_s(sTitle, 1024, _T("%s\nhas %d new reviews"), it->second.name.c_str(), nNewReviews);
+ }
+ else if (nNewCommits == 1)
+ {
+ if(nNewReviews == 0)
+ _stprintf_s(sTitle, 1024, _T("%s\nhas %d new commit"), it->second.name.c_str(), nNewCommits);
+ else if(nNewReviews == 1)
+ _stprintf_s(sTitle, 1024, _T("%s\nhas %d new commit, %d new review"), it->second.name.c_str(), nNewCommits, nNewReviews);
+ else
+ _stprintf_s(sTitle, 1024, _T("%s\nhas %d new commit, %d new reviews"), it->second.name.c_str(), nNewCommits, nNewReviews);
+ }
+ else
+ {
+ if(nNewReviews == 0)
+ _stprintf_s(sTitle, 1024, _T("%s\nhas %d new commits"), it->second.name.c_str(), nNewCommits);
+ else if(nNewReviews == 1)
+ _stprintf_s(sTitle, 1024, _T("%s\nhas %d new commits, %d new review"), it->second.name.c_str(), nNewCommits, nNewReviews);
+ else
+ _stprintf_s(sTitle, 1024, _T("%s\nhas %d new commits, %d new reviews"), it->second.name.c_str(), nNewCommits, nNewReviews);
+ }
}
data.sText = sPopupText;
data.sTitle = wstring(sTitle);
Index: src/Http.cpp
===================================================================
--- src/Http.cpp (revision 0)
+++ src/Http.cpp (revision 0)
@@ -0,0 +1,112 @@
+#include "stdafx.h"
+#include "Http.h"
+
+#pragma warning( push )
+#pragma warning( disable: 4244 )
+#include "Poco/Net/HTTPClientSession.h"
+#include "Poco/Net/HTTPRequest.h"
+#include "Poco/Net/HTTPResponse.h"
+#include "Poco/StreamCopier.h"
+#include "Poco/Path.h"
+#include "Poco/URI.h"
+#include "Poco/Exception.h"
+#pragma warning( pop )
+#include
+#include
+
+using Poco::Net::HTTPClientSession;
+using Poco::Net::HTTPRequest;
+using Poco::Net::HTTPResponse;
+using Poco::Net::HTTPMessage;
+using Poco::StreamCopier;
+using Poco::Path;
+using Poco::URI;
+using Poco::Exception;
+
+using namespace std;
+
+
+Http::Http() : inited(false)
+{
+}
+
+Http::~Http()
+{
+ if(inited)
+ {
+ }
+}
+
+bool Http::Init(const string& host)
+{
+ string url = "http://";
+ url += host;
+ url += "/";
+ URI uri(url);
+ std::string path(uri.getPathAndQuery());
+ if (path.empty()) path = "/";
+
+
+ session.setHost(uri.getHost());
+ session.setPort(uri.getPort());
+ inited = true;
+ return true;
+}
+
+void Http::GetPageText(const string& path, stringstream& sstrm)
+{
+ if(!inited)
+ return;
+ HTTPRequest req(HTTPRequest::HTTP_GET, path, HTTPMessage::HTTP_1_1);
+ req.set("Accept","text/x-patch");
+ session.sendRequest(req);
+ HTTPResponse res;
+ std::istream& rs = session.receiveResponse(res);
+ StreamCopier::copyStream(rs, sstrm);
+}
+
+void Http::GetPageXml(const string& path, stringstream& sstrm)
+{
+ if(!inited)
+ return;
+ HTTPRequest req(HTTPRequest::HTTP_GET, path, HTTPMessage::HTTP_1_1);
+ req.set("Accept","application/xml");
+ session.sendRequest(req);
+ HTTPResponse res;
+ std::istream& rs = session.receiveResponse(res);
+ StreamCopier::copyStream(rs, sstrm);
+}
+
+void Http::GetPageXml(const string& path, string& str)
+{
+ stringstream sstrm;
+ GetPageXml(path,sstrm);
+ str = sstrm.str();
+}
+
+void Http::GetPageText(const string& path, string& str)
+{
+ stringstream sstrm;
+ GetPageText(path,sstrm);
+ str = sstrm.str();
+}
+
+void Http::GetPageXml(const string& path, char *& buf, int& len)
+{
+ string str;
+ GetPageXml(path,str);
+ len = str.length();
+ buf = new char[len+1];
+ memcpy(buf,str.c_str(),len * sizeof(char));
+ buf[len]=0;
+}
+
+void Http::GetPageText(const std::string& path, char *& buf, int& len)
+{
+ string str;
+ GetPageText(path,str);
+ len = str.length();
+ buf = new char[len+1];
+ memcpy(buf,str.c_str(),len * sizeof(char));
+ buf[len]=0;
+}
Index: src/Http.h
===================================================================
--- src/Http.h (revision 0)
+++ src/Http.h (revision 0)
@@ -0,0 +1,32 @@
+#ifndef HTTP_H__
+#define HTTP_H__
+
+#include "stdafx.h"
+
+#include "Poco/Net/HTTPClientSession.h"
+#include
+#include "Singleton.h"
+
+class Http
+{
+ friend class Singleton;
+ Poco::Net::HTTPClientSession session;
+ bool inited;
+private:
+ Http();
+public:
+ ~Http();
+ bool Init(const std::string& host);
+ void GetPageXml(const std::string& path, std::stringstream& sstrm);
+ void GetPageText(const std::string& path, std::stringstream& sstrm);
+ void GetPageXml(const std::string& path, std::string& str);
+ void GetPageText(const std::string& path, std::string& str);
+ void GetPageXml(const std::string& path, char *& buf_you_must_delete_this_when_done_with_processing, int& len);
+ void GetPageText(const std::string& path, char *& buf_you_must_delete_this_when_done_with_processing, int& len);
+};
+
+
+#define gHTTP (*(Singleton::GetInstance()))
+#define gHTTP_Cleanup Singleton::Cleanup()
+
+#endif
Index: src/MainDlg.cpp
===================================================================
--- src/MainDlg.cpp (revision 605)
+++ src/MainDlg.cpp (working copy)
@@ -32,6 +32,8 @@
#include
#include
+#include "ReviewBoardXml.h"
+
#pragma comment(lib, "uxtheme.lib")
#define FILTERBOXHEIGHT 20
@@ -52,6 +54,7 @@
, m_hImgList(NULL)
, m_bNewerVersionAvailable(false)
, m_refreshNeeded(false)
+ , m_commitsradio(true)
{
m_hParent = hParent;
// use the default GUI font, create a copy of it and
@@ -219,6 +222,11 @@
m_hListControl = ::GetDlgItem(*this, IDC_MONITOREDURLS);
m_hLogMsgControl = ::GetDlgItem(*this, IDC_LOGINFO);
m_hFilterControl = ::GetDlgItem(*this, IDC_FILTERSTRING);
+ m_hCommitsRadio = ::GetDlgItem(*this, IDC_RADIO2);
+ m_hReviewsRadio = ::GetDlgItem(*this, IDC_RADIO3);
+
+ ::CheckRadioButton(hwndDlg, IDC_RADIO2, IDC_RADIO3, m_commitsradio ? IDC_RADIO2 : IDC_RADIO3);
+
::SendMessage(m_hTreeControl, TVM_SETUNICODEFORMAT, 1, 0);
SetWindowTheme(m_hListControl, L"Explorer", NULL);
@@ -278,6 +286,9 @@
GetClientRect(*this, &rect);
m_bottommarg = rect.bottom - m_bottommarg;
+ m_radiossize = 160;
+
+
// subclass the tree view control to intercept the WM_SETFOCUS messages
m_oldTreeWndProc = (WNDPROC)SetWindowLongPtr(m_hTreeControl, GWLP_WNDPROC, (LONG)TreeProc);
SetWindowLongPtr(m_hTreeControl, GWLP_USERDATA, (LONG)this);
@@ -446,7 +457,7 @@
case WM_GETMINMAXINFO:
{
MINMAXINFO * mmi = (MINMAXINFO*)lParam;
- mmi->ptMinTrackSize.x = m_xSliderPos + 100;
+ mmi->ptMinTrackSize.x = m_xSliderPos + 265;
mmi->ptMinTrackSize.y = m_ySliderPos + 100;
return 0;
}
@@ -503,7 +514,7 @@
tv.hParent = FindParentTreeNode(it->first);
tv.hInsertAfter = TVI_SORT;
tv.itemex.mask = TVIF_TEXT|TVIF_PARAM|TVIF_STATE|TVIF_IMAGE|TVIF_SELECTEDIMAGE;
- WCHAR * str = new WCHAR[it->second.name.size()+10];
+ WCHAR * str = new WCHAR[it->second.name.size()+30];
// find out if there are some unread entries
int unread = 0;
for (map::const_iterator logit = it->second.logentries.begin(); logit != it->second.logentries.end(); ++logit)
@@ -520,13 +531,14 @@
tv.itemex.hItem = directItem;
tv.itemex.stateMask = TVIS_SELECTED|TVIS_BOLD|TVIF_IMAGE|TVIF_SELECTEDIMAGE;
tv.itemex.pszText = str;
- tv.itemex.cchTextMax = it->second.name.size()+9;
+ tv.itemex.cchTextMax = it->second.name.size()+29;
TreeView_GetItem(m_hTreeControl, &tv.itemex);
wstring sTitle = wstring(str);
bool bRequiresUpdate = false;
- if (unread)
+ int unreadreviews = gReviewBoard.GetUnreadReviewsForUrl(it->second.url);
+ if (unread || unreadreviews)
{
- _stprintf_s(str, it->second.name.size()+10, _T("%s (%d)"), it->second.name.c_str(), unread);
+ _stprintf_s(str, it->second.name.size()+30, _T("%s (%d + %d)"), it->second.name.c_str(), unread, unreadreviews);
tv.itemex.state |= TVIS_BOLD;
tv.itemex.stateMask = TVIS_BOLD;
}
@@ -551,7 +563,7 @@
tv.itemex.iImage = 4;
tv.itemex.iSelectedImage = 4;
}
- else if (unread)
+ else if (unread || unreadreviews)
{
bRequiresUpdate = tv.itemex.iImage != 3;
tv.itemex.iImage = 3;
@@ -594,9 +606,10 @@
}
else
{
- if (unread)
+ int unreadreviews = gReviewBoard.GetUnreadReviewsForUrl(it->second.url);
+ if (unread || unreadreviews)
{
- _stprintf_s(str, it->second.name.size()+10, _T("%s (%d)"), it->second.name.c_str(), unread);
+ _stprintf_s(str, it->second.name.size()+30, _T("%s (%d + %d)"), it->second.name.c_str(), unread, unreadreviews);
tv.itemex.state = TVIS_BOLD;
tv.itemex.stateMask = TVIS_BOLD;
}
@@ -619,7 +632,7 @@
tv.itemex.iImage = 4;
tv.itemex.iSelectedImage = 4;
}
- else if (unread)
+ else if (unread || unreadreviews)
{
tv.itemex.iImage = 3;
tv.itemex.iSelectedImage = 3;
@@ -820,7 +833,7 @@
{
HMENU hMenu = NULL;
wstring tsvninstalled = CAppUtils::GetTSVNPath();
- if (tsvninstalled.empty())
+ if (tsvninstalled.empty() || !m_commitsradio)
hMenu = ::LoadMenu(hResource, MAKEINTRESOURCE(IDR_LISTPOPUP));
else
hMenu = ::LoadMenu(hResource, MAKEINTRESOURCE(IDR_LISTPOPUPTSVN));
@@ -882,48 +895,101 @@
ListView_GetItem(m_hListControl, &item);
if (item.state & LVIS_SELECTED)
{
- SCCSLogEntry * pLogEntry = (SCCSLogEntry*)item.lParam;
- if (pLogEntry)
+ if(m_commitsradio)
{
- // set the entry as unread
- if (pLogEntry->read)
+ SCCSLogEntry * pLogEntry = (SCCSLogEntry*)item.lParam;
+ if (pLogEntry)
{
- pLogEntry->read = false;
- // refresh the name of the tree item to indicate the new
- // number of unread log messages
- // e.g. instead of 'TortoiseSVN (2)', show now 'TortoiseSVN (3)'
- if (pRead->find(*(wstring*)itemex.lParam) != pRead->end())
+ // set the entry as unread
+ if (pLogEntry->read)
{
- const CUrlInfo * uinfo = &pRead->find(*(wstring*)itemex.lParam)->second;
- // count the number of unread messages
- int unread = 0;
- for (map::const_iterator it = uinfo->logentries.begin(); it != uinfo->logentries.end(); ++it)
+ pLogEntry->read = false;
+ // refresh the name of the tree item to indicate the new
+ // number of unread log messages
+ // e.g. instead of 'TortoiseSVN (2)', show now 'TortoiseSVN (3)'
+ if (pRead->find(*(wstring*)itemex.lParam) != pRead->end())
{
- if (!it->second.read)
- unread++;
+ const CUrlInfo * uinfo = &pRead->find(*(wstring*)itemex.lParam)->second;
+ // count the number of unread messages
+ int unread = 0;
+ for (map::const_iterator it = uinfo->logentries.begin(); it != uinfo->logentries.end(); ++it)
+ {
+ if (!it->second.read)
+ unread++;
+ }
+ WCHAR * str = new WCHAR[uinfo->name.size()+30];
+ int unreadreviews = gReviewBoard.GetUnreadReviewsForUrl(uinfo->url);
+ if (unread || unreadreviews)
+ {
+ _stprintf_s(str, uinfo->name.size()+30, _T("%s (%d + %d)"), uinfo->name.c_str(), unread, unreadreviews);
+ itemex.state = TVIS_BOLD;
+ itemex.stateMask = TVIS_BOLD;
+ itemex.iImage = 3;
+ itemex.iSelectedImage = 3;
+ }
+ else
+ {
+ _stprintf_s(str, uinfo->name.size()+10, _T("%s"), uinfo->name.c_str());
+ itemex.state = 0;
+ itemex.stateMask = TVIS_BOLD;
+ itemex.iImage = 2;
+ itemex.iSelectedImage = 2;
+ }
+
+ itemex.pszText = str;
+ itemex.mask = TVIF_TEXT|TVIF_STATE|TVIF_IMAGE|TVIF_SELECTEDIMAGE;
+ m_refreshNeeded = true;
+ TreeView_SetItem(m_hTreeControl, &itemex);
}
- WCHAR * str = new WCHAR[uinfo->name.size()+10];
- if (unread)
+ }
+ }
+ }
+ else
+ {
+ ReviewBoardReview * pLogEntry = (ReviewBoardReview*)item.lParam;
+ if (pLogEntry)
+ {
+ // set the entry as unread
+ if (pLogEntry->read)
+ {
+ gReviewBoard.ReviewSetRead(pLogEntry->reviewid,false);
+ // refresh the name of the tree item to indicate the new
+ // number of unread log messages
+ // e.g. instead of 'TortoiseSVN (2)', show now 'TortoiseSVN (3)'
+ if (pRead->find(*(wstring*)itemex.lParam) != pRead->end())
{
- _stprintf_s(str, uinfo->name.size()+10, _T("%s (%d)"), uinfo->name.c_str(), unread);
- itemex.state = TVIS_BOLD;
- itemex.stateMask = TVIS_BOLD;
- itemex.iImage = 3;
- itemex.iSelectedImage = 3;
+ const CUrlInfo * uinfo = &pRead->find(*(wstring*)itemex.lParam)->second;
+ // count the number of unread messages
+ int unread = 0;
+ for (map::const_iterator it = uinfo->logentries.begin(); it != uinfo->logentries.end(); ++it)
+ {
+ if (!it->second.read)
+ unread++;
+ }
+ WCHAR * str = new WCHAR[uinfo->name.size()+30];
+ int unreadreviews = gReviewBoard.GetUnreadReviewsForUrl(uinfo->url);
+ if (unread || unreadreviews)
+ {
+ _stprintf_s(str, uinfo->name.size()+30, _T("%s (%d + %d)"), uinfo->name.c_str(), unread, unreadreviews);
+ itemex.state = TVIS_BOLD;
+ itemex.stateMask = TVIS_BOLD;
+ itemex.iImage = 3;
+ itemex.iSelectedImage = 3;
+ }
+ else
+ {
+ _stprintf_s(str, uinfo->name.size()+10, _T("%s"), uinfo->name.c_str());
+ itemex.state = 0;
+ itemex.stateMask = TVIS_BOLD;
+ itemex.iImage = 2;
+ itemex.iSelectedImage = 2;
+ }
+
+ itemex.pszText = str;
+ itemex.mask = TVIF_TEXT|TVIF_STATE|TVIF_IMAGE|TVIF_SELECTEDIMAGE;
+ m_refreshNeeded = true;
+ TreeView_SetItem(m_hTreeControl, &itemex);
}
- else
- {
- _stprintf_s(str, uinfo->name.size()+10, _T("%s"), uinfo->name.c_str());
- itemex.state = 0;
- itemex.stateMask = TVIS_BOLD;
- itemex.iImage = 2;
- itemex.iSelectedImage = 2;
- }
-
- itemex.pszText = str;
- itemex.mask = TVIF_TEXT|TVIF_STATE|TVIF_IMAGE|TVIF_SELECTEDIMAGE;
- m_refreshNeeded = true;
- TreeView_SetItem(m_hTreeControl, &itemex);
}
}
}
@@ -1094,13 +1160,36 @@
if (pRead->find(*(wstring*)itemex.lParam) != pRead->end())
{
const CUrlInfo * info = &pRead->find(*(wstring*)itemex.lParam)->second;
- if ((info)&&(!info->webviewer.empty()))
+ if (!m_commitsradio || ((info)&&(!info->webviewer.empty())))
{
// replace "%revision" with the new HEAD revision
wstring tag(_T("%revision"));
wstring commandline = info->webviewer;
+ wstring reviewurl;
+ if(!m_commitsradio)
+ {
+ // find the revision
+ LVITEM item = {0};
+ int nItemCount = ListView_GetItemCount(m_hListControl);
+ for (int i=0; iUrl();
+ break;
+ }
+ }
+ }
+ }
wstring::iterator it_begin = search(commandline.begin(), commandline.end(), tag.begin(), tag.end());
- if (it_begin != commandline.end())
+ if (m_commitsradio && it_begin != commandline.end())
{
// find the revision
LVITEM item = {0};
@@ -1143,8 +1232,19 @@
wstring::iterator it_end= it_begin + tag.size();
commandline.replace(it_begin, it_end, info->name);
}
- if (!commandline.empty())
+ if (!m_commitsradio && !reviewurl.empty())
{
+ tag = _T("api/review-requests");
+ it_begin = search(reviewurl.begin(), reviewurl.end(), tag.begin(), tag.end());
+ if (it_begin != reviewurl.end())
+ {
+ wstring::iterator it_end= it_begin + tag.size();
+ reviewurl.replace(it_begin, it_end, TEXT("r"));
+ }
+ ShellExecute(*this, _T("open"), reviewurl.c_str(), NULL, NULL, SW_SHOWNORMAL);
+ }
+ else if (!commandline.empty())
+ {
ShellExecute(*this, _T("open"), commandline.c_str(), NULL, NULL, SW_SHOWNORMAL);
}
}
@@ -1168,12 +1268,15 @@
{
dlg.SetInfo(&pRead->find(*(wstring*)itemex.lParam)->second);
wstring origurl = dlg.GetInfo()->url;
+ dlg.SetReviewMonitored(gReviewBoard.IsUrlMonitored(origurl));
if (id == ID_POPUP_ADDPROJECTWITHTEMPLATE)
dlg.ClearForTemplate();
m_pURLInfos->ReleaseReadOnlyData();
if (dlg.DoModal(hResource, IDD_URLCONFIG, *this) == IDOK)
{
CUrlInfo * inf = dlg.GetInfo();
+ bool monitor = dlg.GetReviewMonitored();
+ bool needsaverev = false;
if ((inf)&&inf->name.size())
{
inf->errNr = 0;
@@ -1182,9 +1285,25 @@
if ((inf) && (inf->url.size()) && ((origurl.compare(inf->url)) || (id == ID_MAIN_EDIT)))
{
if (id == ID_MAIN_EDIT)
+ {
pWrite->erase(*(wstring*)itemex.lParam);
+ gReviewBoard.RemoveBranchToMonitor(origurl);
+ needsaverev = true;
+ }
(*pWrite)[inf->url] = *inf;
}
+ if(monitor && !gReviewBoard.IsUrlMonitored(inf->url))
+ {
+ gReviewBoard.AddBranchToMonitor(inf->url);
+ needsaverev = true;
+ }
+ else if(!monitor && gReviewBoard.IsUrlMonitored(inf->url))
+ {
+ gReviewBoard.RemoveBranchToMonitor(inf->url);
+ needsaverev = true;
+ }
+ if(needsaverev)
+ gReviewBoard.Save();
m_pURLInfos->Save();
m_pURLInfos->ReleaseWriteData();
RefreshURLTree(false);
@@ -1291,52 +1410,106 @@
ListView_GetItem(m_hListControl, &item);
if (item.state & LVIS_SELECTED)
{
- SCCSLogEntry * pLogEntry = (SCCSLogEntry*)item.lParam;
- if (pLogEntry)
+ if(m_commitsradio)
{
- // get the info to put on the clipboard
- _stprintf_s(tempBuf, 1024, _T("Revision: %ld\nAuthor: %s\nDate: %s\nMessage:\n"),
- pLogEntry->revision,
- pLogEntry->author.c_str(),
- CAppUtils::ConvertDate(pLogEntry->date).c_str());
- sClipboardData += tempBuf;
- sClipboardData += pLogEntry->message;
- sClipboardData += _T("\n-------------------------------\n");
- // now add all changed paths, one path per line
- for (map::const_iterator it = pLogEntry->m_changedPaths.begin(); it != pLogEntry->m_changedPaths.end(); ++it)
+ SCCSLogEntry * pLogEntry = (SCCSLogEntry*)item.lParam;
+ if (pLogEntry)
{
- // action
- sClipboardData += it->second.action;
- bool mods = false;
- if ((it->second.text_modified == svn_tristate_true)||(it->second.props_modified == svn_tristate_true))
+ // get the info to put on the clipboard
+ _stprintf_s(tempBuf, 1024, _T("Revision: %ld\nAuthor: %s\nDate: %s\nMessage:\n"),
+ pLogEntry->revision,
+ pLogEntry->author.c_str(),
+ CAppUtils::ConvertDate(pLogEntry->date).c_str());
+ sClipboardData += tempBuf;
+ sClipboardData += pLogEntry->message;
+ sClipboardData += _T("\n-------------------------------\n");
+ // now add all changed paths, one path per line
+ for (map::const_iterator it = pLogEntry->m_changedPaths.begin(); it != pLogEntry->m_changedPaths.end(); ++it)
{
- mods = true;
+ // action
+ sClipboardData += it->second.action;
+ bool mods = false;
+ if ((it->second.text_modified == svn_tristate_true)||(it->second.props_modified == svn_tristate_true))
+ {
+ mods = true;
+ }
+ if (mods)
+ sClipboardData += L"(";
+ if (it->second.text_modified == svn_tristate_true)
+ sClipboardData += L"T";
+ else if (mods)
+ sClipboardData += L" ";
+ if (it->second.props_modified == svn_tristate_true)
+ sClipboardData += L"P";
+ else if (mods)
+ sClipboardData += L" ";
+ if (mods)
+ sClipboardData += L")";
+ sClipboardData += _T(" : ");
+ sClipboardData += it->first;
+ sClipboardData += _T(" ");
+ if (!it->second.copyfrom_path.empty())
+ {
+ sClipboardData += _T("(copied from: ");
+ sClipboardData += it->second.copyfrom_path;
+ sClipboardData += _T(", revision ");
+ _stprintf_s(tempBuf, 1024, _T("%ld)\n"), it->second.copyfrom_revision);
+ sClipboardData += wstring(tempBuf);
+ }
+ else
+ sClipboardData += _T("\n\n");
}
- if (mods)
- sClipboardData += L"(";
- if (it->second.text_modified == svn_tristate_true)
- sClipboardData += L"T";
- else if (mods)
- sClipboardData += L" ";
- if (it->second.props_modified == svn_tristate_true)
- sClipboardData += L"P";
- else if (mods)
- sClipboardData += L" ";
- if (mods)
- sClipboardData += L")";
- sClipboardData += _T(" : ");
- sClipboardData += it->first;
- sClipboardData += _T(" ");
- if (!it->second.copyfrom_path.empty())
+ }
+ }
+ else
+ {
+ ReviewBoardReview * pLogEntry = (ReviewBoardReview*)item.lParam;
+ if (pLogEntry)
+ {
+ // get the info to put on the clipboard
+ _stprintf_s(tempBuf, 1024, _T("Review id: %s\nAuthor: %s\nDate: %s\nMessage:\n"),
+ pLogEntry->Reviewid().c_str(),
+ pLogEntry->Author().c_str(),
+ pLogEntry->Date().c_str());
+ sClipboardData += tempBuf;
+ sClipboardData += pLogEntry->Message();
+ sClipboardData += _T("\n-------------------------------\n");
+/* // now add all changed paths, one path per line
+ for (map::const_iterator it = pLogEntry->m_changedPaths.begin(); it != pLogEntry->m_changedPaths.end(); ++it)
{
- sClipboardData += _T("(copied from: ");
- sClipboardData += it->second.copyfrom_path;
- sClipboardData += _T(", revision ");
- _stprintf_s(tempBuf, 1024, _T("%ld)\n"), it->second.copyfrom_revision);
- sClipboardData += wstring(tempBuf);
- }
- else
- sClipboardData += _T("\n\n");
+ // action
+ sClipboardData += it->second.action;
+ bool mods = false;
+ if ((it->second.text_modified == svn_tristate_true)||(it->second.props_modified == svn_tristate_true))
+ {
+ mods = true;
+ }
+ if (mods)
+ sClipboardData += L"(";
+ if (it->second.text_modified == svn_tristate_true)
+ sClipboardData += L"T";
+ else if (mods)
+ sClipboardData += L" ";
+ if (it->second.props_modified == svn_tristate_true)
+ sClipboardData += L"P";
+ else if (mods)
+ sClipboardData += L" ";
+ if (mods)
+ sClipboardData += L")";
+ sClipboardData += _T(" : ");
+ sClipboardData += it->first;
+ sClipboardData += _T(" ");
+ if (!it->second.copyfrom_path.empty())
+ {
+ sClipboardData += _T("(copied from: ");
+ sClipboardData += it->second.copyfrom_path;
+ sClipboardData += _T(", revision ");
+ _stprintf_s(tempBuf, 1024, _T("%ld)\n"), it->second.copyfrom_revision);
+ sClipboardData += wstring(tempBuf);
+ }
+ else
+ sClipboardData += _T("\n\n");
+ }*/
}
}
}
@@ -1391,6 +1564,13 @@
}
}
break;
+ case IDC_RADIO2:
+ case IDC_RADIO3:
+ {
+ m_commitsradio = !m_commitsradio;
+ TreeItemSelected(m_hTreeControl, TreeView_GetSelection(m_hTreeControl));
+ }
+ break;
default:
return 0;
}
@@ -1447,222 +1627,298 @@
ListView_GetItem(m_hListControl, &item);
if (item.state & LVIS_SELECTED)
{
- SCCSLogEntry * pLogEntry = (SCCSLogEntry*)item.lParam;
+ if(m_commitsradio)
+ {
+ SCCSLogEntry * pLogEntry = (SCCSLogEntry*)item.lParam;
- // Switch how the diff is done in SVN / Accurev
- switch(pUrlInfo->sccs) {
- default:
- case CUrlInfo::SCCS_SVN:
- {
- // find the diff name
- const CUrlInfo * pInfo = &pRead->find(*(wstring*)itemex.lParam)->second;
- // in case the project name has 'path' chars in it, we have to remove those first
- _stprintf_s(buf, 4096, _T("%s_%ld.diff"), CAppUtils::ConvertName(pInfo->name).c_str(), pLogEntry->revision);
- wstring diffFileName = CAppUtils::GetDataDir();
- diffFileName += _T("\\");
- diffFileName += wstring(buf);
- // construct a title for the diff viewer
- _stprintf_s(buf, 4096, _T("%s, revision %ld"), pInfo->name.c_str(), pLogEntry->revision);
- wstring title = wstring(buf);
- // start the diff viewer
- wstring cmd;
- wstring tsvninstalled = CAppUtils::GetTSVNPath();
- wstring sVer = CAppUtils::GetVersionStringFromExe(tsvninstalled.c_str());
- if ((bUseTSVN)&&(!tsvninstalled.empty())&&(_tstoi(sVer.substr(3, 4).c_str()) > 4))
- {
- // yes, we have TSVN installed
- // call TortoiseProc to do the diff for us
- cmd = _T("\"");
- cmd += wstring(tsvninstalled);
- cmd += _T("\" /command:diff /path:\"");
- cmd += pInfo->url;
- cmd += _T("\" /startrev:");
+ // Switch how the diff is done in SVN / Accurev
+ switch(pUrlInfo->sccs) {
+ default:
+ case CUrlInfo::SCCS_SVN:
+ {
+ // find the diff name
+ const CUrlInfo * pInfo = &pRead->find(*(wstring*)itemex.lParam)->second;
+ // in case the project name has 'path' chars in it, we have to remove those first
+ _stprintf_s(buf, 4096, _T("%s_%ld.diff"), CAppUtils::ConvertName(pInfo->name).c_str(), pLogEntry->revision);
+ wstring diffFileName = CAppUtils::GetDataDir();
+ diffFileName += _T("\\");
+ diffFileName += wstring(buf);
+ // construct a title for the diff viewer
+ _stprintf_s(buf, 4096, _T("%s, revision %ld"), pInfo->name.c_str(), pLogEntry->revision);
+ wstring title = wstring(buf);
+ // start the diff viewer
+ wstring cmd;
+ wstring tsvninstalled = CAppUtils::GetTSVNPath();
+ wstring sVer = CAppUtils::GetVersionStringFromExe(tsvninstalled.c_str());
+ if ((bUseTSVN)&&(!tsvninstalled.empty())&&(_tstoi(sVer.substr(3, 4).c_str()) > 4))
+ {
+ // yes, we have TSVN installed
+ // call TortoiseProc to do the diff for us
+ cmd = _T("\"");
+ cmd += wstring(tsvninstalled);
+ cmd += _T("\" /command:diff /path:\"");
+ cmd += pInfo->url;
+ cmd += _T("\" /startrev:");
- TCHAR numBuf[100] = {0};
- _stprintf_s(numBuf, 100, _T("%ld"), pLogEntry->revision-1);
- cmd += numBuf;
- cmd += _T(" /endrev:");
- _stprintf_s(numBuf, 100, _T("%ld"), pLogEntry->revision);
- cmd += numBuf;
- CAppUtils::LaunchApplication(cmd);
- }
- else
- {
- TCHAR apppath[4096];
- GetModuleFileName(NULL, apppath, 4096);
- CRegStdString diffViewer = CRegStdString(_T("Software\\CommitMonitor\\DiffViewer"));
- if (wstring(diffViewer).empty())
- {
- cmd = apppath;
- cmd += _T(" /patchfile:\"");
+ TCHAR numBuf[100] = {0};
+ _stprintf_s(numBuf, 100, _T("%ld"), pLogEntry->revision-1);
+ cmd += numBuf;
+ cmd += _T(" /endrev:");
+ _stprintf_s(numBuf, 100, _T("%ld"), pLogEntry->revision);
+ cmd += numBuf;
+ CAppUtils::LaunchApplication(cmd);
}
else
{
- cmd = (wstring)diffViewer;
- cmd += _T(" \"");
- }
- cmd += diffFileName;
- cmd += _T("\"");
- if (wstring(diffViewer).empty())
- {
- cmd += _T(" /title:\"");
- cmd += title;
- cmd += _T("\"");
- }
- // Check if the diff file exists. If it doesn't, we have to fetch
- // the diff first
- if (!PathFileExists(diffFileName.c_str()))
- {
- // fetch the diff
- SVN svn;
- svn.SetAuthInfo(pInfo->username, pInfo->password);
- CProgressDlg progDlg;
- svn.SetAndClearProgressInfo(&progDlg);
- progDlg.SetTitle(_T("Fetching Diff"));
- TCHAR dispbuf[MAX_PATH] = {0};
- _stprintf_s(dispbuf, MAX_PATH, _T("fetching diff of revision %ld"), pLogEntry->revision);
- progDlg.SetLine(1, dispbuf);
- progDlg.SetShowProgressBar(false);
- progDlg.ShowModeless(*this);
- progDlg.SetLine(1, dispbuf);
- progDlg.SetProgress(3, 100); // set some dummy progress
- CRegStdString diffParams = CRegStdString(_T("Software\\CommitMonitor\\DiffParameters"));
- if (!svn.Diff(pInfo->url, pLogEntry->revision, pLogEntry->revision-1, pLogEntry->revision, true, true, false, diffParams, false, diffFileName, wstring()))
+ TCHAR apppath[4096];
+ GetModuleFileName(NULL, apppath, 4096);
+ CRegStdString diffViewer = CRegStdString(_T("Software\\CommitMonitor\\DiffViewer"));
+ if (wstring(diffViewer).empty())
{
- progDlg.Stop();
- if (svn.Err->apr_err != SVN_ERR_CANCELLED)
- ::MessageBox(*this, svn.GetLastErrorMsg().c_str(), _T("CommitMonitor"), MB_ICONERROR);
- DeleteFile(diffFileName.c_str());
+ cmd = apppath;
+ cmd += _T(" /patchfile:\"");
}
else
{
- TRACE(_T("Diff fetched for %s, revision %ld\n"), pInfo->url.c_str(), pLogEntry->revision);
- progDlg.Stop();
+ cmd = (wstring)diffViewer;
+ cmd += _T(" \"");
}
+ cmd += diffFileName;
+ cmd += _T("\"");
+ if (wstring(diffViewer).empty())
+ {
+ cmd += _T(" /title:\"");
+ cmd += title;
+ cmd += _T("\"");
+ }
+ // Check if the diff file exists. If it doesn't, we have to fetch
+ // the diff first
+ if (!PathFileExists(diffFileName.c_str()))
+ {
+ // fetch the diff
+ SVN svn;
+ svn.SetAuthInfo(pInfo->username, pInfo->password);
+ CProgressDlg progDlg;
+ svn.SetAndClearProgressInfo(&progDlg);
+ progDlg.SetTitle(_T("Fetching Diff"));
+ TCHAR dispbuf[MAX_PATH] = {0};
+ _stprintf_s(dispbuf, MAX_PATH, _T("fetching diff of revision %ld"), pLogEntry->revision);
+ progDlg.SetLine(1, dispbuf);
+ progDlg.SetShowProgressBar(false);
+ progDlg.ShowModeless(*this);
+ progDlg.SetLine(1, dispbuf);
+ progDlg.SetProgress(3, 100); // set some dummy progress
+ CRegStdString diffParams = CRegStdString(_T("Software\\CommitMonitor\\DiffParameters"));
+ if (!svn.Diff(pInfo->url, pLogEntry->revision, pLogEntry->revision-1, pLogEntry->revision, true, true, false, diffParams, false, diffFileName, wstring()))
+ {
+ progDlg.Stop();
+ if (svn.Err->apr_err != SVN_ERR_CANCELLED)
+ ::MessageBox(*this, svn.GetLastErrorMsg().c_str(), _T("CommitMonitor"), MB_ICONERROR);
+ DeleteFile(diffFileName.c_str());
+ }
+ else
+ {
+ TRACE(_T("Diff fetched for %s, revision %ld\n"), pInfo->url.c_str(), pLogEntry->revision);
+ progDlg.Stop();
+ }
+ }
+ if (PathFileExists(diffFileName.c_str()))
+ CAppUtils::LaunchApplication(cmd);
}
- if (PathFileExists(diffFileName.c_str()))
- CAppUtils::LaunchApplication(cmd);
- }
- }
- break;
+ }
+ break;
- case CUrlInfo::SCCS_ACCUREV:
- {
- /* Accurev 'diff' cannot be used as it mutex locks itself to only allow diffing of one
- * file at a time... how typical. Therefore we 'pop' (get copies) of the correct versions
- * of each file and then diff the directories :)
- * TODO: Somehow hold onto and delete the temporary dirs when commit monitor is closed */
- CRegStdString accurevExe = CRegStdString(_T("Software\\CommitMonitor\\AccurevExe"));
+ case CUrlInfo::SCCS_ACCUREV:
+ {
+ /* Accurev 'diff' cannot be used as it mutex locks itself to only allow diffing of one
+ * file at a time... how typical. Therefore we 'pop' (get copies) of the correct versions
+ * of each file and then diff the directories :)
+ * TODO: Somehow hold onto and delete the temporary dirs when commit monitor is closed */
+ CRegStdString accurevExe = CRegStdString(_T("Software\\CommitMonitor\\AccurevExe"));
- wchar_t transactionNo[64];
- _itow_s(pLogEntry->revision, transactionNo, 10);
+ wchar_t transactionNo[64];
+ _itow_s(pLogEntry->revision, transactionNo, 10);
- wstring uuid;
- CAppUtils::CreateUUIDString(uuid);
+ wstring uuid;
+ CAppUtils::CreateUUIDString(uuid);
- wstring newTempPath = wstring(origTempPath);
- newTempPath.append(_T("\\"));
- newTempPath.append(uuid);
- wstring sLatestRev(transactionNo);
- wstring sBasisRev = _T("BASIS");
- wstring latestDir(newTempPath + _T("\\") + sLatestRev);
- wstring basisDir(newTempPath + _T("\\") + sBasisRev);
- CreateDirectory(newTempPath.c_str(), NULL);
- CreateDirectory(latestDir.c_str(), NULL);
- CreateDirectory(basisDir.c_str(), NULL);
+ wstring newTempPath = wstring(origTempPath);
+ newTempPath.append(_T("\\"));
+ newTempPath.append(uuid);
+ wstring sLatestRev(transactionNo);
+ wstring sBasisRev = _T("BASIS");
+ wstring latestDir(newTempPath + _T("\\") + sLatestRev);
+ wstring basisDir(newTempPath + _T("\\") + sBasisRev);
+ CreateDirectory(newTempPath.c_str(), NULL);
+ CreateDirectory(latestDir.c_str(), NULL);
+ CreateDirectory(basisDir.c_str(), NULL);
- // For each file that should be diffed
- for (map::const_iterator it = pLogEntry->m_changedPaths.begin(); it != pLogEntry->m_changedPaths.end(); ++it)
- {
- // Parse the file and file revision from the stored URL
- wstring rawPath = it->first;
+ // For each file that should be diffed
+ for (map::const_iterator it = pLogEntry->m_changedPaths.begin(); it != pLogEntry->m_changedPaths.end(); ++it)
+ {
+ // Parse the file and file revision from the stored URL
+ wstring rawPath = it->first;
- int lastBracket = rawPath.rfind(L" (");
- rawPath.erase(lastBracket, wstring::npos);
- int lastForwardSlash = rawPath.rfind(L"/");
- wstring sLatestAccuRevision(rawPath);
- sLatestAccuRevision.erase(0, lastForwardSlash+1);
- int iAccuRevision = _wtoi(sLatestAccuRevision.c_str());
- wchar_t basisRevisionNo[64];
- _itow_s(iAccuRevision-1, basisRevisionNo, 10);
- wstring sBasisAccuRevision(basisRevisionNo);
+ int lastBracket = rawPath.rfind(L" (");
+ rawPath.erase(lastBracket, wstring::npos);
+ int lastForwardSlash = rawPath.rfind(L"/");
+ wstring sLatestAccuRevision(rawPath);
+ sLatestAccuRevision.erase(0, lastForwardSlash+1);
+ int iAccuRevision = _wtoi(sLatestAccuRevision.c_str());
+ wchar_t basisRevisionNo[64];
+ _itow_s(iAccuRevision-1, basisRevisionNo, 10);
+ wstring sBasisAccuRevision(basisRevisionNo);
- wstring finalPath(rawPath);
- int lastSpace = rawPath.rfind(L" ");
- finalPath.erase(lastSpace, wstring::npos);
+ wstring finalPath(rawPath);
+ int lastSpace = rawPath.rfind(L" ");
+ finalPath.erase(lastSpace, wstring::npos);
- // Can't diff unless there is a version to diff against :)
- if (iAccuRevision >= 1) {
+ // Can't diff unless there is a version to diff against :)
+ if (iAccuRevision >= 1) {
- // Check out the latest file
- // Build the accurev command line
- for (int i=0; i<2; i++) {
- wstring accurevPopCmd;
- wstring rev;
- wstring dir;
+ // Check out the latest file
+ // Build the accurev command line
+ for (int i=0; i<2; i++) {
+ wstring accurevPopCmd;
+ wstring rev;
+ wstring dir;
- switch (i) {
- default:
- case 0:
- rev = sLatestAccuRevision;
- dir = latestDir;
- break;
- case 1:
- rev = sBasisAccuRevision;
- dir = basisDir;
- break;
- }
+ switch (i) {
+ default:
+ case 0:
+ rev = sLatestAccuRevision;
+ dir = latestDir;
+ break;
+ case 1:
+ rev = sBasisAccuRevision;
+ dir = basisDir;
+ break;
+ }
- /* If this is the basis version, and there is none, since the file was added, then break
- * so we only check out the new version. This will then be shown in the directory compare :) */
- if ((i == 1) && (iAccuRevision == 1)) break;
+ /* If this is the basis version, and there is none, since the file was added, then break
+ * so we only check out the new version. This will then be shown in the directory compare :) */
+ if ((i == 1) && (iAccuRevision == 1)) break;
- accurevPopCmd.append(_T("\""));
- accurevPopCmd.append(wstring(accurevExe));
- accurevPopCmd.append(_T("\" pop -O -R -v "));
- accurevPopCmd.append(pUrlInfo->url);
- accurevPopCmd.append(_T("/"));
- accurevPopCmd.append(rev);
- accurevPopCmd.append(_T(" -L \""));
- accurevPopCmd.append(dir);
- accurevPopCmd.append(_T("\" \""));
- accurevPopCmd.append(finalPath);
- accurevPopCmd.append(_T("\""));
+ accurevPopCmd.append(_T("\""));
+ accurevPopCmd.append(wstring(accurevExe));
+ accurevPopCmd.append(_T("\" pop -O -R -v "));
+ accurevPopCmd.append(pUrlInfo->url);
+ accurevPopCmd.append(_T("/"));
+ accurevPopCmd.append(rev);
+ accurevPopCmd.append(_T(" -L \""));
+ accurevPopCmd.append(dir);
+ accurevPopCmd.append(_T("\" \""));
+ accurevPopCmd.append(finalPath);
+ accurevPopCmd.append(_T("\""));
- // Run accurev to perform the pop command
- CAppUtils::LaunchApplication(accurevPopCmd, true, true, true);
+ // Run accurev to perform the pop command
+ CAppUtils::LaunchApplication(accurevPopCmd, true, true, true);
+ }
+
+ }
}
- }
- }
+ CRegStdString diffCmd = CRegStdString(_T("Software\\CommitMonitor\\AccurevDiffCmd"));
+ wstring finalDiffCmd;
- CRegStdString diffCmd = CRegStdString(_T("Software\\CommitMonitor\\AccurevDiffCmd"));
- wstring finalDiffCmd;
+ // Build the final diff command
+ finalDiffCmd.append(wstring(diffCmd));
- // Build the final diff command
- finalDiffCmd.append(wstring(diffCmd));
+ // Find and replace "%OLD"
+ int pos = finalDiffCmd.find(_T("%OLD"));
+ finalDiffCmd.replace(pos, 4, sBasisRev, 0, sBasisRev.size());
- // Find and replace "%OLD"
- int pos = finalDiffCmd.find(_T("%OLD"));
- finalDiffCmd.replace(pos, 4, sBasisRev, 0, sBasisRev.size());
+ // Find and replace "%NEW"
+ pos = finalDiffCmd.find(_T("%NEW"));
+ finalDiffCmd.replace(pos, 4, sLatestRev, 0, sLatestRev.size());
- // Find and replace "%NEW"
- pos = finalDiffCmd.find(_T("%NEW"));
- finalDiffCmd.replace(pos, 4, sLatestRev, 0, sLatestRev.size());
+ // Find and replace "%1"
+ pos = finalDiffCmd.find(_T("%1"));
+ finalDiffCmd.replace(pos, 2, basisDir, 0, basisDir.size());
- // Find and replace "%1"
- pos = finalDiffCmd.find(_T("%1"));
- finalDiffCmd.replace(pos, 2, basisDir, 0, basisDir.size());
+ // Find and replace "%2"
+ pos = finalDiffCmd.find(_T("%2"));
+ finalDiffCmd.replace(pos, 2, latestDir, 0, latestDir.size());
- // Find and replace "%2"
- pos = finalDiffCmd.find(_T("%2"));
- finalDiffCmd.replace(pos, 2, latestDir, 0, latestDir.size());
+ // Run accurev to perform the diff command
+ CAppUtils::LaunchApplication(finalDiffCmd, true, false, false);
+ }
+ break;
+ }
+ }
+ else
+ {
+ ReviewBoardReview * pLogEntry = (ReviewBoardReview*)item.lParam;
+
+ // Switch how the diff is done in SVN / Accurev
+ switch(pUrlInfo->sccs) {
+ default:
+ case CUrlInfo::SCCS_SVN:
+ {
+ // find the diff name
+ const CUrlInfo * pInfo = &pRead->find(*(wstring*)itemex.lParam)->second;
+ // in case the project name has 'path' chars in it, we have to remove those first
+ _stprintf_s(buf, 4096, _T("%s_review_%s.diff"), CAppUtils::ConvertName(pInfo->name).c_str(), pLogEntry->Reviewid().c_str());
+ wstring diffFileName = CAppUtils::GetDataDir();
+ diffFileName += _T("\\");
+ diffFileName += wstring(buf);
+ // construct a title for the diff viewer
+ _stprintf_s(buf, 4096, _T("%s, review id %s"), pInfo->name.c_str(), pLogEntry->Reviewid().c_str());
+ wstring title = wstring(buf);
+ // start the diff viewer
+ wstring cmd;
- // Run accurev to perform the diff command
- CAppUtils::LaunchApplication(finalDiffCmd, true, false, false);
+ TCHAR apppath[4096];
+ GetModuleFileName(NULL, apppath, 4096);
+ CRegStdString diffViewer = CRegStdString(_T("Software\\CommitMonitor\\DiffViewer"));
+ if (wstring(diffViewer).empty())
+ {
+ cmd = apppath;
+ cmd += _T(" /patchfile:\"");
+ }
+ else
+ {
+ cmd = (wstring)diffViewer;
+ cmd += _T(" \"");
+ }
+ cmd += diffFileName;
+ cmd += _T("\"");
+ if (wstring(diffViewer).empty())
+ {
+ cmd += _T(" /title:\"");
+ cmd += title;
+ cmd += _T("\"");
+ }
+ // Check if the diff file exists. If it doesn't, we have to fetch
+ // the diff first
+ if (!PathFileExists(diffFileName.c_str()))
+ {
+ // fetch the diff
+ SVN svn;
+ svn.SetAuthInfo(pInfo->username, pInfo->password);
+ CProgressDlg progDlg;
+ svn.SetAndClearProgressInfo(&progDlg);
+ progDlg.SetTitle(_T("Fetching Diff"));
+ TCHAR dispbuf[MAX_PATH] = {0};
+ _stprintf_s(dispbuf, MAX_PATH, _T("fetching diff of review id %s"), pLogEntry->Reviewid().c_str());
+ progDlg.SetLine(1, dispbuf);
+ progDlg.SetShowProgressBar(false);
+ progDlg.ShowModeless(*this);
+ progDlg.SetLine(1, dispbuf);
+ progDlg.SetProgress(3, 100); // set some dummy progress
+ CRegStdString diffParams = CRegStdString(_T("Software\\CommitMonitor\\DiffParameters"));
+ ReviewBoardReviewUnifiedDiff unidiff;
+ unidiff.Open(pLogEntry->reviewid.c_str());
+ unidiff.Save(diffFileName);
+ TRACE(_T("Diff fetched for %s, review id %s\n"), pInfo->url.c_str(), pLogEntry->Reviewid().c_str());
+ progDlg.Stop();
+ }
+ if (PathFileExists(diffFileName.c_str()))
+ CAppUtils::LaunchApplication(cmd);
+ }
+ break;
}
- break;
}
}
}
@@ -1699,7 +1955,7 @@
tv.hParent = FindParentTreeNode(it->first);
tv.hInsertAfter = TVI_SORT;
tv.itemex.mask = TVIF_TEXT|TVIF_PARAM|TVIF_STATE|TVIF_IMAGE|TVIF_SELECTEDIMAGE;
- WCHAR * str = new WCHAR[it->second.name.size()+10];
+ WCHAR * str = new WCHAR[it->second.name.size()+30];
// find out if there are some unread entries
int unread = 0;
for (map::const_iterator logit = it->second.logentries.begin(); logit != it->second.logentries.end(); ++logit)
@@ -1707,9 +1963,10 @@
if (!logit->second.read)
unread++;
}
- if (unread)
+ int unreadreviews = gReviewBoard.GetUnreadReviewsForUrl(it->second.url);
+ if (unread || unreadreviews)
{
- _stprintf_s(str, it->second.name.size()+10, _T("%s (%d)"), it->second.name.c_str(), unread);
+ _stprintf_s(str, it->second.name.size()+30, _T("%s (%d + %d)"), it->second.name.c_str(), unread, unreadreviews);
tv.itemex.state = TVIS_BOLD;
tv.itemex.stateMask = TVIS_BOLD;
}
@@ -1747,7 +2004,7 @@
HTREEITEM hItem = TreeView_InsertItem(m_hTreeControl, &tv);
if ((!bShowLastUnread)&&(m_lastSelectedProject.compare(it->second.name) == 0))
tvToSel = hItem;
- if ((unread)&&(tvToSel == 0))
+ if ((unread || unreadreviews)&&(tvToSel == 0))
tvToSel = hItem;
TreeView_Expand(m_hTreeControl, tv.hParent, TVE_EXPAND);
delete [] str;
@@ -1955,14 +2212,28 @@
lvc.mask = LVCF_TEXT;
lvc.fmt = LVCFMT_LEFT;
lvc.cx = -1;
- lvc.pszText = _T("revision");
- ListView_InsertColumn(m_hListControl, 0, &lvc);
- lvc.pszText = _T("date");
- ListView_InsertColumn(m_hListControl, 1, &lvc);
- lvc.pszText = _T("author");
- ListView_InsertColumn(m_hListControl, 2, &lvc);
- lvc.pszText = _T("log message");
- ListView_InsertColumn(m_hListControl, 3, &lvc);
+ if(m_commitsradio)
+ {
+ lvc.pszText = _T("revision");
+ ListView_InsertColumn(m_hListControl, 0, &lvc);
+ lvc.pszText = _T("date");
+ ListView_InsertColumn(m_hListControl, 1, &lvc);
+ lvc.pszText = _T("author");
+ ListView_InsertColumn(m_hListControl, 2, &lvc);
+ lvc.pszText = _T("log message");
+ ListView_InsertColumn(m_hListControl, 3, &lvc);
+ }
+ else
+ {
+ lvc.pszText = _T("review id");
+ ListView_InsertColumn(m_hListControl, 0, &lvc);
+ lvc.pszText = _T("date");
+ ListView_InsertColumn(m_hListControl, 1, &lvc);
+ lvc.pszText = _T("author");
+ ListView_InsertColumn(m_hListControl, 2, &lvc);
+ lvc.pszText = _T("log message");
+ ListView_InsertColumn(m_hListControl, 3, &lvc);
+ }
LVITEM item = {0};
TCHAR buf[1024];
@@ -1984,99 +2255,203 @@
delete [] buffer;
- for (map::const_iterator it = info->logentries.begin(); it != info->logentries.end(); ++it)
+ if(m_commitsradio)
{
- // only add entries that match the filter string
- bool addEntry = true;
- bool useFilter = filterstringlower.size() != 0;
- bool bUseRegex = (filterstring.size() > 1)&&(filterstring[0] == '\\');
+ for (map::const_iterator it = info->logentries.begin(); it != info->logentries.end(); ++it)
+ {
+ // only add entries that match the filter string
+ bool addEntry = true;
+ bool useFilter = filterstringlower.size() != 0;
+ bool bUseRegex = (filterstring.size() > 1)&&(filterstring[0] == '\\');
- if (useFilter)
- {
- if (bUseRegex)
- {
- try
+ if (useFilter)
{
- const tr1::wregex regCheck(filterstring.substr(1), tr1::regex_constants::icase | tr1::regex_constants::ECMAScript);
+ if (bUseRegex)
+ {
+ try
+ {
+ const tr1::wregex regCheck(filterstring.substr(1), tr1::regex_constants::icase | tr1::regex_constants::ECMAScript);
- addEntry = tr1::regex_search(it->second.author, regCheck);
+ addEntry = tr1::regex_search(it->second.author, regCheck);
+ if (!addEntry)
+ {
+ addEntry = tr1::regex_search(it->second.message, regCheck);
+ if (!addEntry)
+ {
+ _stprintf_s(buf, 1024, _T("%ld"), it->first);
+ wstring s = wstring(buf);
+ addEntry = tr1::regex_search(s, regCheck);
+ }
+ }
+ }
+ catch (exception)
+ {
+ bUseRegex = false;
+ }
+ if (bNegateFilter)
+ addEntry = !addEntry;
+ }
+ if (!bUseRegex)
+ {
+ // search plain text
+ // note: \Q...\E doesn't seem to work with tr1 - it still
+ // throws an exception if the regex in between is not a valid regex :(
+
+ wstring s = it->second.author;
+ std::transform(s.begin(), s.end(), s.begin(), std::tolower);
+ addEntry = s.find(filterstringlower) != wstring::npos;
+
if (!addEntry)
{
- addEntry = tr1::regex_search(it->second.message, regCheck);
+ s = it->second.message;
+ std::transform(s.begin(), s.end(), s.begin(), std::tolower);
+ addEntry = s.find(filterstringlower) != wstring::npos;
if (!addEntry)
{
_stprintf_s(buf, 1024, _T("%ld"), it->first);
- wstring s = wstring(buf);
- addEntry = tr1::regex_search(s, regCheck);
+ s = buf;
+ addEntry = s.find(filterstringlower) != wstring::npos;
}
}
+ if (bNegateFilter)
+ addEntry = !addEntry;
}
- catch (exception)
+ }
+
+ if (!addEntry)
+ continue;
+
+ item.mask = LVIF_TEXT|LVIF_PARAM;
+ item.iItem = 0;
+ item.lParam = (LPARAM)&it->second;
+ _stprintf_s(buf, 1024, _T("%ld"), it->first);
+ item.pszText = buf;
+ ListView_InsertItem(m_hListControl, &item);
+ if (it->second.date)
+ _tcscpy_s(buf, 1024, CAppUtils::ConvertDate(it->second.date).c_str());
+ else
+ _tcscpy_s(buf, 1024, _T("(no date)"));
+ ListView_SetItemText(m_hListControl, 0, 1, buf);
+ if (it->second.author.size())
+ _tcscpy_s(buf, 1024, it->second.author.c_str());
+ else
+ _tcscpy_s(buf, 1024, _T("(no author)"));
+ ListView_SetItemText(m_hListControl, 0, 2, buf);
+ wstring msg = it->second.message;
+ std::remove(msg.begin(), msg.end(), '\r');
+ std::replace(msg.begin(), msg.end(), '\n', ' ');
+ std::replace(msg.begin(), msg.end(), '\t', ' ');
+ _tcsncpy_s(buf, 1024, msg.c_str(), 1023);
+ ListView_SetItemText(m_hListControl, 0, 3, buf);
+
+ if ((iLastUnread < 0)&&(!it->second.read))
{
- bUseRegex = false;
+ iLastUnread = 0;
}
- if (bNegateFilter)
- addEntry = !addEntry;
+ if (iLastUnread >= 0)
+ iLastUnread++;
}
- if (!bUseRegex)
+ }
+ else
+ {
+ ReviewBoard::WR2Rmap vec;
+ gReviewBoard.GetReviewsForUrl(info->url,vec);
+ gReviewBoard.GetReadLock();
+ for (ReviewBoard::WR2Rmap::const_iterator it = vec.begin(); it != vec.end(); ++it)
{
- // search plain text
- // note: \Q...\E doesn't seem to work with tr1 - it still
- // throws an exception if the regex in between is not a valid regex :(
+ // only add entries that match the filter string
+ bool addEntry = true;
+ bool useFilter = filterstringlower.size() != 0;
+ bool bUseRegex = (filterstring.size() > 1)&&(filterstring[0] == '\\');
- wstring s = it->second.author;
- std::transform(s.begin(), s.end(), s.begin(), std::tolower);
- addEntry = s.find(filterstringlower) != wstring::npos;
+ if (useFilter)
+ {
+ if (bUseRegex)
+ {
+ try
+ {
+ const tr1::wregex regCheck(filterstring.substr(1), tr1::regex_constants::icase | tr1::regex_constants::ECMAScript);
- if (!addEntry)
+ addEntry = tr1::regex_search(it->second->Author(), regCheck);
+ if (!addEntry)
+ {
+ addEntry = tr1::regex_search(it->second->Message(), regCheck);
+ if (!addEntry)
+ {
+ _stprintf_s(buf, 1024, _T("%ld"), it->first);
+ wstring s = wstring(buf);
+ addEntry = tr1::regex_search(s, regCheck);
+ }
+ }
+ }
+ catch (exception)
+ {
+ bUseRegex = false;
+ }
+ if (bNegateFilter)
+ addEntry = !addEntry;
+ }
+ if (!bUseRegex)
{
- s = it->second.message;
+ // search plain text
+ // note: \Q...\E doesn't seem to work with tr1 - it still
+ // throws an exception if the regex in between is not a valid regex :(
+
+ wstring s = it->second->Author();
std::transform(s.begin(), s.end(), s.begin(), std::tolower);
addEntry = s.find(filterstringlower) != wstring::npos;
+
if (!addEntry)
{
- _stprintf_s(buf, 1024, _T("%ld"), it->first);
- s = buf;
- addEntry = s.find(filterstringlower) != wstring::npos;
+ s = it->second->Message();
+ std::transform(s.begin(), s.end(), s.begin(), std::tolower);
+ addEntry = s.find(filterstringlower) != wstring::npos;
+ if (!addEntry)
+ {
+ _stprintf_s(buf, 1024, _T("%ld"), it->first);
+ s = buf;
+ addEntry = s.find(filterstringlower) != wstring::npos;
+ }
}
+ if (bNegateFilter)
+ addEntry = !addEntry;
}
- if (bNegateFilter)
- addEntry = !addEntry;
- }
- }
+ }
- if (!addEntry)
- continue;
+ if (!addEntry)
+ continue;
- item.mask = LVIF_TEXT|LVIF_PARAM;
- item.iItem = 0;
- item.lParam = (LPARAM)&it->second;
- _stprintf_s(buf, 1024, _T("%ld"), it->first);
- item.pszText = buf;
- ListView_InsertItem(m_hListControl, &item);
- if (it->second.date)
- _tcscpy_s(buf, 1024, CAppUtils::ConvertDate(it->second.date).c_str());
- else
- _tcscpy_s(buf, 1024, _T("(no date)"));
- ListView_SetItemText(m_hListControl, 0, 1, buf);
- if (it->second.author.size())
- _tcscpy_s(buf, 1024, it->second.author.c_str());
- else
- _tcscpy_s(buf, 1024, _T("(no author)"));
- ListView_SetItemText(m_hListControl, 0, 2, buf);
- wstring msg = it->second.message;
- std::remove(msg.begin(), msg.end(), '\r');
- std::replace(msg.begin(), msg.end(), '\n', ' ');
- std::replace(msg.begin(), msg.end(), '\t', ' ');
- _tcsncpy_s(buf, 1024, msg.c_str(), 1023);
- ListView_SetItemText(m_hListControl, 0, 3, buf);
+ item.mask = LVIF_TEXT|LVIF_PARAM;
+ item.iItem = 0;
+ item.lParam = (LPARAM)it->second;
+ _stprintf_s(buf, 1024, _T("%s"), it->first.c_str());
+ item.pszText = buf;
+ ListView_InsertItem(m_hListControl, &item);
+ if (it->second->Date().size())
+ _tcscpy_s(buf, 1024, it->second->Date().c_str());
+ else
+ _tcscpy_s(buf, 1024, _T("(no date)"));
+ ListView_SetItemText(m_hListControl, 0, 1, buf);
+ if (it->second->Author().size())
+ _tcscpy_s(buf, 1024, it->second->Author().c_str());
+ else
+ _tcscpy_s(buf, 1024, _T("(no author)"));
+ ListView_SetItemText(m_hListControl, 0, 2, buf);
+ wstring msg = it->second->Message();
+ std::remove(msg.begin(), msg.end(), '\r');
+ std::replace(msg.begin(), msg.end(), '\n', ' ');
+ std::replace(msg.begin(), msg.end(), '\t', ' ');
+ _tcsncpy_s(buf, 1024, msg.c_str(), 1023);
+ ListView_SetItemText(m_hListControl, 0, 3, buf);
- if ((iLastUnread < 0)&&(!it->second.read))
- {
- iLastUnread = 0;
+ if ((iLastUnread < 0)&&(!it->second->read))
+ {
+ iLastUnread = 0;
+ }
+ if (iLastUnread >= 0)
+ iLastUnread++;
}
- if (iLastUnread >= 0)
- iLastUnread++;
+ gReviewBoard.ReleaseReadOnlyData();
}
ListView_SetSelectionMark(m_hListControl, selMark);
m_bBlockListCtrlUI = false;
@@ -2110,6 +2485,7 @@
bChanged = true;
it->second.read = true;
}
+ gReviewBoard.ReviewsSetRead(info->url);
// refresh the name of the tree item to indicate the new
// number of unread log messages
WCHAR * str = new WCHAR[info->name.size()+10];
@@ -2208,124 +2584,246 @@
return;
if ((lpNMListView->uOldState ^ lpNMListView->uNewState) & LVIS_SELECTED)
{
- const map * pRead = m_pURLInfos->GetReadOnlyData();
LVITEM item = {0};
item.mask = LVIF_PARAM;
item.iItem = lpNMListView->iItem;
ListView_GetItem(m_hListControl, &item);
- SCCSLogEntry * pLogEntry = (SCCSLogEntry*)item.lParam;
- if (pLogEntry)
+ if(m_commitsradio)
{
- HTREEITEM hSelectedItem = TreeView_GetSelection(m_hTreeControl);
- // get the url this entry refers to
- TVITEMEX itemex = {0};
- itemex.hItem = hSelectedItem;
- itemex.mask = TVIF_PARAM;
- TreeView_GetItem(m_hTreeControl, &itemex);
- if (itemex.lParam == 0)
+ const map * pRead = m_pURLInfos->GetReadOnlyData();
+ SCCSLogEntry * pLogEntry = (SCCSLogEntry*)item.lParam;
+ if (pLogEntry)
{
- m_pURLInfos->ReleaseReadOnlyData();
- return;
- }
- // set the entry as read
- if ((!pLogEntry->read)&&(lpNMListView->uNewState & LVIS_SELECTED))
- {
- pLogEntry->read = true;
- // refresh the name of the tree item to indicate the new
- // number of unread log messages
- // e.g. instead of 'TortoiseSVN (3)', show now 'TortoiseSVN (2)'
- if (pRead->find(*(wstring*)itemex.lParam) != pRead->end())
+ HTREEITEM hSelectedItem = TreeView_GetSelection(m_hTreeControl);
+ // get the url this entry refers to
+ TVITEMEX itemex = {0};
+ itemex.hItem = hSelectedItem;
+ itemex.mask = TVIF_PARAM;
+ TreeView_GetItem(m_hTreeControl, &itemex);
+ if (itemex.lParam == 0)
{
- const CUrlInfo * uinfo = &pRead->find(*(wstring*)itemex.lParam)->second;
- // count the number of unread messages
- int unread = 0;
- for (map::const_iterator it = uinfo->logentries.begin(); it != uinfo->logentries.end(); ++it)
+ m_pURLInfos->ReleaseReadOnlyData();
+ return;
+ }
+ // set the entry as read
+ if ((!pLogEntry->read)&&(lpNMListView->uNewState & LVIS_SELECTED))
+ {
+ pLogEntry->read = true;
+ // refresh the name of the tree item to indicate the new
+ // number of unread log messages
+ // e.g. instead of 'TortoiseSVN (3)', show now 'TortoiseSVN (2)'
+ if (pRead->find(*(wstring*)itemex.lParam) != pRead->end())
{
- if (!it->second.read)
- unread++;
+ const CUrlInfo * uinfo = &pRead->find(*(wstring*)itemex.lParam)->second;
+ // count the number of unread messages
+ int unread = 0;
+ for (map::const_iterator it = uinfo->logentries.begin(); it != uinfo->logentries.end(); ++it)
+ {
+ if (!it->second.read)
+ unread++;
+ }
+ WCHAR * str = new WCHAR[uinfo->name.size()+30];
+ int unreadreviews = gReviewBoard.GetUnreadReviewsForUrl(uinfo->url);
+ if (unread || unreadreviews)
+ {
+ _stprintf_s(str, uinfo->name.size()+30, _T("%s (%d + %d)"), uinfo->name.c_str(), unread, unreadreviews);
+ itemex.state = TVIS_BOLD;
+ itemex.stateMask = TVIS_BOLD;
+ itemex.iImage = 3;
+ itemex.iSelectedImage = 3;
+ }
+ else
+ {
+ _stprintf_s(str, uinfo->name.size()+30, _T("%s"), uinfo->name.c_str());
+ itemex.state = 0;
+ itemex.stateMask = TVIS_BOLD;
+ itemex.iImage = 2;
+ itemex.iSelectedImage = 2;
+ }
+
+ itemex.pszText = str;
+ itemex.mask = TVIF_TEXT|TVIF_STATE|TVIF_IMAGE|TVIF_SELECTEDIMAGE;
+ TreeView_SetItem(m_hTreeControl, &itemex);
}
- WCHAR * str = new WCHAR[uinfo->name.size()+10];
- if (unread)
+ // the icon in the system tray needs to be changed back
+ // to 'normal'
+ ::SendMessage(m_hParent, COMMITMONITOR_CHANGEDINFO, (WPARAM)false, (LPARAM)0);
+ }
+ TCHAR buf[1024];
+ wstring msg;
+ if (ListView_GetSelectedCount(m_hListControl) > 1)
+ {
+ msg = _T("multiple log entries selected. Info for the last selected one:\n-------------------------------\n\n");
+ }
+ msg += pLogEntry->message.c_str();
+ msg += _T("\n\n-------------------------------\n");
+ // now add all changed paths, one path per line
+ for (map::const_iterator it = pLogEntry->m_changedPaths.begin(); it != pLogEntry->m_changedPaths.end(); ++it)
+ {
+ // action
+ msg += it->second.action;
+ bool mods = false;
+ if ((it->second.text_modified == svn_tristate_true)||(it->second.props_modified == svn_tristate_true))
{
- _stprintf_s(str, uinfo->name.size()+10, _T("%s (%d)"), uinfo->name.c_str(), unread);
- itemex.state = TVIS_BOLD;
- itemex.stateMask = TVIS_BOLD;
- itemex.iImage = 3;
- itemex.iSelectedImage = 3;
+ mods = true;
}
- else
+ if (mods)
+ msg += L"(";
+ if (it->second.text_modified == svn_tristate_true)
+ msg += L"T";
+ else if (mods)
+ msg += L" ";
+ if (it->second.props_modified == svn_tristate_true)
+ msg += L"P";
+ else if (mods)
+ msg += L" ";
+ if (mods)
+ msg += L")";
+ msg += _T(" : ");
+ msg += it->first;
+ msg += _T(" ");
+ if (!it->second.copyfrom_path.empty())
{
- _stprintf_s(str, uinfo->name.size()+10, _T("%s"), uinfo->name.c_str());
- itemex.state = 0;
- itemex.stateMask = TVIS_BOLD;
- itemex.iImage = 2;
- itemex.iSelectedImage = 2;
+ msg += _T("(copied from: ");
+ msg += it->second.copyfrom_path;
+ msg += _T(", revision ");
+ _stprintf_s(buf, 1024, _T("%ld)\n"), it->second.copyfrom_revision);
+ msg += wstring(buf);
}
+ else
+ msg += _T("\n");
+ }
- itemex.pszText = str;
- itemex.mask = TVIF_TEXT|TVIF_STATE|TVIF_IMAGE|TVIF_SELECTEDIMAGE;
- TreeView_SetItem(m_hTreeControl, &itemex);
- }
- // the icon in the system tray needs to be changed back
- // to 'normal'
- ::SendMessage(m_hParent, COMMITMONITOR_CHANGEDINFO, (WPARAM)false, (LPARAM)0);
+ CAppUtils::SearchReplace(msg, _T("\n"), _T("\r\n"));
+ SetWindowText(m_hLogMsgControl, msg.c_str());
+
+ // find the diff name
+ _stprintf_s(buf, 1024, _T("%s_%ld.diff"), pRead->find(*(wstring*)itemex.lParam)->second.name.c_str(), pLogEntry->revision);
+ wstring diffFileName = CAppUtils::GetDataDir();
+ diffFileName += _T("\\");
+ diffFileName += wstring(buf);
+ SendMessage(m_hwndToolbar, TB_ENABLEBUTTON, ID_MAIN_SHOWDIFFCHOOSE, MAKELONG(true, 0));
}
- TCHAR buf[1024];
- wstring msg;
- if (ListView_GetSelectedCount(m_hListControl) > 1)
+ m_pURLInfos->ReleaseReadOnlyData();
+ }
+ else
+ {
+ ReviewBoardReview * pLogEntry = (ReviewBoardReview*)item.lParam; // conversion loses qualifiers, cast to non-const
+ const map * pRead = m_pURLInfos->GetReadOnlyData();
+ if (pLogEntry)
{
- msg = _T("multiple log entries selected. Info for the last selected one:\n-------------------------------\n\n");
- }
- msg += pLogEntry->message.c_str();
- msg += _T("\n\n-------------------------------\n");
- // now add all changed paths, one path per line
- for (map::const_iterator it = pLogEntry->m_changedPaths.begin(); it != pLogEntry->m_changedPaths.end(); ++it)
- {
- // action
- msg += it->second.action;
- bool mods = false;
- if ((it->second.text_modified == svn_tristate_true)||(it->second.props_modified == svn_tristate_true))
+ HTREEITEM hSelectedItem = TreeView_GetSelection(m_hTreeControl);
+ // get the url this entry refers to
+ TVITEMEX itemex = {0};
+ itemex.hItem = hSelectedItem;
+ itemex.mask = TVIF_PARAM;
+ TreeView_GetItem(m_hTreeControl, &itemex);
+ if (itemex.lParam == 0)
{
- mods = true;
+ m_pURLInfos->ReleaseReadOnlyData();
+ return;
}
- if (mods)
- msg += L"(";
- if (it->second.text_modified == svn_tristate_true)
- msg += L"T";
- else if (mods)
- msg += L" ";
- if (it->second.props_modified == svn_tristate_true)
- msg += L"P";
- else if (mods)
- msg += L" ";
- if (mods)
- msg += L")";
- msg += _T(" : ");
- msg += it->first;
- msg += _T(" ");
- if (!it->second.copyfrom_path.empty())
+ // set the entry as read
+ if ((!pLogEntry->read)&&(lpNMListView->uNewState & LVIS_SELECTED))
{
- msg += _T("(copied from: ");
- msg += it->second.copyfrom_path;
- msg += _T(", revision ");
- _stprintf_s(buf, 1024, _T("%ld)\n"), it->second.copyfrom_revision);
- msg += wstring(buf);
+ gReviewBoard.ReviewSetRead(pLogEntry->reviewid);
+ // refresh the name of the tree item to indicate the new
+ // number of unread log messages
+ // e.g. instead of 'TortoiseSVN (3)', show now 'TortoiseSVN (2)'
+ if (pRead->find(*(wstring*)itemex.lParam) != pRead->end())
+ {
+ const CUrlInfo * uinfo = &pRead->find(*(wstring*)itemex.lParam)->second;
+ // count the number of unread messages
+ int unread = 0;
+ for (map::const_iterator it = uinfo->logentries.begin(); it != uinfo->logentries.end(); ++it)
+ {
+ if (!it->second.read)
+ unread++;
+ }
+ WCHAR * str = new WCHAR[uinfo->name.size()+30];
+ int unreadreviews = gReviewBoard.GetUnreadReviewsForUrl(uinfo->url);
+ if (unread || unreadreviews)
+ {
+ _stprintf_s(str, uinfo->name.size()+30, _T("%s (%d + %d)"), uinfo->name.c_str(), unread, unreadreviews);
+ itemex.state = TVIS_BOLD;
+ itemex.stateMask = TVIS_BOLD;
+ itemex.iImage = 3;
+ itemex.iSelectedImage = 3;
+ }
+ else
+ {
+ _stprintf_s(str, uinfo->name.size()+30, _T("%s"), uinfo->name.c_str());
+ itemex.state = 0;
+ itemex.stateMask = TVIS_BOLD;
+ itemex.iImage = 2;
+ itemex.iSelectedImage = 2;
+ }
+
+ itemex.pszText = str;
+ itemex.mask = TVIF_TEXT|TVIF_STATE|TVIF_IMAGE|TVIF_SELECTEDIMAGE;
+ TreeView_SetItem(m_hTreeControl, &itemex);
+ }
+ // the icon in the system tray needs to be changed back
+ // to 'normal'
+ ::SendMessage(m_hParent, COMMITMONITOR_CHANGEDINFO, (WPARAM)false, (LPARAM)0);
}
- else
- msg += _T("\n");
+ TCHAR buf[1024];
+ wstring msg;
+ if (ListView_GetSelectedCount(m_hListControl) > 1)
+ {
+ msg = _T("multiple log entries selected. Info for the last selected one:\n-------------------------------\n\n");
+ }
+ msg += pLogEntry->Message().c_str();
+ msg += _T("\n\n-------------------------------\n");
+/* // now add all changed paths, one path per line
+ for (map::const_iterator it = pLogEntry->m_changedPaths.begin(); it != pLogEntry->m_changedPaths.end(); ++it)
+ {
+ // action
+ msg += it->second.action;
+ bool mods = false;
+ if ((it->second.text_modified == svn_tristate_true)||(it->second.props_modified == svn_tristate_true))
+ {
+ mods = true;
+ }
+ if (mods)
+ msg += L"(";
+ if (it->second.text_modified == svn_tristate_true)
+ msg += L"T";
+ else if (mods)
+ msg += L" ";
+ if (it->second.props_modified == svn_tristate_true)
+ msg += L"P";
+ else if (mods)
+ msg += L" ";
+ if (mods)
+ msg += L")";
+ msg += _T(" : ");
+ msg += it->first;
+ msg += _T(" ");
+ if (!it->second.copyfrom_path.empty())
+ {
+ msg += _T("(copied from: ");
+ msg += it->second.copyfrom_path;
+ msg += _T(", revision ");
+ _stprintf_s(buf, 1024, _T("%ld)\n"), it->second.copyfrom_revision);
+ msg += wstring(buf);
+ }
+ else
+ msg += _T("\n");
+ }
+ */
+ CAppUtils::SearchReplace(msg, _T("\n"), _T("\r\n"));
+ SetWindowText(m_hLogMsgControl, msg.c_str());
+
+ // find the diff name
+ _stprintf_s(buf, 1024, _T("%s_review_%s.diff"), pRead->find(*(wstring*)itemex.lParam)->second.name.c_str(), pLogEntry->Reviewid().c_str());
+ wstring diffFileName = CAppUtils::GetDataDir();
+ diffFileName += _T("\\");
+ diffFileName += wstring(buf);
+ SendMessage(m_hwndToolbar, TB_ENABLEBUTTON, ID_MAIN_SHOWDIFFCHOOSE, MAKELONG(true, 0));
}
-
- CAppUtils::SearchReplace(msg, _T("\n"), _T("\r\n"));
- SetWindowText(m_hLogMsgControl, msg.c_str());
-
- // find the diff name
- _stprintf_s(buf, 1024, _T("%s_%ld.diff"), pRead->find(*(wstring*)itemex.lParam)->second.name.c_str(), pLogEntry->revision);
- wstring diffFileName = CAppUtils::GetDataDir();
- diffFileName += _T("\\");
- diffFileName += wstring(buf);
- SendMessage(m_hwndToolbar, TB_ENABLEBUTTON, ID_MAIN_SHOWDIFFCHOOSE, MAKELONG(true, 0));
+ m_pURLInfos->ReleaseReadOnlyData();
}
- m_pURLInfos->ReleaseReadOnlyData();
}
}
@@ -2371,14 +2869,29 @@
break;
case CDDS_ITEMPREPAINT:
{
- SCCSLogEntry * pLogEntry = (SCCSLogEntry*)lpNMCustomDraw->nmcd.lItemlParam;
+ if(m_commitsradio)
+ {
+ SCCSLogEntry * pLogEntry = (SCCSLogEntry*)lpNMCustomDraw->nmcd.lItemlParam;
- if (!pLogEntry->read)
+ if (!pLogEntry->read)
+ {
+ SelectObject(lpNMCustomDraw->nmcd.hdc, m_boldFont);
+ // We changed the font, so we're returning CDRF_NEWFONT. This
+ // tells the control to recalculate the extent of the text.
+ result = CDRF_NEWFONT;
+ }
+ }
+ else
{
- SelectObject(lpNMCustomDraw->nmcd.hdc, m_boldFont);
- // We changed the font, so we're returning CDRF_NEWFONT. This
- // tells the control to recalculate the extent of the text.
- result = CDRF_NEWFONT;
+ ReviewBoardReview * pLogEntry = (ReviewBoardReview*)lpNMCustomDraw->nmcd.lItemlParam;
+
+ if (!pLogEntry->read)
+ {
+ SelectObject(lpNMCustomDraw->nmcd.hdc, m_boldFont);
+ // We changed the font, so we're returning CDRF_NEWFONT. This
+ // tells the control to recalculate the extent of the text.
+ result = CDRF_NEWFONT;
+ }
}
}
break;
@@ -2504,18 +3017,40 @@
ListView_GetItem(m_hListControl, &item);
if (item.state & LVIS_SELECTED)
{
- SCCSLogEntry * pLogEntry = (SCCSLogEntry*)item.lParam;
- // find the diff name
- _stprintf_s(buf, 4096, _T("%s_%ld.diff"), pWrite->find(*(wstring*)itemex.lParam)->second.name.c_str(), pLogEntry->revision);
- wstring diffFileName = CAppUtils::GetDataDir();
- diffFileName += _T("\\");
- diffFileName += wstring(buf);
- DeleteFile(diffFileName.c_str());
+ if(m_commitsradio)
+ {
+ SCCSLogEntry * pLogEntry = (SCCSLogEntry*)item.lParam;
+ // find the diff name
+ _stprintf_s(buf, 4096, _T("%s_%ld.diff"), pWrite->find(*(wstring*)itemex.lParam)->second.name.c_str(), pLogEntry->revision);
+ wstring diffFileName = CAppUtils::GetDataDir();
+ diffFileName += _T("\\");
+ diffFileName += wstring(buf);
+ DeleteFile(diffFileName.c_str());
- pWrite->find((*(wstring*)itemex.lParam))->second.logentries.erase(pLogEntry->revision);
- ListView_DeleteItem(m_hListControl, i);
- if (nFirstDeleted < 0)
- nFirstDeleted = i;
+ pWrite->find((*(wstring*)itemex.lParam))->second.logentries.erase(pLogEntry->revision);
+ ListView_DeleteItem(m_hListControl, i);
+ if (nFirstDeleted < 0)
+ nFirstDeleted = i;
+ }
+ else
+ {
+ ReviewBoardReview * pLogEntry = (ReviewBoardReview*)item.lParam;
+ if(pLogEntry)
+ {
+ // find the diff name
+ _stprintf_s(buf, 1024, _T("%s_review_%s.diff"), pWrite->find(*(wstring*)itemex.lParam)->second.name.c_str(), pLogEntry->Reviewid().c_str());
+ wstring diffFileName = CAppUtils::GetDataDir();
+ diffFileName += _T("\\");
+ diffFileName += wstring(buf);
+ DeleteFile(diffFileName.c_str());
+
+ gReviewBoard.DeleteReview(pLogEntry->reviewid);
+ pLogEntry = 0;
+ ListView_DeleteItem(m_hListControl, i);
+ if (nFirstDeleted < 0)
+ nFirstDeleted = i;
+ }
+ }
}
else
++i;
@@ -2561,7 +3096,9 @@
HDWP hdwp = BeginDeferWindowPos(9);
hdwp = DeferWindowPos(hdwp, m_hwndToolbar, *this, 0, 0, width, m_topmarg, SWP_NOZORDER|SWP_NOACTIVATE);
hdwp = DeferWindowPos(hdwp, hFilterLabel, *this, m_xSliderPos+4, m_topmarg+5, FILTERLABELWIDTH, 12, SWP_NOZORDER|SWP_NOACTIVATE|SWP_FRAMECHANGED);
- 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);
+ 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);
+ hdwp = DeferWindowPos(hdwp, m_hCommitsRadio, *this, width - m_radiossize + 4, m_topmarg+1, 80, FILTERBOXHEIGHT-1, SWP_NOZORDER|SWP_NOACTIVATE);
+ hdwp = DeferWindowPos(hdwp, m_hReviewsRadio, *this, width - m_radiossize + 84, m_topmarg+1, 80, FILTERBOXHEIGHT-1, SWP_NOZORDER|SWP_NOACTIVATE);
hdwp = DeferWindowPos(hdwp, m_hTreeControl, *this, 0, m_topmarg, m_xSliderPos, height-m_topmarg-m_bottommarg+FILTERBOXHEIGHT+4, SWP_NOZORDER|SWP_NOACTIVATE);
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);
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);
@@ -2872,9 +3409,13 @@
loglist.left, treelist.top+5, 0, 0, SWP_NOACTIVATE|SWP_NOOWNERZORDER|SWP_NOZORDER|SWP_NOSIZE);
hdwp = DeferWindowPos(hdwp, m_hFilterControl, NULL,
- loglist.left+FILTERLABELWIDTH, treelist.top, loglist.right-FILTERLABELWIDTH, FILTERBOXHEIGHT,
+ loglist.left+FILTERLABELWIDTH, treelist.top, loglist.right-loglist.left-FILTERLABELWIDTH - 160 - 4, FILTERBOXHEIGHT,
SWP_NOACTIVATE|SWP_NOOWNERZORDER|SWP_NOZORDER);
+ hdwp = DeferWindowPos(hdwp, m_hCommitsRadio, NULL, loglist.right - m_radiossize + 4, treelist.top + 1, 80, FILTERBOXHEIGHT-1, SWP_NOACTIVATE|SWP_NOOWNERZORDER|SWP_NOZORDER);
+ hdwp = DeferWindowPos(hdwp, m_hReviewsRadio, NULL, loglist.right - m_radiossize + 84, treelist.top + 1, 80, FILTERBOXHEIGHT-1, SWP_NOACTIVATE|SWP_NOOWNERZORDER|SWP_NOZORDER);
+
+
hdwp = DeferWindowPos(hdwp, m_hListControl, NULL,
loglist.left, treelist.top+FILTERBOXHEIGHT, loglist.right-loglist.left, loglist.bottom-treelist.top-FILTERBOXHEIGHT,
SWP_NOACTIVATE|SWP_NOOWNERZORDER|SWP_NOZORDER);
Index: src/MainDlg.h
===================================================================
--- src/MainDlg.h (revision 605)
+++ src/MainDlg.h (working copy)
@@ -97,6 +97,8 @@
HWND m_hListControl;
HWND m_hLogMsgControl;
HWND m_hFilterControl;
+ HWND m_hCommitsRadio;
+ HWND m_hReviewsRadio;
HFONT m_font;
CListCtrl m_ListCtrl;
@@ -112,6 +114,8 @@
LONG m_xSliderPos;
LONG m_ySliderPos;
LONG m_bottommarg;
+ LONG m_radiossize;
+ bool m_commitsradio;
HFONT m_boldFont;
Index: src/Resources/CommitMonitor.rc
===================================================================
--- src/Resources/CommitMonitor.rc (revision 605)
+++ src/Resources/CommitMonitor.rc (working copy)
@@ -21,7 +21,7 @@
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_NEU)
LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
-#pragma code_page(1252)
+#pragma code_page(1250)
/////////////////////////////////////////////////////////////////////////////
//
@@ -103,8 +103,10 @@
LTEXT "",IDC_INFOLABEL,0,222,303,16
PUSHBUTTON "&Exit",IDC_EXIT,359,222,50,14
DEFPUSHBUTTON "&Hide",IDOK,304,222,50,14
- EDITTEXT IDC_FILTERSTRING,186,32,225,14,ES_AUTOHSCROLL
+ EDITTEXT IDC_FILTERSTRING,186,32,141,14,ES_AUTOHSCROLL
LTEXT "Filter:",IDC_FILTERLABEL,158,34,26,8
+ CONTROL "Commits",IDC_RADIO2,"Button",BS_AUTORADIOBUTTON,331,35,40,10
+ CONTROL "Reviews",IDC_RADIO3,"Button",BS_AUTORADIOBUTTON,371,35,40,10
END
IDD_URLCONFIG DIALOGEX 0, 0, 336, 322
@@ -144,6 +146,7 @@
COMBOBOX IDC_SCCSCOMBO,89,23,110,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
LTEXT "Accurev Repository:",IDC_REPOLABEL,14,70,66,8
EDITTEXT IDC_ACCUREVREPO,89,67,233,14,ES_AUTOHSCROLL
+ CONTROL "Check review requests",IDC_CHECKREVIEWS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,223,91,89,10
END
IDD_OPTIONS DIALOGEX 0, 0, 308, 293
@@ -270,6 +273,10 @@
BOTTOMMARGIN, 286
END
+ IDD_FINDBAR, DIALOG
+ BEGIN
+ END
+
IDD_NEWERNOTIFYDLG, DIALOG
BEGIN
LEFTMARGIN, 7
@@ -365,7 +372,7 @@
POPUP "Popup"
BEGIN
MENUITEM "&Show Unified Diff", ID_MAIN_SHOWDIFF
- MENUITEM "&Open WebViewer", ID_POPUP_OPENWEBVIEWER, INACTIVE
+ MENUITEM "&Open WebViewer", ID_POPUP_OPENWEBVIEWER
MENUITEM "&Remove", ID_MAIN_REMOVE
MENUITEM "&Mark as unread", ID_POPUP_MARKASUNREAD
MENUITEM "&Copy to clipboard", ID_MAIN_COPY
Index: src/Resources/resource.h
===================================================================
--- src/Resources/resource.h (revision 605)
+++ src/Resources/resource.h (working copy)
@@ -104,6 +104,7 @@
#define IDC_NUMLOGS 1040
#define IDC_CHECK2 1041
#define IDC_NOTIFYCONNECTERROR 1041
+#define IDC_CHECKREVIEWS 1041
#define IDC_FILTERSTRING 1042
#define IDC_FILTERLABEL 1043
#define IDC_MAXLOGENTRIES 1044
@@ -130,6 +131,8 @@
#define IDC_ACCUDIFFCMDLABEL 1064
#define IDC_SCCSCOMBO 1065
#define IDC_EDIT2 1069
+#define IDC_RADIO2 1072
+#define IDC_RADIO3 1074
#define ID_FILE_OPENCOMMITMONITOR 32771
#define ID_MAIN_OPENCOMMITMONITOR 32772
#define ID_POPUP_OPENCOMMITMONITOR 32773
@@ -181,7 +184,7 @@
#define _APS_NO_MFC 1
#define _APS_NEXT_RESOURCE_VALUE 165
#define _APS_NEXT_COMMAND_VALUE 32836
-#define _APS_NEXT_CONTROL_VALUE 1070
+#define _APS_NEXT_CONTROL_VALUE 1073
#define _APS_NEXT_SYMED_VALUE 110
#endif
#endif
Index: src/ReviewBoardXml.cpp
===================================================================
--- src/ReviewBoardXml.cpp (revision 0)
+++ src/ReviewBoardXml.cpp (revision 0)
@@ -0,0 +1,737 @@
+#include "stdafx.h"
+
+#include "ReviewBoardXml.h"
+#include "rapidxml.hpp"
+#include
+#include
+#include "timesupport.h"
+#include "Http.h"
+
+#include "UnicodeUtils.h"
+#include "AppUtils.h"
+
+using namespace rapidxml;
+using namespace std;
+
+#define GET_REVIEWS_BASE "/api/review-requests/"
+#define GET_REVIEWS "/api/review-requests/?status=all&max-results=30"
+#define GET_REVIEWS_FROM "&time-added-from="
+#define GET_REVIEW_ID "/api/review-requests/"
+#define GET_UNIDIFF_1 "/api/review-requests/"
+#define GET_UNIDIFF_2 "/diffs/1/"
+#define GET_FILES_1 "/api/review-requests/"
+#define GET_FILES_2 "/diffs/1/files/"
+
+
+
+time_t ReviewBoard::Now()
+{
+ return time(NULL);
+}
+
+time_t ReviewBoard::FromISO8601(const std::string& timestr)
+{
+ if(timestr == "0000-00-00T00:00:00")
+ return (time_t)0;
+ struct tm tm;
+ if(NULL == strptime(timestr.c_str(),"%Y-%m-%dT%H:%M:%S",&tm))
+ return (time_t)0;
+ return mktime(&tm);
+}
+
+std::string ReviewBoard::ToISO8601(time_t tim)
+{
+ if(tim == 0)
+ return string("0000-00-00T00:00:00");
+ char buf[128];
+ memset(buf,0,128);
+ strftime(buf,128,"%Y-%m-%dT%H:%M:%S",localtime(&tim));
+ return string(buf);
+}
+
+ReviewBoardXmlNode::~ReviewBoardXmlNode()
+{
+ while(!children.empty())
+ {
+ delete children.back();
+ children.pop_back();
+ }
+}
+
+void ReviewBoardXmlNode::Load(void* rapidnode, ReviewBoardXmlNode * paren)
+{
+ parent = paren;
+
+ xml_node<> *rnode = (xml_node<>*)rapidnode;
+
+ name.insert(0,rnode->name(),rnode->name_size());
+/*
+ the xml output from the reviewboard doesn't contain attributes
+ for (xml_attribute<> *attr = rnode->first_attribute();
+ attr; attr = attr->next_attribute())
+ {
+ }
+*/
+ for(rnode = rnode->first_node();rnode;rnode = rnode->next_sibling())
+ {
+ if(rnode->value_size() != 0)
+ {
+ string name, val;
+ name.insert(0,rnode->name(),rnode->name_size());
+ val.insert(0,rnode->value(),rnode->value_size());
+ attributes[name] = val;
+ }
+ else
+ {
+ ReviewBoardXmlNode * child = new ReviewBoardXmlNode();
+ child->Load(rnode,this);
+ children.push_back(child);
+ }
+ }
+}
+
+ReviewBoardXmlNode const * ReviewBoardXmlNode::GetNodeWithAttributeAndValue(const std::string& attr, const std::string& val) const
+{
+ map::const_iterator itr = attributes.find(attr);
+ if(itr != attributes.end() && itr->second == val)
+ return this;
+
+ for(vector::const_iterator it = children.begin(); it != children.end(); ++it)
+ {
+ ReviewBoardXmlNode const * ret = (*it)->GetNodeWithAttributeAndValue(attr,val);
+ if(ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+ReviewBoardXmlNode const * ReviewBoardXmlNode::GetFirstNodeWithAttribute(const std::string& nam, const std::string& attr) const
+{
+ map::const_iterator itr = attributes.find(attr);
+ if(name == nam && itr != attributes.end())
+ return this;
+
+ for(vector::const_iterator it = children.begin(); it != children.end(); ++it)
+ {
+ ReviewBoardXmlNode const * ret = (*it)->GetFirstNodeWithAttribute(nam, attr);
+ if(ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+void ReviewBoardXmlNode::GetNodesVectorWithAttribute(const std::string& attr, std::vector& nodes) const
+{
+ if(attributes.find(attr) != attributes.end())
+ nodes.push_back(this);
+ for(vector::const_iterator it = children.begin(); it != children.end(); ++it)
+ (*it)->GetNodesVectorWithAttribute(attr,nodes);
+}
+
+
+void ReviewBoardXmlDocument::Cleanup()
+{
+ if(root)
+ delete root;
+ root = 0;
+}
+
+
+ReviewBoardXmlDocument::~ReviewBoardXmlDocument()
+{
+ Cleanup();
+}
+
+void ReviewBoardXmlDocument::LoadBuf(const string& buffer)
+{
+ char * buf;
+ int len;
+
+ len = buffer.length();
+ buf = new char[len+1];
+ memcpy(buf,buffer.c_str(),len);
+ buf[len] = 0;
+
+ xml_document<> doc; // character type defaults to char
+ doc.parse<0>(buf); // 0 means default parse flags
+
+ xml_node<> * rapidnode = doc.first_node();
+
+ root = new ReviewBoardXmlNode();
+
+ if(rapidnode)
+ root->Load(rapidnode,NULL);
+
+ delete[] buf;
+
+ value.write(buffer.c_str(),buffer.length());
+}
+
+void ReviewBoardXmlDocument::Load(const string& path)
+{
+ Cleanup();
+
+ stringstream sstrm;
+ gHTTP.GetPageXml(path,sstrm);
+
+ LoadBuf(sstrm.str());
+}
+
+void ReviewBoardTextDocument::Load(const string& path)
+{
+ gHTTP.GetPageText(path,value);
+}
+
+void ReviewBoardReviewUnifiedDiff::Open(const char * review)
+{
+ string path = GET_UNIDIFF_1;
+ path += review;
+ path += GET_UNIDIFF_2;
+ Load(path);
+}
+
+void ReviewBoardAllReviews::Open(const char * from_time)
+{
+ string path = GET_REVIEWS;
+ if(from_time)
+ {
+ path += GET_REVIEWS_FROM;
+ path += from_time;
+ }
+ Load(path);
+}
+
+void ReviewBoardReview::Open(const char * id)
+{
+ string path = GET_REVIEW_ID;
+ path += id;
+ path += "/";
+ Load(path);
+}
+
+void ReviewBoardReviewFiles::Open(const char * id)
+{
+ string path = GET_FILES_1;
+ path += id;
+ path += GET_FILES_2;
+ Load(path);
+}
+
+ReviewBoardXmlNode const * ReviewBoardXmlDocument::GetNodeWithAttributeAndValue(const std::string& attr, const std::string& val) const
+{
+ if(root)
+ return root->GetNodeWithAttributeAndValue(attr,val);
+ return 0;
+}
+
+void ReviewBoardXmlDocument::GetNodesVectorWithAttribute(const std::string& attr, std::vector& nodes) const
+{
+ if(root)
+ return root->GetNodesVectorWithAttribute(attr,nodes);
+}
+
+ReviewBoardXmlNode const * ReviewBoardXmlDocument::GetFirstNodeWithAttribute(const std::string& name, const std::string& attr) const
+{
+ if(root)
+ return root->GetFirstNodeWithAttribute(name, attr);
+ return 0;
+}
+
+
+typedef vector rbxvec;
+
+void ReviewBoard::GetAllNeededData_FirstPass(const char * from_time)
+{
+ ReviewBoardAllReviews * rbd = new ReviewBoardAllReviews();
+ rbd->Open(from_time);
+ all_docs.push_back(rbd);
+
+ rbxvec vec;
+ rbd->GetNodesVectorWithAttribute("id",vec);
+ for(rbxvec::const_iterator itr = vec.begin(); itr != vec.end(); ++itr)
+ {
+ string id = (*itr)->attributes.at("id");
+ // if not cached already
+ if(reviewid_to_files.find(id) == reviewid_to_files.end())
+ {
+ ReviewBoardReviewFiles * rbf = new ReviewBoardReviewFiles();
+
+ rbf->Open(id.c_str());
+ reviewid_to_files[id] = rbf;
+
+ all_docs.push_back(rbf);
+ }
+ }
+}
+
+void ReviewBoard::GetSpecificReviewData_SecondPass(const std::vector& branches)
+{
+ for(R2Fmap::const_iterator itr = reviewid_to_files.begin(); itr != reviewid_to_files.end(); ++itr)
+ {
+ // if not cached already
+ if(reviewid_to_review.find(itr->first) == reviewid_to_review.end())
+ {
+ if(itr->second->HasMatch(branches))
+ {
+ ReviewBoardReview * rbr = new ReviewBoardReview();
+ rbr->Open(itr->first.c_str());
+ rbr->reviewid = itr->first;
+ reviewid_to_review[itr->first] = rbr;
+ all_docs.push_back(rbr);
+ }
+ }
+ }
+}
+
+bool ReviewBoardReviewFiles::HasMatch(const std::string& branch)
+{
+ rbxvec vec;
+ GetNodesVectorWithAttribute("source_file",vec);
+ for(rbxvec::const_iterator it = vec.begin(); it != vec.end(); ++it)
+ {
+ const std::string& str = (*it)->attributes.at("source_file");
+ if(str.find(branch) != string::npos)
+ return true;
+ }
+ return false;
+}
+
+
+bool ReviewBoardReviewFiles::HasMatch(const std::vector& branches)
+{
+ rbxvec vec;
+ GetNodesVectorWithAttribute("source_file",vec);
+ for(rbxvec::const_iterator it = vec.begin(); it != vec.end(); ++it)
+ {
+ const std::string& str = (*it)->attributes.at("source_file");
+ for(std::vector::const_iterator itr = branches.begin(); itr != branches.end(); ++itr)
+ {
+ if(str.find(*itr) != string::npos)
+ return true;
+ }
+ }
+ return false;
+}
+
+ReviewBoard::~ReviewBoard()
+{
+ // do not destroy while other threads are accessing the object
+ guard.AcquireWriterLock();
+ while(!all_docs.empty())
+ {
+ delete all_docs.back();
+ all_docs.pop_back();
+ }
+}
+
+vector const * ReviewBoard::GetReadOnlyData()
+{
+ guard.AcquireReaderLock();
+ return &all_docs;
+}
+
+void ReviewBoard::ReleaseReadOnlyData()
+{
+ guard.ReleaseReaderLock();
+}
+
+void ReviewBoard::Save(const std::wstring& filename)
+{
+ guard.AcquireReaderLock();
+
+ if(filename != TEXT(""))
+ savefilename = filename;
+
+ if(savefilename == TEXT(""))
+ savefilename = CAppUtils::GetDataDir() + TEXT("\\reviews");
+
+ ofstream ofile(savefilename.c_str());
+
+
+ ofile << ToISO8601(lastupdate) << endl;
+ ofile << url_to_branch.size() << endl;
+ for(U2Bmap::const_iterator itr = url_to_branch.begin(); itr != url_to_branch.end(); ++itr)
+ {
+ ofile << itr->first << endl;
+ ofile << itr->second << endl;
+ }
+
+ ofile << reviewid_to_files.size() << endl;
+
+ for(R2Fmap::const_iterator itr = reviewid_to_files.begin(); itr != reviewid_to_files.end(); ++itr)
+ {
+ ofile << itr->first << endl;
+ ofile << itr->second->value.str().length() << endl;
+ ofile.write(itr->second->value.str().c_str(),itr->second->value.str().length());
+ }
+
+ ofile << reviewid_to_review.size() << endl;
+
+ for(R2Rmap::const_iterator itr = reviewid_to_review.begin(); itr != reviewid_to_review.end(); ++itr)
+ {
+ ofile << itr->first << endl;
+ ofile << (itr->second->read ? "1" : "0") << endl;
+ ofile << itr->second->value.str().length() << endl;
+ ofile.write(itr->second->value.str().c_str(),itr->second->value.str().length());
+ }
+
+
+ ofile.close();
+
+ guard.ReleaseReaderLock();
+}
+
+void ReviewBoard::Load(const std::wstring& filename)
+{
+ guard.AcquireWriterLock();
+
+ if(filename != TEXT(""))
+ savefilename = filename;
+
+ if(savefilename == TEXT(""))
+ savefilename = CAppUtils::GetDataDir() + TEXT("\\reviews");
+
+ ifstream ifile(savefilename.c_str());
+
+
+ std::string timestr;
+ ifile >> timestr;
+ lastupdate = FromISO8601(timestr);
+ int numbranches;
+ ifile >> numbranches;
+ for(int i = 0; i < numbranches; ++i)
+ {
+ string url, branch;
+ ifile >> url;
+ ifile >> branch;
+ url_to_branch[url] = branch;
+ }
+
+ int filesnum;
+ ifile >> filesnum;
+
+ for(int i = 0; i < filesnum; ++i)
+ {
+ string review;
+ ifile >> review;
+ int len;
+ ifile >> len;
+ ifile.get();// eat eol
+ string buf(len,0);
+ int j = 0;
+ while(j < len)
+ {
+ buf[j] = ifile.get();
+ ++j;
+ }
+ ReviewBoardReviewFiles * r = new ReviewBoardReviewFiles();
+ r->LoadBuf(buf);
+ reviewid_to_files[review] = r;
+ all_docs.push_back(r);
+ }
+
+ ifile >> filesnum;
+
+ for(int i = 0; i < filesnum; ++i)
+ {
+ string review;
+ ifile >> review;
+ int read;
+ ifile >> read;
+ int len;
+ ifile >> len;
+ ifile.get();// eat eol
+ string buf(len,0);
+ int j = 0;
+ while(j < len)
+ {
+ buf[j] = ifile.get();
+ ++j;
+ }
+ ReviewBoardReview * r = new ReviewBoardReview();
+ r->read = (read == 1);
+ r->LoadBuf(buf);
+ r->reviewid = review;
+ reviewid_to_review[review] = r;
+ all_docs.push_back(r);
+ }
+
+
+ ifile.close();
+
+ guard.ReleaseWriterLock();
+}
+
+int ReviewBoard::DoUpdate()
+{
+ guard.AcquireWriterLock();
+
+ // set already dl-d reviews to old
+ for(R2Rmap::iterator itr = reviewid_to_review.begin();itr != reviewid_to_review.end(); ++itr)
+ itr->second->old = true;
+
+ int filesnum = reviewid_to_files.size();
+ if(lastupdate != 0)
+ GetAllNeededData_FirstPass(ToISO8601(lastupdate).c_str());
+ else
+ GetAllNeededData_FirstPass();
+
+ lastupdate = time(NULL);
+ filesnum -= reviewid_to_files.size();
+
+ std::vector vec;
+
+ for(U2Bmap::const_iterator itr = url_to_branch.begin(); itr != url_to_branch.end(); ++itr)
+ {
+ vec.push_back(itr->second);
+ }
+
+ int revsnum = reviewid_to_review.size();
+ GetSpecificReviewData_SecondPass(vec);
+ revsnum -= reviewid_to_review.size();
+
+ guard.ReleaseWriterLock();
+ if(filesnum < 0)
+ Save();
+
+ newreviews = -revsnum;
+
+ return newreviews;
+}
+
+void ReviewBoard::AddBranchToMonitor(const string& url, const string& branch)
+{
+ guard.AcquireWriterLock();
+ url_to_branch[url] = branch;
+ guard.ReleaseWriterLock();
+}
+
+void ReviewBoard::AddBranchToMonitor(const wstring& wurl)
+{
+ std::string url = CUnicodeUtils::StdGetUTF8(wurl);
+ std::string branch = url;
+ size_t pos = branch.find("/code/");
+ if(pos != string::npos)
+ {
+ branch = branch.substr(pos+6);
+ }
+ AddBranchToMonitor(url,branch);
+}
+
+
+void ReviewBoard::RemoveBranchToMonitor(const wstring& wurl)
+{
+ RemoveBranchToMonitor(CUnicodeUtils::StdGetUTF8(wurl));
+}
+
+
+void ReviewBoard::RemoveBranchToMonitor(const string& url)
+{
+ guard.AcquireWriterLock();
+ U2Bmap::iterator itr = url_to_branch.find(url);
+ if(itr != url_to_branch.end())
+ url_to_branch.erase(url);
+ guard.ReleaseWriterLock();
+}
+
+bool ReviewBoard::IsUrlMonitored(const std::string& url)
+{
+ guard.AcquireReaderLock();
+ U2Bmap::const_iterator itr = url_to_branch.find(url);
+ if(itr != url_to_branch.end())
+ {
+ guard.ReleaseReaderLock();
+ return true;
+ }
+ guard.ReleaseReaderLock();
+ return false;
+}
+
+bool ReviewBoard::IsUrlMonitored(const std::wstring& wurl)
+{
+ std::string url = CUnicodeUtils::StdGetUTF8(wurl);
+ return IsUrlMonitored(url);
+}
+
+typedef std::vector rbrvec;
+void ReviewBoard::GetReviewsForUrl(const std::wstring& wurl, WR2Rmap& vec)
+{
+ std::string url = CUnicodeUtils::StdGetUTF8(wurl);
+ guard.AcquireReaderLock();
+ U2Bmap::const_iterator itr = url_to_branch.find(url);
+ if(itr != url_to_branch.end())
+ {
+ for(R2Fmap::const_iterator filesitr = reviewid_to_files.begin(); filesitr != reviewid_to_files.end(); ++filesitr)
+ {
+ if(filesitr->second->HasMatch(itr->second))
+ {
+ R2Rmap::const_iterator ritr = reviewid_to_review.find(filesitr->first);
+ if(ritr != reviewid_to_review.end())
+ vec[UTF8ToWide(ritr->first)] = (const_cast(ritr->second));
+ }
+ }
+ }
+ guard.ReleaseReaderLock();
+}
+
+int ReviewBoard::GetUnreadReviews()
+{
+ guard.AcquireReaderLock();
+ int ret = 0;
+ for(R2Rmap::const_iterator ritr = reviewid_to_review.begin(); ritr != reviewid_to_review.end(); ++ritr)
+ {
+ if(!ritr->second->read)
+ ++ret;
+ }
+ guard.ReleaseReaderLock();
+ return ret;
+}
+
+int ReviewBoard::GetNewReviewsForUrl(const std::wstring& url)
+{
+ WR2Rmap v;
+ GetReviewsForUrl(url,v);
+ int n = 0;
+ for(WR2Rmap::const_iterator it = v.begin(); it != v.end(); ++it)
+ {
+ if(!it->second->old)
+ n++;
+ }
+ return n;
+}
+
+int ReviewBoard::GetUnreadReviewsForUrl(const std::wstring& url)
+{
+ WR2Rmap v;
+ GetReviewsForUrl(url,v);
+ int n = 0;
+ for(WR2Rmap::const_iterator it = v.begin(); it != v.end(); ++it)
+ {
+ if(!it->second->read)
+ n++;
+ }
+ return n;
+}
+
+void ReviewBoard::ReviewSetRead(const std::string& rev, bool b)
+{
+ guard.AcquireWriterLock();
+ R2Rmap::iterator ritr = reviewid_to_review.find(rev);
+ bool needsave = false;
+ if(ritr != reviewid_to_review.end())
+ {
+ if(ritr->second->read != b)
+ needsave = true;
+ ritr->second->read = b;
+ }
+ guard.ReleaseWriterLock();
+ if(needsave)
+ Save();
+}
+
+void ReviewBoard::DeleteReview(const std::string& rev)
+{
+ guard.AcquireWriterLock();
+ R2Rmap::iterator ritr = reviewid_to_review.find(rev);
+ bool needsave = false;
+ if(ritr != reviewid_to_review.end())
+ {
+ std::vector::iterator it2 = all_docs.begin();
+ while(it2 != all_docs.end())
+ {
+ if((*it2) == (ritr->second))
+ break;
+ it2++;
+ }
+
+ if(!ritr->second->old)
+ newreviews--;
+
+ delete (*it2);
+
+ if(it2 != all_docs.end())
+ all_docs.erase(it2);
+
+ reviewid_to_review.erase(ritr);
+ needsave = true;
+ }
+ guard.ReleaseWriterLock();
+ if(needsave)
+ Save();
+}
+
+
+void ReviewBoardTextDocument::Save(const wstring& filename)
+{
+ ofstream ofile(filename.c_str());
+ ofile.write(value.str().c_str(),value.str().length());
+ ofile.close();
+}
+
+void ReviewBoard::ReviewsSetRead(const std::wstring& wurl)
+{
+ std::string url = CUnicodeUtils::StdGetUTF8(wurl);
+ guard.AcquireWriterLock();
+ U2Bmap::const_iterator itr = url_to_branch.find(url);
+ if(itr != url_to_branch.end())
+ {
+ for(R2Fmap::const_iterator filesitr = reviewid_to_files.begin(); filesitr != reviewid_to_files.end(); ++filesitr)
+ {
+ if(filesitr->second->HasMatch(itr->second))
+ {
+ R2Rmap::iterator ritr = reviewid_to_review.find(filesitr->first);
+ if(ritr != reviewid_to_review.end())
+ ritr->second->read = true;
+ }
+ }
+ }
+ guard.ReleaseWriterLock();
+ Save();
+}
+
+wstring ReviewBoardReview::Author() const
+{
+ ReviewBoardXmlNode const * n = GetFirstNodeWithAttribute("submitter","title");
+ if(n)
+ {
+ return UTF8ToWide(n->attributes.at("title"));
+ }
+ return TEXT("no author");
+}
+
+wstring ReviewBoardReview::Date() const
+{
+ ReviewBoardXmlNode const * n = GetFirstNodeWithAttribute("review_request","time_added");
+ if(n)
+ {
+ return UTF8ToWide(n->attributes.at("time_added"));
+ }
+ return TEXT("(no date)");
+}
+
+wstring ReviewBoardReview::Url() const
+{
+ ReviewBoardXmlNode const * n = GetFirstNodeWithAttribute("self","href");
+ if(n)
+ {
+ return UTF8ToWide(n->attributes.at("href"));
+ }
+ return TEXT("");
+}
+
+wstring ReviewBoardReview::Message() const
+{
+ ReviewBoardXmlNode const * n = GetFirstNodeWithAttribute("review_request","description");
+ if(n)
+ {
+ return UTF8ToWide(n->attributes.at("description"));
+ }
+ return TEXT("(no description)");
+}
+
+wstring ReviewBoardReview::Reviewid() const
+{
+ return UTF8ToWide(reviewid);
+}
Index: src/ReviewBoardXml.h
===================================================================
--- src/ReviewBoardXml.h (revision 0)
+++ src/ReviewBoardXml.h (revision 0)
@@ -0,0 +1,167 @@
+#ifndef REVIEWBOARDXML_H
+#define REVIEWBOARDXML_H
+
+#include "stdafx.h"
+
+#include "ReaderWriterLock.h"
+#include "Singleton.h"
+
+#include
+#include
+#include
+#include