You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdesvn/src/svnfrontend/svnlogdlgimp.cpp

662 lines
20 KiB

/***************************************************************************
* Copyright (C) 2005-2007 by Rajko Albrecht *
* ral@alwins-world.de *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
***************************************************************************/
#include "svnlogdlgimp.h"
#include "tdesvnsettings.h"
#include "log_entry.h"
#include "sub2qt.h"
#include "svnactions.h"
#include "revisionbuttonimpl.h"
#include <tdelistview.h>
#include <ktextbrowser.h>
#include <kpushbutton.h>
#include <tdeglobal.h>
#include <tdelocale.h>
#include <kapp.h>
#include <tdeconfigbase.h>
#include <tdeconfig.h>
#include <ktabwidget.h>
#include <kdebug.h>
#include <tqdatetime.h>
#include <tqheader.h>
#include <tqsplitter.h>
#include <tqtextstream.h>
#include <tqpopupmenu.h>
#include <list>
const char* SvnLogDlgImp::groupName = "log_dialog_size";
class LogListViewItem:public TDEListViewItem
{
public:
LogListViewItem (TDEListView *parent,const svn::LogEntry&);
virtual int compare( TQListViewItem* i, int col, bool ascending ) const;
static const int COL_MARKER,COL_REV,COL_AUTHOR,COL_DATE,COL_MSG;
const TQString&message()const;
svn_revnum_t rev()const{return _revision;}
void showChangedEntries(TDEListView*);
unsigned int numChangedEntries(){return changedPaths.count();}
void setChangedEntries(const svn::LogEntry&);
void setRealName(const TQString&_n){_realName=_n;}
const TQString&realName()const{return _realName;}
bool copiedFrom(TQString&_n,long&rev)const;
static bool isParent(const TQString&_par,const TQString&tar);
protected:
svn_revnum_t _revision;
TQDateTime fullDate;
TQString _message,_realName;
TQValueList<svn::LogChangePathEntry> changedPaths;
};
const int LogListViewItem::COL_MARKER = 0;
const int LogListViewItem::COL_REV = 2;
const int LogListViewItem::COL_AUTHOR = 1;
const int LogListViewItem::COL_DATE = 3;
const int LogListViewItem::COL_MSG = 4;
class LogChangePathItem:public TDEListViewItem
{
public:
LogChangePathItem(TDEListView*parent,const svn::LogChangePathEntry&);
virtual ~LogChangePathItem(){}
TQChar action() const{return _action;}
const TQString& path() const{return _path;}
const TQString& source() const{return _source;}
svn_revnum_t revision() const{ return _revision;}
protected:
TQString _path,_source;
TQChar _action;
svn_revnum_t _revision;
};
LogListViewItem::LogListViewItem(TDEListView*_parent,const svn::LogEntry&_entry)
: TDEListViewItem(_parent),_realName(TQString())
{
setMultiLinesEnabled(false);
_revision=_entry.revision;
fullDate=svn::DateTime(_entry.date);
setText(COL_REV,TQString("%1").arg(_revision));
setText(COL_AUTHOR,_entry.author);
setText(COL_DATE,helpers::sub2qt::apr_time2qtString(_entry.date));
_message = _entry.message;
TQStringList sp = TQStringList::split("\n",_message);
if (sp.count()==0) {
setText(COL_MSG,_message);
} else {
setText(COL_MSG,sp[0]);
}
changedPaths = _entry.changedPaths;
//setText(COL_MSG,_entry.message.c_str());
}
const TQString&LogListViewItem::message()const
{
return _message;
}
int LogListViewItem::compare( TQListViewItem* item, int col, bool ) const
{
LogListViewItem* k = static_cast<LogListViewItem*>( item );
if (col==COL_REV) {
return _revision-k->_revision;
}
if (col==COL_DATE) {
return k->fullDate.secsTo(fullDate);
}
return text(col).localeAwareCompare(k->text(col));
}
void LogListViewItem::showChangedEntries(TDEListView*where)
{
if (!where)return;
where->clear();
if (changedPaths.count()==0) {
return;
}
for (unsigned i = 0; i < changedPaths.count();++i) {
new LogChangePathItem(where,changedPaths[i]);
}
}
LogChangePathItem::LogChangePathItem(TDEListView*parent,const svn::LogChangePathEntry&e)
:TDEListViewItem(parent)
{
_action = TQChar(e.action);
setText(0,_action);
_path = e.path;
setText(1,e.path);
_revision = e.copyFromRevision;
_source = e.copyFromPath;
if (e.copyFromRevision>-1)
{
setText(2,i18n("%1 at revision %2").arg(e.copyFromPath).arg(e.copyFromRevision));
}
}
void LogListViewItem::setChangedEntries(const svn::LogEntry&_entry)
{
changedPaths = _entry.changedPaths;
}
bool LogListViewItem::copiedFrom(TQString&_n,long&_rev)const
{
for (unsigned i = 0; i < changedPaths.count();++i) {
if (changedPaths[i].action=='A' &&
!changedPaths[i].copyFromPath.isEmpty() &&
isParent(changedPaths[i].path,_realName)) {
kdDebug()<<_realName<< " - " << changedPaths[i].path << endl;
TQString tmpPath = _realName;
TQString r = _realName.mid(changedPaths[i].path.length());
_n=changedPaths[i].copyFromPath;
_n+=r;
_rev = changedPaths[i].copyFromRevision;
kdDebug()<<"Found switch from "<< changedPaths[i].copyFromPath << " rev "<<changedPaths[i].copyFromRevision<<endl;
kdDebug()<<"Found switch from "<< _n << " rev "<<_rev<<endl;
return true;
}
}
return false;
}
bool LogListViewItem::isParent(const TQString&_par,const TQString&tar)
{
if (_par==tar) return true;
TQString par = _par+(_par.endsWith("/")?"":"/");
return tar.startsWith(par);
}
SvnLogDlgImp::SvnLogDlgImp(SvnActions*ac,TQWidget *parent, const char *name,bool modal)
:SvnLogDialogData(parent, name,modal),_name("")
{
m_LogView->setSorting(LogListViewItem::COL_REV);
m_LogView->setSortOrder(TQt::Descending);
resize(dialogSize());
m_ControlKeyDown = false;
m_first = 0;
m_second = 0;
if (Kdesvnsettings::self()->log_always_list_changed_files()) {
buttonListFiles->hide();
} else {
m_ChangedList->hide();
}
m_Actions = ac;
TDEConfigGroup cs(Kdesvnsettings::self()->config(), groupName);
TQString t1 = cs.readEntry("logsplitter",TQString());
if (!t1.isEmpty()) {
TQTextStream st2(&t1,IO_ReadOnly);
st2 >> *m_centralSplitter;
}
t1 = cs.readEntry("right_logsplitter",TQString());
if (!t1.isEmpty()) {
if (cs.readBoolEntry("laststate",false)==m_ChangedList->isHidden()) {
TQTextStream st2(&t1,IO_ReadOnly);
st2 >> *m_rightSplitter;
}
}
}
SvnLogDlgImp::~SvnLogDlgImp()
{
TQString t1,t2;
TQTextStream st1(&t1,IO_WriteOnly);
st1 << *m_rightSplitter;
TQTextStream st2(&t2,IO_WriteOnly);
st2 << *m_centralSplitter;
TDEConfigGroup cs(Kdesvnsettings::self()->config(), groupName);
cs.writeEntry("right_logsplitter",t1);
cs.writeEntry("logsplitter",t2);
cs.writeEntry("laststate",m_ChangedList->isHidden());
}
void SvnLogDlgImp::dispLog(const svn::SharedPointer<svn::LogEntriesMap>&_log,const TQString & what,const TQString&root,const svn::Revision&peg,const TQString&pegUrl)
{
m_peg = peg;
m_PegUrl = pegUrl;
m_first = m_second = 0;
m_startRevButton->setNoWorking(m_PegUrl.isUrl());
m_endRevButton->setNoWorking(m_PegUrl.isUrl());
if (!m_PegUrl.isUrl() || Kdesvnsettings::remote_special_properties()) {
TQString s = m_Actions->searchProperty(_bugurl,"bugtraq:url",pegUrl,peg,true);
if (!s.isEmpty() ){
TQString reg;
s = m_Actions->searchProperty(reg,"bugtraq:logregex",pegUrl,peg,true);
if (!s.isNull() && !reg.isEmpty()) {
TQStringList s1 = TQStringList::split("\n",reg);
if (s1.size()>0) {
_r1.setPattern(s1[0]);
if (s1.size()>1) {
_r2.setPattern(s1[1]);
}
}
}
}
}
_base = root;
m_first = m_second = 0;
m_Entries = _log;
kdDebug()<<"What: "<<what << endl;
if (!what.isEmpty()){
setCaption(i18n("SVN Log of %1").arg(what));
} else {
setCaption(i18n("SVN Log"));
}
_name = what;
dispLog(_log);
}
void SvnLogDlgImp::dispLog(const svn::SharedPointer<svn::LogEntriesMap>&_log)
{
m_LogView->clear();
m_LogView->header()->setLabel(0, " ");
m_LogView->setColumnWidth(0,10);
if (!_log) {
return;
}
svn::LogEntriesMap::const_iterator lit;
LogListViewItem * item;
TQMap<long int,TQString> namesMap;
TQMap<long int,LogListViewItem*> itemMap;
long min,max;
min = max = -1;
for (lit=_log->begin();lit!=_log->end();++lit) {
item = new LogListViewItem(m_LogView,(*lit));
if ((*lit).revision>max) max = (*lit).revision;
if ((*lit).revision<min || min == -1) min = (*lit).revision;
itemMap[(*lit).revision]=item;
}
if (itemMap.count()==0) {
return;
}
m_startRevButton->setRevision(max);
m_endRevButton->setRevision(min);
m_LogView->setSelected(m_LogView->firstChild(),true);
TQString bef = _name;
long rev;
// YES! I'd checked it: this is much faster than getting list of keys
// and iterating over that list!
for (long c=max;c>-1;--c) {
if (!itemMap.contains(c)) {
continue;
}
if (itemMap[c]->realName().isEmpty()) {
itemMap[c]->setRealName(bef);
}
itemMap[c]->copiedFrom(bef,rev);
}
}
TQString SvnLogDlgImp::genReplace(const TQString&r1match)
{
static TQString anf("<a href=\"");
static TQString mid("\">");
static TQString end("</a>");
TQString res("");
if (_r2.pattern().length()<1) {
res = _bugurl;
res.replace("%BUGID%",_r1.cap(1));
res = anf+res+mid+r1match+end;
return res;
}
int pos=0;
int count=0;
int oldpos;
kdDebug()<<"Search second pattern: "<<TQString(_r2.pattern())<<" in "<<r1match<<endl;
while (pos > -1) {
oldpos = pos+count;
pos = r1match.find(_r2,pos+count);
if (pos==-1) {
break;
}
count = _r2.matchedLength();
res+=r1match.mid(oldpos,pos-oldpos);
TQString sub = r1match.mid(pos,count);
TQString _url = _bugurl;
_url.replace("%BUGID%",sub);
res+=anf+_url+mid+sub+end;
}
res+=r1match.mid(oldpos);
return res;
}
void SvnLogDlgImp::replaceBugids(TQString&msg)
{
msg = TQStyleSheet::convertFromPlainText(msg);
if (!_r1.isValid() || _r1.pattern().length()<1 || _bugurl.isEmpty()) {
return;
}
kdDebug()<<"Try match "<< TQString(_r1.pattern()) << endl;
int pos = 0;
int count = 0;
pos = _r1.search(msg,pos+count);
count = _r1.matchedLength();
while (pos>-1) {
kdDebug()<<"Found at "<<pos << " length "<<count << " with " << TQString(_r1.pattern())<< endl;
TQString s1 = msg.mid(pos,count);
kdDebug()<<"Sub: "<<s1 << endl;
kdDebug()<<TQString(_r1.cap(1)) << endl;
TQString rep = genReplace(s1);
kdDebug()<<"Replace with "<<rep << endl;
msg = msg.replace(pos,count,rep);
pos = _r1.search(msg,pos+rep.length());
count = _r1.matchedLength();
}
}
/*!
\fn SvnLogDlgImp::slotItemClicked(TQListViewItem*)
*/
void SvnLogDlgImp::slotSelectionChanged(TQListViewItem*_it)
{
if (!_it) {
m_DispPrevButton->setEnabled(false);
buttonListFiles->setEnabled(false);
buttonBlame->setEnabled(false);
m_ChangedList->clear();
return;
}
LogListViewItem* k = static_cast<LogListViewItem*>( _it );
if (k->numChangedEntries()==0) {
buttonListFiles->setEnabled(true);
if (m_ChangedList->isVisible()){
m_ChangedList->hide();
}
} else {
buttonListFiles->setEnabled(false);
if (!m_ChangedList->isVisible()){
m_ChangedList->show();
}
}
TQString msg = k->message();
replaceBugids(msg);
m_LogDisplay->setText(msg);
k->showChangedEntries(m_ChangedList);
buttonBlame->setEnabled(true);
k = static_cast<LogListViewItem*>(_it->nextSibling());
if (!k) {
m_DispPrevButton->setEnabled(false);
} else {
m_DispPrevButton->setEnabled(true);
}
}
/*!
\fn SvnLogDlgImp::slotDispPrevious()
*/
void SvnLogDlgImp::slotDispPrevious()
{
LogListViewItem* k = static_cast<LogListViewItem*>(m_LogView->selectedItem());
if (!k) {
m_DispPrevButton->setEnabled(false);
return;
}
LogListViewItem* p = static_cast<LogListViewItem*>(k->nextSibling());
if (!p) {
m_DispPrevButton->setEnabled(false);
return;
}
TQString s,e;
s = _base+k->realName();
e = _base+p->realName();
emit makeDiff(e,p->rev(),s,k->rev(),this);
}
/*!
\fn SvnLogDlgImp::saveSize()
*/
void SvnLogDlgImp::saveSize()
{
int scnum = TQApplication::desktop()->screenNumber(parentWidget());
TQRect desk = TQApplication::desktop()->screenGeometry(scnum);
TDEConfigGroupSaver cs(Kdesvnsettings::self()->config(), groupName);
TQSize sizeToSave = size();
Kdesvnsettings::self()->config()->writeEntry( TQString::fromLatin1("Width %1").arg( desk.width()),
TQString::number( sizeToSave.width()), true, false);
Kdesvnsettings::self()->config()->writeEntry( TQString::fromLatin1("Height %1").arg( desk.height()),
TQString::number( sizeToSave.height()), true, false);
}
TQSize SvnLogDlgImp::dialogSize()
{
int w, h;
int scnum = TQApplication::desktop()->screenNumber(parentWidget());
TQRect desk = TQApplication::desktop()->screenGeometry(scnum);
w = sizeHint().width();
h = sizeHint().height();
TDEConfigGroupSaver cs(Kdesvnsettings::self()->config(), groupName);
w = Kdesvnsettings::self()->config()->readNumEntry( TQString::fromLatin1("Width %1").arg( desk.width()), w );
h = Kdesvnsettings::self()->config()->readNumEntry( TQString::fromLatin1("Height %1").arg( desk.height()), h );
return( TQSize( w, h ) );
}
void SvnLogDlgImp::slotItemClicked(int button,TQListViewItem*item,const TQPoint &,int)
{
if (!item) {
m_ChangedList->clear();
return;
}
LogListViewItem*which = static_cast<LogListViewItem*>(item);
/* left mouse */
if (button == 1&&!m_ControlKeyDown) {
if (m_first) m_first->setText(0,"");
if (m_first == which) {
m_first = 0;
} else {
m_first = which;
m_first->setText(0,"1");
}
if (m_first==m_second) {
m_second = 0;
}
m_startRevButton->setRevision(which->rev());
/* other mouse or ctrl hold*/
} else {
if (m_second) m_second->setText(0,"");
if (m_second == which) {
m_second = 0;
} else {
m_second = which;
m_second->setText(0,"2");
}
if (m_first==m_second) {
m_first = 0;
}
m_endRevButton->setRevision(which->rev());
}
m_DispSpecDiff->setEnabled(m_first!=0 && m_second!=0);
}
void SvnLogDlgImp::slotRevisionSelected()
{
m_goButton->setFocus();
//m_DispSpecDiff->setEnabled( m_first && m_second && m_first != m_second);
}
void SvnLogDlgImp::slotDispSelected()
{
if (!m_first || !m_second) return;
emit makeDiff(_base+m_first->realName(),m_first->rev(),_base+m_second->realName(),m_second->rev(),this);
}
bool SvnLogDlgImp::getSingleLog(svn::LogEntry&t,const svn::Revision&r,const TQString&what,const svn::Revision&peg,TQString&root)
{
root = _base;
if (m_Entries->find(r.revnum()) == m_Entries->end())
{
return m_Actions->getSingleLog(t,r,what,peg,root);
}
t=(*m_Entries)[r.revnum()];
return true;
}
void SvnLogDlgImp::slotGetLogs()
{
kdDebug()<<"Displog: "<<m_peg.toString()<<endl;
svn::SharedPointer<svn::LogEntriesMap> lm = m_Actions->getLog(m_startRevButton->revision(),
m_endRevButton->revision(),m_peg,
_base+"/"+_name,Kdesvnsettings::self()->log_always_list_changed_files(),0,this);
if (lm) {
dispLog(lm);
}
}
void SvnLogDlgImp::slotListEntries()
{
LogListViewItem * it = static_cast<LogListViewItem*>(m_LogView->selectedItem());
if (!it||it->numChangedEntries()>0||!m_Actions) {
buttonListFiles->setEnabled(false);
return;
}
svn::SharedPointer<svn::LogEntriesMap>_log = m_Actions->getLog(it->rev(),it->rev(),it->rev(),_name,true,0);
if (!_log) {
return;
}
if (_log->count()>0) {
it->setChangedEntries((*_log)[it->rev()]);
it->showChangedEntries(m_ChangedList);
if (!m_ChangedList->isVisible()) m_ChangedList->show();
}
buttonListFiles->setEnabled(false);
}
void SvnLogDlgImp::keyPressEvent (TQKeyEvent * e)
{
if (!e) return;
if (e->text().isEmpty()&&e->key()==Key_Control) {
m_ControlKeyDown = true;
}
SvnLogDialogData::keyPressEvent(e);
}
void SvnLogDlgImp::keyReleaseEvent (TQKeyEvent * e)
{
if (!e) return;
if (e->text().isEmpty()&&e->key()==TQt::Key_Control) {
m_ControlKeyDown = false;
}
SvnLogDialogData::keyReleaseEvent(e);
}
void SvnLogDlgImp::slotBlameItem()
{
LogListViewItem* k = static_cast<LogListViewItem*>(m_LogView->selectedItem());
if (!k) {
buttonBlame->setEnabled(false);
return;
}
svn::Revision start(svn::Revision::START);
m_Actions->makeBlame(start,k->rev(),_base+k->realName(),TQT_TQWIDGET(kapp->activeModalWidget()),k->rev(),this);
}
void SvnLogDlgImp::slotEntriesSelectionChanged()
{
}
void SvnLogDlgImp::slotSingleContext(TQListViewItem*_item, const TQPoint & e, int)
{
if (!_item)
{
return;
}
LogChangePathItem* item = static_cast<LogChangePathItem*>(_item);
LogListViewItem* k = static_cast<LogListViewItem*>(m_LogView->selectedItem());
if (!k) {
kdDebug()<<"????"<<endl;
return;
}
TQPopupMenu popup;
TQString name = item->path();
TQString action = item->action();
TQString source =item->revision()>-1?item->source():item->path();
svn_revnum_t prev = item->revision()>0?item->revision():k->rev()-1;
if (action != "D") {
popup.insertItem(i18n("Annotate"),101);
if (action != "A" || item->revision()>-1) {
popup.insertItem(i18n("Diff previous"),102);
}
popup.insertItem(i18n("Cat this version"),103);
}
int r = popup.exec(e);
svn::Revision start(svn::Revision::START);
switch (r)
{
case 101:
{
m_Actions->makeBlame(start,k->rev(),_base+name,TQT_TQWIDGET(kapp->activeModalWidget()),k->rev(),this);
break;
}
case 102:
{
emit makeDiff(_base+source,prev,_base+name,k->rev(),this);
break;
}
case 103:
{
emit makeCat(k->rev(),_base+source,source,k->rev(),TQT_TQWIDGET(kapp->activeModalWidget()));
}
default:
break;
}
}
void SvnLogDlgImp::slotSingleDoubleClicked(TQListViewItem*_item)
{
if (!_item)
{
return;
}
LogChangePathItem* item = static_cast<LogChangePathItem*>(_item);
LogListViewItem* k = static_cast<LogListViewItem*>(m_LogView->selectedItem());
if (!k) {
kdDebug()<<"????"<<endl;
return;
}
TQString name = item->path();
TQString action = item->action();
TQString source =item->revision()>-1?item->source():item->path();
//svn_revnum_t prev = item->revision()>0?item->revision():k->rev()-1;
svn::Revision start(svn::Revision::START);
if (action != "D") {
m_Actions->makeBlame(start,k->rev(),_base+name,TQT_TQWIDGET(kapp->activeModalWidget()),k->rev(),this);
}
}
#include "svnlogdlgimp.moc"