File indexing completed on 2024-05-12 17:16:17

0001 /***************************************************************************
0002  *   Copyright (C) 2005-2009 by Rajko Albrecht                             *
0003  *   ral@alwins-world.de                                                   *
0004  *                                                                         *
0005  *   This program is free software; you can redistribute it and/or modify  *
0006  *   it under the terms of the GNU General Public License as published by  *
0007  *   the Free Software Foundation; either version 2 of the License, or     *
0008  *   (at your option) any later version.                                   *
0009  *                                                                         *
0010  *   This program is distributed in the hope that it will be useful,       *
0011  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
0012  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
0013  *   GNU General Public License for more details.                          *
0014  *                                                                         *
0015  *   You should have received a copy of the GNU General Public License     *
0016  *   along with this program; if not, write to the                         *
0017  *   Free Software Foundation, Inc.,                                       *
0018  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
0019  ***************************************************************************/
0020 
0021 #include "svnitem.h"
0022 #include "svnactions.h"
0023 #include "kdesvn_part.h"
0024 #include "settings/kdesvnsettings.h"
0025 #include "svnqt/status.h"
0026 #include "svnqt/url.h"
0027 #include "helpers/ktranslateurl.h"
0028 #include "helpers/kdesvn_debug.h"
0029 
0030 #include <klocalizedstring.h>
0031 #include <kiconloader.h>
0032 #include <kiconeffect.h>
0033 #include <kfileitem.h>
0034 
0035 #include <QString>
0036 #include <QFileInfo>
0037 #include <QImage>
0038 #include <QMimeDatabase>
0039 #include <QPainter>
0040 #include <QPixmap>
0041 #include <QMutexLocker>
0042 
0043 class SvnItem_p
0044 {
0045 public:
0046     SvnItem_p();
0047     explicit SvnItem_p(const svn::StatusPtr &);
0048 
0049     KFileItem &createItem(const svn::Revision &peg);
0050     QUrl &kdeName(const svn::Revision &);
0051     QMimeType mimeType(bool dir);
0052 
0053     svn::StatusPtr m_Stat;
0054     void init();
0055     QUrl m_url;
0056     QString m_full, m_short;
0057     QUrl m_kdename;
0058     QDateTime m_fullDate;
0059     QString m_infoText;
0060     KFileItem m_fitem;
0061     bool isWc;
0062     svn::Revision lRev;
0063     QMimeType m_mimeType;
0064     QMutex _infoTextMutex;
0065 };
0066 
0067 SvnItem_p::SvnItem_p()
0068     : m_Stat(new svn::Status())
0069 {
0070     init();
0071 }
0072 
0073 SvnItem_p::SvnItem_p(const svn::StatusPtr &aStat)
0074     : m_Stat(aStat)
0075 {
0076     init();
0077 }
0078 
0079 
0080 void SvnItem_p::init()
0081 {
0082     isWc = false;
0083     m_full = m_Stat->path();
0084     m_kdename.clear();
0085     m_mimeType = QMimeType();
0086     lRev = svn::Revision::UNDEFINED;
0087     while (m_full.endsWith(QLatin1Char('/'))) {
0088         /* dir name possible */
0089         m_full.chop(1);
0090     }
0091     int p = m_full.lastIndexOf(QLatin1Char('/'));
0092     if (p > -1) {
0093         ++p;
0094         m_short = m_full.right(m_full.length() - p);
0095     } else {
0096         m_short = m_full;
0097     }
0098     m_url = m_Stat->entry().url();
0099     m_fullDate = m_Stat->entry().cmtDate().toQDateTime();
0100     m_infoText.clear();
0101 }
0102 
0103 QMimeType SvnItem_p::mimeType(bool dir)
0104 {
0105     if (!m_mimeType.isValid() || m_kdename.isEmpty()) {
0106         if (m_kdename.isEmpty()) {
0107             kdeName(svn::Revision::UNDEFINED);
0108         }
0109         QMimeDatabase db;
0110         if (dir) {
0111             m_mimeType = db.mimeTypeForName(QLatin1String("inode/directory"));
0112         } else {
0113             m_mimeType = db.mimeTypeForUrl(m_kdename);
0114         }
0115     }
0116     return m_mimeType;
0117 }
0118 
0119 QUrl &SvnItem_p::kdeName(const svn::Revision &r)
0120 {
0121     isWc = !svn::Url::isValid(m_Stat->path());
0122     if (!(r == lRev) || m_kdename.isEmpty()) {
0123         lRev = r;
0124         if (!isWc) {
0125             m_kdename = m_Stat->entry().url();
0126             QString proto = helpers::KTranslateUrl::makeKdeUrl(m_kdename.scheme());
0127             m_kdename.setScheme(proto);
0128             QString revstr = lRev.toString();
0129             if (!revstr.isEmpty()) {
0130                 m_kdename.setQuery(QStringLiteral("rev=") + revstr);
0131             }
0132         } else {
0133             // Working copy path() is local file
0134             m_kdename = QUrl::fromLocalFile(m_Stat->path());
0135         }
0136     }
0137     return m_kdename;
0138 }
0139 
0140 KFileItem &SvnItem_p::createItem(const svn::Revision &peg)
0141 {
0142     if (m_fitem.isNull() || !(peg == lRev)) {
0143         m_fitem = KFileItem(kdeName(peg));
0144     }
0145     return m_fitem;
0146 }
0147 
0148 SvnItem::SvnItem()
0149     : m_overlaycolor(false)
0150     , m_bgColor(NONE)
0151     , p_Item(new SvnItem_p())
0152 {
0153 }
0154 
0155 SvnItem::SvnItem(const svn::StatusPtr &aStat)
0156     : m_overlaycolor(false)
0157     , m_bgColor(NONE)
0158     , p_Item(new SvnItem_p(aStat))
0159 {
0160 }
0161 
0162 SvnItem::~SvnItem()
0163 {
0164 }
0165 
0166 void SvnItem::setStat(const svn::StatusPtr &aStat)
0167 {
0168     m_overlaycolor = false;
0169     p_Item.reset(new SvnItem_p(aStat));
0170     SvnActions *wrap = getWrapper();
0171     if (isChanged() || isConflicted()) {
0172         wrap->addModifiedCache(aStat);
0173     } else {
0174         wrap->deleteFromModifiedCache(fullName());
0175     }
0176 }
0177 
0178 const QString &SvnItem::fullName()const
0179 {
0180     return (p_Item->m_full);
0181 }
0182 
0183 const QString &SvnItem::shortName()const
0184 {
0185     return (p_Item->m_short);
0186 }
0187 
0188 const QUrl &SvnItem::Url()const
0189 {
0190     return (p_Item->m_url);
0191 }
0192 
0193 bool SvnItem::isDir()const
0194 {
0195     if (p_Item->m_Stat->entry().isValid() || isRemoteAdded()) {
0196         if (p_Item->m_Stat->entry().kind() != svn_node_unknown) {
0197             return p_Item->m_Stat->entry().kind() == svn_node_dir;
0198         }
0199     }
0200     /* must be a local file */
0201     QFileInfo f(fullName());
0202     return f.isDir();
0203 }
0204 
0205 const QDateTime &SvnItem::fullDate()const
0206 {
0207     return (p_Item->m_fullDate);
0208 }
0209 
0210 QPixmap SvnItem::internalTransform(const QPixmap &first, int size)
0211 {
0212     if (first.isNull()) {
0213         return QPixmap();
0214     }
0215     QPixmap _p = first.scaled(QSize(size, size), Qt::KeepAspectRatio);
0216     if (_p.width() == size && _p.height() == size) {
0217         return _p;
0218     }
0219     QPixmap result(size, size);
0220     result.fill(Qt::transparent);
0221     QPainter pa;
0222     pa.begin(&result);
0223     int w = _p.width() > size ? size : _p.width();
0224     int h = _p.height() > size ? size : _p.height();
0225     pa.drawPixmap(0, 0, _p, 0, 0, w, h);
0226     pa.end();
0227     return result;
0228 }
0229 
0230 QPixmap SvnItem::getPixmap(const QPixmap &_p, int size, bool overlay)
0231 {
0232     if (!isVersioned()) {
0233         m_bgColor = NOTVERSIONED;
0234     } else if (isRealVersioned()) {
0235         SvnActions *wrap = getWrapper();
0236         bool mod = false;
0237         QPixmap p2;
0238         if (p_Item->m_Stat->nodeStatus() == svn_wc_status_conflicted) {
0239             m_bgColor = CONFLICT;
0240             if (overlay) {
0241                 p2 = KIconLoader::global()->loadIcon(QStringLiteral("kdesvnconflicted"), KIconLoader::Desktop, size);
0242             }
0243         } else if (p_Item->m_Stat->nodeStatus() == svn_wc_status_missing) {
0244             m_bgColor = MISSING;
0245         } else if (isLocked() || (wrap && wrap->checkReposLockCache(fullName()))) {
0246             if (overlay) {
0247                 p2 = KIconLoader::global()->loadIcon(QStringLiteral("kdesvnlocked"), KIconLoader::Desktop, size);
0248             }
0249             m_bgColor = LOCKED;
0250         } else if (Kdesvnsettings::check_needslock() && !isRemoteAdded() && wrap && wrap->isLockNeeded(this, svn::Revision::UNDEFINED)) {
0251             if (overlay) {
0252                 p2 = KIconLoader::global()->loadIcon(QStringLiteral("kdesvnneedlock"), KIconLoader::Desktop, size);
0253             }
0254             m_bgColor = NEEDLOCK;
0255         } else if (wrap && wrap->isUpdated(p_Item->m_Stat->path())) {
0256             if (overlay) {
0257                 p2 = KIconLoader::global()->loadIcon(QStringLiteral("kdesvnupdates"), KIconLoader::Desktop, size);
0258             }
0259             m_bgColor = UPDATES;
0260         } else if (p_Item->m_Stat->nodeStatus() == svn_wc_status_deleted) {
0261             if (overlay) {
0262                 p2 = KIconLoader::global()->loadIcon(QStringLiteral("kdesvndeleted"), KIconLoader::Desktop, size);
0263             }
0264             m_bgColor = DELETED;
0265         } else if (p_Item->m_Stat->nodeStatus() == svn_wc_status_added) {
0266             if (overlay) {
0267                 p2 = KIconLoader::global()->loadIcon(QStringLiteral("kdesvnadded"), KIconLoader::Desktop, size);
0268             }
0269             m_bgColor = ADDED;
0270         } else if (isModified()) {
0271             mod = true;
0272         } else if (isDir() && wrap) {
0273             if (isRemoteAdded() || wrap->checkUpdateCache(fullName())) {
0274                 if (overlay) {
0275                     p2 = KIconLoader::global()->loadIcon(QStringLiteral("kdesvnupdates"), KIconLoader::Desktop, size);
0276                 }
0277                 m_bgColor = UPDATES;
0278             } else if (wrap->checkConflictedCache(fullName())) {
0279                 m_bgColor = CONFLICT;
0280                 if (overlay) {
0281                     p2 = KIconLoader::global()->loadIcon(QStringLiteral("kdesvnconflicted"), KIconLoader::Desktop, size);
0282                 }
0283             } else {
0284                 mod = wrap->checkModifiedCache(fullName());
0285             }
0286         }
0287         if (mod) {
0288             m_bgColor = MODIFIED;
0289             if (overlay) {
0290                 p2 = KIconLoader::global()->loadIcon(QStringLiteral("kdesvnmodified"), KIconLoader::Desktop, size);
0291             }
0292         }
0293         if (!p2.isNull()) {
0294             QPixmap p;
0295             if (_p.width() != size || _p.height() != size) {
0296                 p = internalTransform(_p, size);
0297             } else {
0298                 p = _p;
0299             }
0300             if (p2.width() != size || p2.height() != size) {
0301                 p2 = internalTransform(p2, size);
0302             }
0303             m_overlaycolor = true;
0304             QImage i1(p.toImage());
0305             QImage i2(p2.toImage());
0306 
0307             KIconEffect::overlay(i1, i2);
0308             return QPixmap::fromImage(i1);
0309         }
0310     }
0311     return _p;
0312 }
0313 
0314 QPixmap SvnItem::getPixmap(int size, bool overlay)
0315 {
0316     QPixmap p;
0317     m_overlaycolor = false;
0318     m_bgColor = NONE;
0319     /* yes - different way to "isDir" above 'cause here we try to use the
0320        mime-features of KDE on ALL not just unversioned entries.
0321      */
0322 #ifdef DEBUG_TIMER
0323     QTime _counttime;
0324     _counttime.start();
0325 #endif
0326     if (svn::Url::isValid(p_Item->m_Stat->path())) {
0327         /* remote access */
0328         p = KIconLoader::global()->loadMimeTypeIcon(p_Item->mimeType(isDir()).iconName(),
0329                 KIconLoader::Desktop,
0330                 size,
0331                 KIconLoader::DefaultState);
0332         if (isLocked()) {
0333             m_bgColor = LOCKED;
0334             if (overlay) {
0335                 QPixmap p2 = KIconLoader::global()->loadIcon(QStringLiteral("kdesvnlocked"), KIconLoader::Desktop, size);
0336                 if (!p2.isNull()) {
0337                     QImage i1; i1 = p.toImage();
0338                     QImage i2; i2 = p2.toImage();
0339                     KIconEffect::overlay(i1, i2);
0340                     p.fromImage(i1);
0341                 }
0342             }
0343         }
0344     } else {
0345         if (isRemoteAdded()) {
0346             if (isDir()) {
0347                 p = KIconLoader::global()->loadIcon(QStringLiteral("folder"), KIconLoader::Desktop, size);
0348             } else {
0349                 p = KIconLoader::global()->loadIcon(QStringLiteral("unknown"), KIconLoader::Desktop, size);
0350             }
0351         } else {
0352             // local access
0353             const QUrl uri(QUrl::fromLocalFile(fullName()));
0354             QMimeDatabase db;
0355             const QMimeType mimeType(db.mimeTypeForUrl(uri));
0356             p = KIconLoader::global()->loadMimeTypeIcon(mimeType.iconName(), KIconLoader::Desktop, size);
0357             p = getPixmap(p, size, overlay);
0358         }
0359     }
0360 #ifdef DEBUG_TIMER
0361     //qCDebug(KDESVN_LOG)<<"Time getting icon: "<<_counttime.elapsed();
0362 #endif
0363     return p;
0364 }
0365 
0366 bool SvnItem::isVersioned()const
0367 {
0368     return p_Item->m_Stat->isVersioned();
0369 }
0370 
0371 bool SvnItem::isValid()const
0372 {
0373     if (isVersioned()) {
0374         return true;
0375     }
0376     QFileInfo f(fullName());
0377     return f.exists();
0378 }
0379 
0380 bool SvnItem::isRealVersioned()const
0381 {
0382     return p_Item->m_Stat->isRealVersioned();
0383 }
0384 
0385 bool SvnItem::isIgnored()const
0386 {
0387     return p_Item->m_Stat->nodeStatus() == svn_wc_status_ignored;
0388 }
0389 
0390 bool SvnItem::isRemoteAdded()const
0391 {
0392     return getWrapper()->isUpdated(p_Item->m_Stat->path()) &&
0393            p_Item->m_Stat->validReposStatus() && !p_Item->m_Stat->validLocalStatus();
0394 }
0395 
0396 bool SvnItem::isLocalAdded()const
0397 {
0398     return p_Item->m_Stat->nodeStatus() == svn_wc_status_added;
0399 }
0400 
0401 QString SvnItem::infoText()const
0402 {
0403     QString info_text;
0404     if (!isVersioned()) {
0405         info_text = i18n("Not versioned");
0406     } else if (getWrapper()->isUpdated(p_Item->m_Stat->path())) {
0407         if (p_Item->m_Stat->validReposStatus() && !p_Item->m_Stat->validLocalStatus()) {
0408             info_text = i18n("Added in repository");
0409         } else {
0410             info_text = i18n("Needs update");
0411         }
0412     } else {
0413         switch (p_Item->m_Stat->nodeStatus()) {
0414         case svn_wc_status_none:
0415         case svn_wc_status_normal:
0416             break;
0417         case svn_wc_status_unversioned:
0418             info_text = i18n("Not versioned");
0419             break;
0420         case svn_wc_status_modified: {
0421             if (p_Item->m_Stat->textStatus() == svn_wc_status_modified)
0422                 info_text = i18n("Locally modified");
0423             else
0424                 info_text = i18n("Property modified");
0425             break;
0426         }
0427         case svn_wc_status_added:
0428             info_text = i18n("Locally added");
0429             break;
0430         case svn_wc_status_missing:
0431             info_text = i18n("Missing");
0432             break;
0433         case svn_wc_status_deleted:
0434             info_text = i18n("Deleted");
0435             break;
0436         case svn_wc_status_replaced:
0437             info_text = i18n("Replaced");
0438             break;
0439         case svn_wc_status_ignored:
0440             info_text = i18n("Ignored");
0441             break;
0442         case svn_wc_status_external:
0443             info_text = i18n("External");
0444             break;
0445         case svn_wc_status_conflicted: {
0446             if (p_Item->m_Stat->textStatus() == svn_wc_status_conflicted)
0447                 info_text = i18n("Conflict");
0448             else
0449                 info_text = i18n("Property conflicted");
0450             break;
0451         }
0452         case svn_wc_status_merged:
0453             info_text = i18n("Merged");
0454             break;
0455         case svn_wc_status_incomplete:
0456             info_text = i18n("Incomplete");
0457             break;
0458         case svn_wc_status_obstructed:
0459             info_text = i18n("Obstructed");
0460             break;
0461         }
0462     }
0463     return info_text;
0464 }
0465 
0466 QString SvnItem::cmtAuthor()const
0467 {
0468     return p_Item->m_Stat->entry().cmtAuthor();
0469 }
0470 
0471 long int SvnItem::cmtRev()const
0472 {
0473     return p_Item->m_Stat->entry().cmtRev();
0474 }
0475 
0476 bool SvnItem::isLocked()const
0477 {
0478     return p_Item->m_Stat->entry().lockEntry().Locked();
0479 }
0480 
0481 QString SvnItem::lockOwner()const
0482 {
0483     if (p_Item->m_Stat->entry().lockEntry().Locked()) {
0484         return p_Item->m_Stat->entry().lockEntry().Owner();
0485     }
0486     svn::StatusPtr tmp;
0487     if (getWrapper()->checkReposLockCache(fullName(), tmp) && tmp) {
0488         return tmp->lockEntry().Owner();
0489     }
0490     return QString();
0491 }
0492 
0493 /*!
0494     \fn SvnItem::isModified()
0495  */
0496 bool SvnItem::isModified()const
0497 {
0498     return p_Item->m_Stat->nodeStatus() == svn_wc_status_modified
0499            || p_Item->m_Stat->nodeStatus() == svn_wc_status_replaced;
0500 }
0501 
0502 bool SvnItem::isChanged()const
0503 {
0504     return isRealVersioned() && (isModified() || isDeleted() || isLocalAdded());
0505 }
0506 
0507 bool SvnItem::isChildModified()const
0508 {
0509     return getWrapper()->checkModifiedCache(fullName());
0510 }
0511 
0512 const svn::StatusPtr &SvnItem::stat()const
0513 {
0514     return p_Item->m_Stat;
0515 }
0516 
0517 /*!
0518     \fn SvnItem::isNormal()const
0519  */
0520 bool SvnItem::isNormal()const
0521 {
0522     return p_Item->m_Stat->nodeStatus() == svn_wc_status_normal;
0523 }
0524 
0525 bool SvnItem::isMissing()const
0526 {
0527     return p_Item->m_Stat->nodeStatus() == svn_wc_status_missing;
0528 }
0529 
0530 bool SvnItem::isDeleted()const
0531 {
0532     return p_Item->m_Stat->nodeStatus() == svn_wc_status_deleted;
0533 }
0534 
0535 bool SvnItem::isConflicted()const
0536 {
0537     return p_Item->m_Stat->nodeStatus() == svn_wc_status_conflicted;
0538 }
0539 
0540 bool SvnItem::hasToolTipText()
0541 {
0542     QMutexLocker ml(&(p_Item->_infoTextMutex));
0543     return !p_Item->m_infoText.isNull();
0544 }
0545 
0546 svn::Revision SvnItem::revision() const
0547 {
0548     if (isRealVersioned() && !p_Item->m_Stat->entry().url().isEmpty()) {
0549         return p_Item->m_Stat->entry().revision();
0550     }
0551     return svn::Revision::UNDEFINED;
0552 }
0553 
0554 /*!
0555     \fn SvnItem::getToolTipText()
0556  */
0557 const QString &SvnItem::getToolTipText()
0558 {
0559     if (!hasToolTipText()) {
0560         qCDebug(KDESVN_LOG) << "Try getting text" << endl;
0561         QString text;
0562         if (isRealVersioned() && !p_Item->m_Stat->entry().url().isEmpty()) {
0563             SvnActions *wrap = getWrapper();
0564             svn::Revision peg(svn_opt_revision_unspecified);
0565             svn::Revision rev(svn_opt_revision_unspecified);
0566             if (svn::Url::isValid(p_Item->m_Stat->path())) {
0567                 /* remote */
0568                 rev = p_Item->m_Stat->entry().revision();
0569                 peg = correctPeg();
0570             } else {
0571                 /* local */
0572             }
0573             if (wrap) {
0574                 SvnItemList lst;
0575                 lst.append(this);
0576                 text = wrap->getInfo(lst, rev, peg, false, false);
0577                 qCDebug(KDESVN_LOG) << text << endl;
0578                 // KF5: TODO
0579                 /*
0580                 if (!p_Item->m_fitem.isNull()) {
0581                     text += p_Item->m_fitem.getToolTipText(0);
0582                 }
0583                 */
0584             }
0585         } else if (!p_Item->m_fitem.isNull()) {
0586             // KF5: TODO
0587 //            text = p_Item->m_fitem.getToolTipText(6);
0588         }
0589         QMutexLocker ml(&(p_Item->_infoTextMutex));
0590         p_Item->m_infoText = text;
0591     }
0592     QMutexLocker ml(&(p_Item->_infoTextMutex));
0593     return p_Item->m_infoText;
0594 }
0595 
0596 void SvnItem::generateToolTip(const svn::InfoEntry &entry)
0597 {
0598     QString text;
0599     if (isRealVersioned() &&  !p_Item->m_Stat->entry().url().isEmpty()) {
0600         SvnActions *wrap = getWrapper();
0601         if (wrap) {
0602             svn::InfoEntries e; e.append(entry);
0603             text = wrap->getInfo(e, fullName(), false);
0604         }
0605         if (!p_Item->m_fitem.isNull()) {
0606             // KF5: TODO
0607 //            text += p_Item->m_fitem.getToolTipText(0);
0608         }
0609     } else if (!p_Item->m_fitem.isNull()) {
0610 //        text = p_Item->m_fitem.getToolTipText(6);
0611     }
0612     {
0613         QMutexLocker ml(&(p_Item->_infoTextMutex));
0614         p_Item->m_infoText = text;
0615     }
0616 }
0617 
0618 KFileItem SvnItem::fileItem()
0619 {
0620     return p_Item->createItem(correctPeg());
0621 }
0622 
0623 const QUrl &SvnItem::kdeName(const svn::Revision &r)
0624 {
0625     return p_Item->kdeName(r);
0626 }
0627 
0628 QMimeType SvnItem::mimeType()
0629 {
0630     return p_Item->mimeType(isDir());
0631 }