summaryrefslogtreecommitdiffstats
path: root/src/svnfrontend/blamedisplay_impl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/svnfrontend/blamedisplay_impl.cpp')
-rw-r--r--src/svnfrontend/blamedisplay_impl.cpp490
1 files changed, 490 insertions, 0 deletions
diff --git a/src/svnfrontend/blamedisplay_impl.cpp b/src/svnfrontend/blamedisplay_impl.cpp
new file mode 100644
index 0000000..36381e4
--- /dev/null
+++ b/src/svnfrontend/blamedisplay_impl.cpp
@@ -0,0 +1,490 @@
+/***************************************************************************
+ * Copyright (C) 2006-2007 by Rajko Albrecht *
+ * *
+ * 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 "blamedisplay_impl.h"
+#include "simple_logcb.h"
+#include "src/settings/kdesvnsettings.h"
+#include "src/svnqt/log_entry.hpp"
+#include "fronthelpers/cursorstack.h"
+#include "fronthelpers/widgetblockstack.h"
+#include "src/ksvnwidgets/encodingselector_impl.h"
+
+#include <klistview.h>
+#include <kglobalsettings.h>
+#include <kglobal.h>
+#include <klocale.h>
+#include <kdebug.h>
+#include <kinputdialog.h>
+#include <kmessagebox.h>
+#include <kdialogbase.h>
+#include <kapp.h>
+#include <ktextbrowser.h>
+#include <klistviewsearchline.h>
+
+#include <qpixmap.h>
+#include <qpainter.h>
+#include <qheader.h>
+#include <qmap.h>
+#include <qpopupmenu.h>
+#include <qvbox.h>
+#include <qtooltip.h>
+#include <qwhatsthis.h>
+#include <qlayout.h>
+#include <qtextcodec.h>
+
+#define COL_LINENR 0
+#define COL_REV 1
+#define COL_DATE 2
+#define COL_AUT 3
+#define COL_LINE 4
+
+class LocalizedAnnotatedLine:public svn::AnnotateLine
+{
+public:
+ LocalizedAnnotatedLine(const svn::AnnotateLine&al)
+ :svn::AnnotateLine(al)
+ {
+ localeChanged();
+ }
+
+ void localeChanged()
+ {
+ if (!codec_searched) {
+ cc = QTextCodec::codecForName(Kdesvnsettings::locale_for_blame());
+ codec_searched = true;
+ }
+ if (cc) {
+ m_tLine=cc->toUnicode(line().data(),line().size());
+ m_tAuthor=cc->toUnicode(author().data(),author().size());
+ } else {
+ m_tLine=QString::FROMUTF8(line().data(),line().size());
+ m_tAuthor=QString::FROMUTF8(author().data(),author().size());
+ }
+ }
+
+ const QString& tAuthor()const{return m_tAuthor;}
+ const QString& tLine()const{return m_tLine;}
+
+ static void reset_codec(){codec_searched = false; cc=0;}
+
+protected:
+ QString m_tAuthor,m_tLine;
+
+ static bool codec_searched;
+ static QTextCodec * cc;
+};
+
+QTextCodec* LocalizedAnnotatedLine::cc = 0;
+bool LocalizedAnnotatedLine::codec_searched = false;
+
+class BlameDisplayItem:public KListViewItem
+{
+public:
+ BlameDisplayItem(KListView*,const svn::AnnotateLine&,bool,BlameDisplay_impl*);
+ BlameDisplayItem(KListView*,BlameDisplayItem*,const svn::AnnotateLine&,bool,BlameDisplay_impl*);
+ virtual ~BlameDisplayItem(){}
+ virtual int compare(QListViewItem *i, int col, bool ascending)const;
+ virtual void paintCell(QPainter *p, const QColorGroup &cg, int column, int width, int alignment);
+ virtual int rtti()const{return 1000;}
+
+ virtual int width( const QFontMetrics & fm, const QListView * lv, int c ) const;
+
+ apr_int64_t lineNumber(){return m_Content.lineNumber();}
+ svn_revnum_t rev(){return m_Content.revision();}
+
+ void localeChanged()
+ {
+ m_Content.localeChanged();
+ if (m_disp){
+ setText(COL_AUT,m_Content.tAuthor());
+ }
+ QString _line = m_Content.tLine();
+ _line.replace("\t"," ");
+ setText(COL_LINE,QString("%1").arg(_line));
+ }
+
+protected:
+ LocalizedAnnotatedLine m_Content;
+
+ bool m_disp;
+
+ void display();
+ BlameDisplay_impl*cb;
+};
+
+BlameDisplayItem::BlameDisplayItem(KListView*lv,const svn::AnnotateLine&al,bool disp,BlameDisplay_impl*_c)
+ : KListViewItem(lv),m_Content(al),m_disp(disp),cb(_c)
+{
+ display();
+}
+
+BlameDisplayItem::BlameDisplayItem(KListView*lv,BlameDisplayItem*it,const svn::AnnotateLine&al,bool disp,BlameDisplay_impl*_c)
+ : KListViewItem(lv,it),m_Content(al),m_disp(disp),cb(_c)
+{
+ display();
+}
+
+#define BORDER 4
+
+int BlameDisplayItem::width (const QFontMetrics & fm, const QListView * lv, int c ) const
+{
+ if (c == COL_LINE) {
+ return KListViewItem::width(QFontMetrics(KGlobalSettings::fixedFont()),lv,c)+2*BORDER;
+ }
+ return KListViewItem::width(fm,lv,c)+2*BORDER;
+}
+
+void BlameDisplayItem::display()
+{
+ if (m_disp){
+ setText(COL_REV,QString("%1").arg(m_Content.revision()));
+ setText(COL_AUT,m_Content.tAuthor());
+ if (m_Content.date().isValid()) {
+ setText(COL_DATE,KGlobal::locale()->formatDateTime(m_Content.date()));
+ }
+ }
+
+ setText(COL_LINENR,QString("%1").arg(m_Content.lineNumber()+1));
+ QString _line = m_Content.tLine();
+ _line.replace("\t"," ");
+ setText(COL_LINE,QString("%1").arg(_line));
+}
+
+int BlameDisplayItem::compare(QListViewItem *item, int col, bool ascending)const
+{
+ Q_UNUSED(ascending);
+ BlameDisplayItem* k = static_cast<BlameDisplayItem*>(item);
+ if (col == COL_REV) {
+ return k->m_Content.revision()-m_Content.revision();
+ }
+ if (col == COL_AUT) {
+ if (Kdesvnsettings::locale_is_casesensitive()) {
+ return m_Content.tAuthor().localeAwareCompare(k->m_Content.author());
+ }
+ return m_Content.tAuthor().compare(k->m_Content.author());
+ }
+ return k->m_Content.lineNumber()-m_Content.lineNumber();
+}
+
+void BlameDisplayItem::paintCell(QPainter *p, const QColorGroup &cg, int column, int width, int alignment)
+{
+ if (alignment & (AlignTop || AlignBottom) == 0)
+ alignment |= AlignVCenter;
+
+ /* don't copy string */
+ const QString & str = text(column);;
+ if (column == COL_LINE) {
+ p->setFont(KGlobalSettings::fixedFont());
+ }
+
+ QColorGroup _cg = cg;
+ QColor _bgColor;
+ if (column==COL_LINENR || isSelected()) {
+ _bgColor = KGlobalSettings::highlightColor();
+ p->setPen(KGlobalSettings::highlightedTextColor());
+ } else {
+ if (Kdesvnsettings::self()->colored_blame()) {
+ _bgColor = cb->rev2color(m_Content.revision());
+ } else {
+ _bgColor = listView()->viewport()->colorGroup().base();
+ }
+ }
+
+ p->fillRect(0, 0, width, height(), _bgColor);
+ if (column==COL_AUT) {
+ p->drawLine(width-1,0,width-1,height());
+ }
+
+ if (str.isEmpty())
+ return;
+ p->drawText(BORDER, 0, width - 2*BORDER, height(), alignment, str);
+}
+
+class BlameDisplayData
+{
+ public:
+ BlameDisplayData()
+ {
+ max=-1;
+ min=INT_MAX-1;
+ rev_count=0;
+ up=false;
+ m_cb=0;m_File="";
+ m_dlg = 0;
+ }
+ ~BlameDisplayData(){}
+ svn_revnum_t max,min;
+ QMap<svn_revnum_t,QColor> m_shadingMap;
+ QMap<svn_revnum_t,svn::LogEntry> m_logCache;
+
+ QColor m_lastCalcColor;
+ unsigned int rev_count;
+ bool up;
+ SimpleLogCb*m_cb;
+ QString m_File;
+ KDialogBase*m_dlg;
+
+ QString reposRoot;
+};
+
+BlameDisplay_impl::BlameDisplay_impl(QWidget*parent,const char*name)
+ : BlameDisplay(parent,name)
+{
+ m_Data = new BlameDisplayData();
+ connect(m_BlameList,SIGNAL(selectionChanged()),this,SLOT(slotSelectionChanged()));
+}
+
+BlameDisplay_impl::BlameDisplay_impl(const QString&what,const svn::AnnotatedFile&blame,QWidget*parent,const char*name)
+ : BlameDisplay(parent,name)
+{
+ m_Data = new BlameDisplayData();
+ connect(m_BlameList,SIGNAL(selectionChanged()),this,SLOT(slotSelectionChanged()));
+ setContent(what,blame);
+}
+
+void BlameDisplay_impl::setCb(SimpleLogCb*_cb)
+{
+ m_Data->m_cb = _cb;
+}
+
+void BlameDisplay_impl::setContent(const QString&what,const svn::AnnotatedFile&blame)
+{
+ m_Data->m_File = what;
+ m_SearchWidget = new KListViewSearchLineWidget(m_BlameList,this);
+ EncodingSelector_impl*m_Ls = new EncodingSelector_impl(Kdesvnsettings::locale_for_blame(),this);
+ connect(m_Ls,SIGNAL(TextCodecChanged(const QString&)),
+ this,SLOT(slotTextCodecChanged(const QString&)));
+
+ BlameDisplayLayout->remove(m_BlameList);
+ BlameDisplayLayout->addWidget(m_Ls);
+ BlameDisplayLayout->addWidget(m_SearchWidget);
+ BlameDisplayLayout->addWidget(m_BlameList);
+
+ m_BlameList->setColumnAlignment(COL_REV,Qt::AlignRight);
+ m_BlameList->setColumnAlignment(COL_LINENR,Qt::AlignRight);
+
+ m_BlameList->clear();
+ if (m_Data->m_dlg) {
+ m_Data->m_dlg->enableButton(KDialogBase::User2,false);
+ }
+ svn::AnnotatedFile::const_iterator bit;
+ m_BlameList->setSorting(COL_LINENR,false);
+ m_Data->max = -1;
+ svn_revnum_t lastRev(-1);
+ for (bit=blame.begin();bit!=blame.end();++bit) {
+ bool disp = (*bit).revision()!=lastRev || bit==blame.begin() ;
+
+ if ((*bit).revision()>m_Data->max) {m_Data->max=(*bit).revision();++(m_Data->rev_count);}
+ if ((*bit).revision()<m_Data->min) m_Data->min=(*bit).revision();
+ new BlameDisplayItem(m_BlameList,(*bit),disp,this);
+ if (disp) {
+ lastRev = (*bit).revision();
+ }
+ if (m_Data->m_shadingMap.find((*bit).revision())==m_Data->m_shadingMap.end()) {
+ m_Data->m_shadingMap[(*bit).revision()]=QColor();
+ }
+ }
+ if (Kdesvnsettings::self()->colored_blame()) {
+ QColor a(160,160,160);
+ int offset = 10;
+ int r=0; int g=0;int b=0;
+ uint colinc=0;
+
+ for (svn_revnum_t i = m_Data->min; i<= m_Data->max;++i) {
+ if (m_Data->m_shadingMap.find(i)==m_Data->m_shadingMap.end()) {
+ continue;
+ }
+ a.setRgb(a.red()+offset,a.green()+offset,a.blue()+offset);
+ m_Data->m_shadingMap[i]=a;
+ if ( a.red()>245||a.green()>245||a.blue()>245 ) {
+ if (colinc==0) {
+ ++colinc;
+ } else if (r>=50||g>=50||b>=50) {
+ if (++colinc>6) {
+ colinc = 0;
+ r=g=b=0;
+ } else {
+ r=g=b=-10;
+ }
+ }
+ if (colinc & 0x1) {
+ r+=10;
+ }
+ if (colinc & 0x2) {
+ g+=10;
+ }
+ if (colinc & 0x4) {
+ b+=10;
+ }
+ a.setRgb(160+r,160+g,160+b);
+ }
+ }
+ }
+}
+
+const QColor BlameDisplay_impl::rev2color(svn_revnum_t r )const
+{
+ if (m_Data->m_shadingMap.find(r)!=m_Data->m_shadingMap.end() && m_Data->m_shadingMap[r].isValid())
+ {
+ return m_Data->m_shadingMap[r];
+ } else {
+ return m_BlameList->viewport()->colorGroup().base();
+ }
+}
+
+BlameDisplay_impl::~BlameDisplay_impl()
+{
+ delete m_Data;
+}
+
+void BlameDisplay_impl::slotGoLine()
+{
+ bool ok = true;
+ int line = KInputDialog::getInteger(i18n("Show line"),i18n("Show line number"),
+ 1,1,m_BlameList->childCount(),1,&ok,this);
+ if (!ok) {
+ return;
+ }
+ QListViewItem*item = m_BlameList->firstChild();
+ --line;
+ while (item) {
+ if (item->rtti()==1000) {
+ BlameDisplayItem*bit = static_cast<BlameDisplayItem*>(item);
+ if (bit->lineNumber()==line) {
+ m_BlameList->ensureItemVisible(bit);
+ m_BlameList->setSelected(bit,true);
+ return;
+ }
+ }
+ item = item->nextSibling();
+ }
+}
+
+void BlameDisplay_impl::slotContextMenuRequested(KListView*,QListViewItem*item, const QPoint&pos)
+{
+ if (item==0||item->rtti()!=1000) return;
+ BlameDisplayItem*bit = static_cast<BlameDisplayItem*>(item);
+ QPopupMenu popup;
+ popup.insertItem(i18n("Log message for revision"),101);
+ int r = popup.exec(pos);
+
+ switch (r)
+ {
+ case 101:
+ showCommit(bit);
+ break;
+ default:
+ break;
+ }
+}
+
+void BlameDisplay_impl::showCommit(BlameDisplayItem*bit)
+{
+ if (!bit) return;
+ WidgetBlockStack a(m_BlameList);
+ QString text;
+ if (m_Data->m_logCache.find(bit->rev())!=m_Data->m_logCache.end()) {
+ text = m_Data->m_logCache[bit->rev()].message;
+ } else {
+ CursorStack a(Qt::BusyCursor);
+ svn::LogEntry t;
+ if (m_Data->m_cb && m_Data->m_cb->getSingleLog(t,bit->rev(),m_Data->m_File,m_Data->max,m_Data->reposRoot)) {
+ m_Data->m_logCache[bit->rev()] = t;
+ text = m_Data->m_logCache[bit->rev()].message;
+ }
+ }
+ KDialogBase* dlg = new KDialogBase(
+ KApplication::activeModalWidget(),
+ "simplelog",true,QString(i18n("Logmessage for revision %1").arg(bit->rev())),
+ KDialogBase::Close);
+ QWidget* Dialog1Layout = dlg->makeVBoxMainWidget();
+ KTextBrowser*ptr = new KTextBrowser(Dialog1Layout);
+ ptr->setFont(KGlobalSettings::fixedFont());
+ ptr->setWordWrap(QTextEdit::NoWrap);
+ ptr->setText(text);
+ dlg->resize(dlg->configDialogSize(*(Kdesvnsettings::self()->config()),"simplelog_display"));
+ dlg->exec();
+ dlg->saveDialogSize(*(Kdesvnsettings::self()->config()),"simplelog_display",false);
+}
+
+void BlameDisplay_impl::slotShowCurrentCommit()
+{
+ QListViewItem*item = m_BlameList->selectedItem();
+ if (item==0||item->rtti()!=1000) return;
+ BlameDisplayItem*bit = static_cast<BlameDisplayItem*>(item);
+ showCommit(bit);
+}
+
+void BlameDisplay_impl::slotSelectionChanged()
+{
+ if (!m_Data->m_dlg) return;
+ QListViewItem*item = m_BlameList->selectedItem();
+ if (item==0||item->rtti()!=1000) {
+ m_Data->m_dlg->enableButton(KDialogBase::User2,false);
+ } else {
+ m_Data->m_dlg->enableButton(KDialogBase::User2,true);
+ }
+}
+
+void BlameDisplay_impl::displayBlame(SimpleLogCb*_cb,const QString&item,const svn::AnnotatedFile&blame,QWidget*,const char*name)
+{
+ int buttons = KDialogBase::Close|KDialogBase::User1|KDialogBase::User2;
+ KDialogBase * dlg = new KDialogBase(
+ KApplication::activeModalWidget(),
+ name,true,QString(i18n("Blame %1")).arg(item),buttons,KDialogBase::Close,false,
+ KGuiItem(i18n("Goto line")),KGuiItem(i18n("Log message for revision"),"kdesvnlog"));
+
+ QWidget* Dialog1Layout = dlg->makeVBoxMainWidget();
+ BlameDisplay_impl*ptr = new BlameDisplay_impl(Dialog1Layout);
+ dlg->resize(dlg->configDialogSize(*(Kdesvnsettings::self()->config()),"blame_dlg"));
+ ptr->setContent(item,blame);
+ ptr->setCb(_cb);
+ ptr->m_Data->m_dlg = dlg;
+ dlg->enableButton(KDialogBase::User2,false);
+ connect(dlg,SIGNAL(user1Clicked()),ptr,SLOT(slotGoLine()));
+ connect(dlg,SIGNAL(user2Clicked()),ptr,SLOT(slotShowCurrentCommit()));
+ Dialog1Layout->adjustSize();
+ dlg->exec();
+
+ dlg->saveDialogSize(*(Kdesvnsettings::self()->config()),"blame_dlg",false);
+}
+
+void BlameDisplay_impl::slotItemDoubleClicked(QListViewItem*item)
+{
+ if (item==0||item->rtti()!=1000) return;
+ BlameDisplayItem*bit = static_cast<BlameDisplayItem*>(item);
+ showCommit(bit);
+}
+
+void BlameDisplay_impl::slotTextCodecChanged(const QString&what)
+{
+ if (Kdesvnsettings::locale_for_blame()!=what) {
+ Kdesvnsettings::setLocale_for_blame(what);
+ Kdesvnsettings::self()->writeConfig();
+ LocalizedAnnotatedLine::reset_codec();
+ QListViewItemIterator it(m_BlameList);
+ while ( it.current() ) {
+ BlameDisplayItem*_it = static_cast<BlameDisplayItem*>(it.current());
+ _it->localeChanged();
+ ++it;
+ }
+ }
+}
+
+#include "blamedisplay_impl.moc"