diff --git a/src/kdesvn.cpp b/src/kdesvn.cpp index 54ee8cb0..deffea7b 100644 --- a/src/kdesvn.cpp +++ b/src/kdesvn.cpp @@ -1,447 +1,447 @@ /*************************************************************************** * Copyright (C) 2005-2009 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 "kdesvn.h" #include "urldlg.h" #include "kdesvn_part.h" #include "helpers/ktranslateurl.h" #include "helpers/kdesvn_debug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef TESTING_RC #include #endif kdesvn::kdesvn() : KParts::MainWindow(), KBookmarkOwner() { setAttribute(Qt::WA_DeleteOnClose); m_part = nullptr; #ifdef TESTING_RC setXMLFile(TESTING_RC); qCDebug(KDESVN_LOG) << "Using test rc file in " << TESTING_RC << endl; // I hate this crashhandler in development KCrash::setCrashHandler(0); #else setXMLFile(QStringLiteral("kdesvnui.rc")); #endif setStandardToolBarMenuEnabled(true); // then, setup our actions setupActions(); // and a status bar statusBar()->show(); QDir bookmarkDir(QStandardPaths::writableLocation(QStandardPaths::DataLocation)); if (!bookmarkDir.exists()) { bookmarkDir.mkpath(bookmarkDir.absolutePath()); } m_bookmarkFile = bookmarkDir.absolutePath()+ QLatin1String("/bookmarks.xml"); m_BookmarkManager = KBookmarkManager::managerForExternalFile(m_bookmarkFile); m_BookmarksActionmenu = new KBookmarkActionMenu(m_BookmarkManager->root(), i18n("&Bookmarks"), this); actionCollection()->addAction(QStringLiteral("bookmarks"), m_BookmarksActionmenu); m_Bookmarkactions = new KActionCollection(static_cast(this)); m_pBookmarkMenu = new KBookmarkMenu(m_BookmarkManager, this, m_BookmarksActionmenu->menu(), m_Bookmarkactions); m_pBookmarkMenu->setParent(this); // clear when kdesvn window gets destroyed #ifdef EXTRA_KDE_LIBPATH QCoreApplication::addLibraryPath(QString::fromLocal8Bit(EXTRA_KDE_LIBPATH)) #endif // this routine will find and load our Part. it finds the Part by // name which is a bad idea usually.. but it's alright in this // case since our Part is made for this Shell KPluginLoader loader(QStringLiteral("kdesvnpart")); KPluginFactory *factory = loader.factory(); if (factory) { m_part = factory->create(this); if (m_part) { // tell the KParts::MainWindow that this is indeed the main widget setCentralWidget(m_part->widget()); connect(this, SIGNAL(sigSavestate()), m_part->widget(), SLOT(slotSavestate())); connect(m_part->widget(), SIGNAL(sigExtraStatusMessage(QString)), this, SLOT(slotExtraStatus(QString))); connect(m_part->widget(), SIGNAL(sigUrlOpened(bool)), this, SLOT(slotUrlOpened(bool))); QAction *tmpAction; tmpAction = actionCollection()->addAction(QStringLiteral("subversion_create_repo"), m_part->widget(), SLOT(slotCreateRepo())); tmpAction->setText(i18n("Create and open new repository")); tmpAction->setToolTip(i18n("Create and opens a new local Subversion repository")); tmpAction = actionCollection()->addAction(QStringLiteral("subversion_dump_repo"), m_part->widget(), SLOT(slotDumpRepo())); tmpAction->setText(i18n("Dump repository to file")); tmpAction->setToolTip(i18n("Dump a Subversion repository to a file")); tmpAction = actionCollection()->addAction(QStringLiteral("subversion_hotcopy_repo"), m_part->widget(), SLOT(slotHotcopy())); tmpAction->setText(i18n("Hotcopy a repository")); tmpAction->setToolTip(i18n("Hotcopy a Subversion repository to a new folder")); tmpAction = actionCollection()->addAction(QStringLiteral("subversion_load_repo"), m_part->widget(), SLOT(slotLoaddump())); tmpAction->setText(i18n("Load dump into repository")); tmpAction->setToolTip(i18n("Load a dump file into a repository.")); tmpAction = actionCollection()->addAction(QStringLiteral("kdesvn_ssh_add"), m_part, SLOT(slotSshAdd())); tmpAction->setText(i18n("Add ssh identities to ssh-agent")); tmpAction->setToolTip(i18n("Force add ssh-identities to ssh-agent for future use.")); tmpAction = actionCollection()->addAction(QStringLiteral("help_about_kdesvnpart"), m_part, SLOT(showAboutApplication())); tmpAction->setText(i18n("Info about kdesvn part")); tmpAction->setToolTip(i18n("Shows info about the kdesvn plugin and not the standalone application.")); tmpAction = actionCollection()->addAction(QStringLiteral("db_show_status"), m_part, SLOT(showDbStatus())); tmpAction->setText(i18n("Show database content")); tmpAction->setToolTip(i18n("Show the content of log cache database")); // and integrate the part's GUI with the shells createGUI(m_part); } else { KMessageBox::error(this, i18n("Could not load the part:\n%1", loader.errorString())); qApp->quit(); return; } } else { // if we couldn't find our Part, we exit since the Shell by // itself can't do anything useful KMessageBox::error(this, i18n("Could not find our part:\n%1", loader.errorString())); qApp->quit(); // we return here, cause kapp->quit() only means "exit the // next time we enter the event loop... return; } setAutoSaveSettings(); } kdesvn::~kdesvn() { } void kdesvn::loadRescent(const QUrl &url) { load(url, true); } void kdesvn::load(const QUrl &url, bool addRescent) { QTimer::singleShot(100, this, &kdesvn::slotResetExtraStatus); if (m_part) { bool ret = m_part->openUrl(url); KRecentFilesAction *rac = nullptr; if (addRescent) { QAction *ac = actionCollection()->action(QStringLiteral("file_open_recent")); if (ac) { rac = (KRecentFilesAction *)ac; } } if (!ret) { changeStatusbar(i18n("Could not open URL %1", url.toString())); if (rac) { rac->removeUrl(url); } } else { resetStatusBar(); if (rac) { rac->addUrl(url); } } if (rac) { KConfigGroup cg(KSharedConfig::openConfig(), "recent_files"); rac->saveEntries(cg); } } } void kdesvn::setupActions() { QAction *ac; KStandardAction::open(this, SLOT(fileOpen()), actionCollection()); KStandardAction::openNew(this, SLOT(fileNew()), actionCollection()); ac = KStandardAction::close(this, SLOT(fileClose()), actionCollection()); // ac->setEnabled(getMemberList()->count()>1); ac->setEnabled(memberList().count() > 1); KStandardAction::quit(this, SLOT(close()), actionCollection()); KRecentFilesAction *rac = KStandardAction::openRecent(this, SLOT(loadRescent(QUrl)), actionCollection()); if (rac) { rac->setMaxItems(8); KConfigGroup cg(KSharedConfig::openConfig(), "recent_files"); rac->loadEntries(cg); rac->setText(i18n("Recent opened URLs")); } KStandardAction::keyBindings(this, SLOT(optionsConfigureKeys()), actionCollection()); KStandardAction::configureToolbars(this, SLOT(optionsConfigureToolbars()), actionCollection()); m_statusbarAction = KStandardAction::showStatusbar(this, SLOT(optionsShowStatusbar()), actionCollection()); KToggleAction *toggletemp; toggletemp = new KToggleAction(i18n("Load last opened URL on start"), this); actionCollection()->addAction(QStringLiteral("toggle_load_last_url"), toggletemp); toggletemp->setToolTip(i18n("Reload last opened URL if no one is given on command line")); KConfigGroup cs(KSharedConfig::openConfig(), "startup"); toggletemp->setChecked(cs.readEntry("load_last_on_start", false)); connect(toggletemp, &QAction::toggled, this, &kdesvn::slotLoadLast); } void kdesvn::optionsShowStatusbar() { // this is all very cut and paste code for showing/hiding the // statusbar statusBar()->setVisible(m_statusbarAction->isChecked()); } void kdesvn::fileClose() { if (m_part) { m_part->closeUrl(); } // if (getMemberList()->count()>1) { if (memberList().count() > 1) { close(); } else { enableClose(false); QTimer::singleShot(100, this, &kdesvn::slotResetExtraStatus); enableClose(false); } } void kdesvn::saveProperties(KConfigGroup &config) { // the 'config' object points to the session managed // config file. anything you write here will be available // later when this app is restored if (!m_part) { return; } if (!m_part->url().isEmpty()) { config.writeEntry("lastURL", m_part->url().toString()); } } void kdesvn::readProperties(const KConfigGroup &config) { // the 'config' object points to the session managed // config file. this function is automatically called whenever // the app is being restored. read in here whatever you wrote // in 'saveProperties' if (!m_part) { return; } const QUrl url(config.readPathEntry("lastURL", QString())); if (url.isValid()) { m_part->openUrl(url); } } void kdesvn::fileNew() { // this slot is called whenever the File->New menu is selected, // the New shortcut is pressed (usually CTRL+N) or the New toolbar // button is clicked // create a new window (new kdesvn)->show(); enableClose(true); } void kdesvn::fileOpen() { QUrl url = UrlDlg::getUrl(this); if (!url.isEmpty()) { load(url, true); } } void kdesvn::changeStatusbar(const QString &text) { statusBar()->showMessage(text); } void kdesvn::resetStatusBar() { statusBar()->showMessage(i18n("Ready"), 4000); } // kde4 port - pv void kdesvn::openBookmark(const KBookmark &bm, Qt::MouseButtons mb, Qt::KeyboardModifiers km) { Q_UNUSED(mb); Q_UNUSED(km); if (!bm.url().isEmpty() && m_part) { load(bm.url(), false); } } QUrl kdesvn::currentUrl() const { if (!m_part) { return QUrl(); } return m_part->url(); } QString kdesvn::currentTitle() const { if (!m_part) { return QString(); } return m_part->url().fileName(); } void kdesvn::enableClose(bool how) { QAction *ac; if ((ac = actionCollection()->action(QStringLiteral("file_close")))) { ac->setEnabled(how); } } /*! \fn kdesvn::slotUrlOpened(bool) */ void kdesvn::slotUrlOpened(bool how) { enableClose(how); } /*! \fn kdesvn::optionsConfigureToolbars() */ void kdesvn::optionsConfigureToolbars() { KConfigGroup cg(KSharedConfig::openConfig(), autoSaveGroup()); saveMainWindowSettings(cg); // use the standard toolbar editor QPointer dlg(new KEditToolBar(factory())); - connect(dlg.data(), &KEditToolBar::newToolbarConfig, + connect(dlg.data(), &KEditToolBar::newToolBarConfig, this, &kdesvn::applyNewToolbarConfig); dlg->exec(); delete dlg; } /*! \fn kdesvn::applyNewToolbarConfig() */ void kdesvn::applyNewToolbarConfig() { KConfigGroup cg(KSharedConfig::openConfig(), autoSaveGroup()); applyMainWindowSettings(cg); } void kdesvn::optionsConfigureKeys() { QPointer kdlg(new KShortcutsDialog(KShortcutsEditor::AllActions, KShortcutsEditor::LetterShortcutsAllowed, m_part->widget())); kdlg->addCollection(actionCollection()); kdlg->addCollection(m_part->actionCollection()); kdlg->configure(true); delete kdlg; } /*! \fn kdesvn::closeEvent() */ void kdesvn::closeEvent(QCloseEvent *ev) { emit sigSavestate(); if (m_part) { KConfigGroup cs(KSharedConfig::openConfig(), "startup"); cs.writeEntry("lastURL", m_part->url().toString(QUrl::FullyEncoded)); cs.sync(); } return KParts::MainWindow::closeEvent(ev); } /*! \fn kdesvn::checkReload() */ void kdesvn::checkReload() { KConfigGroup cs(KSharedConfig::openConfig(), "startup"); if (!cs.readEntry("load_last_on_start", false)) { return; } const QUrl url(cs.readPathEntry("lastURL", QString())); if (url.isValid() && m_part) { load(url, false); } } /*! \fn kdesvn::slotLoadLast(bool) */ void kdesvn::slotLoadLast(bool how) { KConfigGroup cs(KSharedConfig::openConfig(), "startup"); cs.writeEntry("load_last_on_start", how); cs.sync(); } void kdesvn::slotResetExtraStatus() { statusBar()->clearMessage(); } void kdesvn::slotExtraStatus(const QString &message) { statusBar()->clearMessage(); statusBar()->showMessage(message); } diff --git a/src/svnfrontend/graphtree/revisiontree.cpp b/src/svnfrontend/graphtree/revisiontree.cpp index 1c80e1ad..6516dae9 100644 --- a/src/svnfrontend/graphtree/revisiontree.cpp +++ b/src/svnfrontend/graphtree/revisiontree.cpp @@ -1,524 +1,524 @@ /*************************************************************************** * Copyright (C) 2005-2009 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 "revisiontree.h" #include "../stopdlg.h" #include "../ccontextlistener.h" #include "svnqt/log_entry.h" #include "svnqt/cache/LogCache.h" #include "svnqt/cache/ReposLog.h" #include "svnqt/cache/ReposConfig.h" #include "svnqt/url.h" #include "svnqt/client_parameter.h" #include "revtreewidget.h" #include "revgraphview.h" #include "elogentry.h" #include "svnfrontend/fronthelpers/cursorstack.h" #include "settings/kdesvnsettings.h" #include #include #include #include #define INTERNALCOPY 1 #define INTERNALRENAME 2 class CContextListener; class RtreeData { public: RtreeData(); virtual ~RtreeData(); QMap m_History; svn::LogEntriesMap m_OldHistory; long max_rev, min_rev; QProgressDialog *progress; QTime m_stopTick; QWidget *dlgParent; RevTreeWidget *m_TreeDisplay; svn::ClientP m_Client; CContextListener *m_Listener; bool getLogs(const QString &, const svn::Revision &startr, const svn::Revision &endr, const QString &origin); }; RtreeData::RtreeData() : max_rev(-1), min_rev(-1) { progress = nullptr; m_TreeDisplay = nullptr; dlgParent = nullptr; m_Listener = nullptr; } RtreeData::~RtreeData() { delete progress; } bool RtreeData::getLogs(const QString &reposRoot, const svn::Revision &startr, const svn::Revision &endr, const QString &origin) { Q_UNUSED(origin); if (!m_Listener || !m_Client) { return false; } svn::LogParameter params; params.targets(reposRoot).revisionRange(endr, startr).peg(startr).limit(0).discoverChangedPathes(true).strictNodeHistory(false); const svn::StringArray ex(svn::cache::ReposConfig::self()->readEntry(reposRoot, "tree_exclude_list", QStringList())); try { CursorStack a(Qt::BusyCursor); StopDlg sdlg(m_Listener, dlgParent, i18nc("@title:window", "Logs"), i18n("Getting logs - hit Cancel for abort")); if (svn::Url::isLocal(reposRoot)) { m_Client->log(params.excludeList(ex), m_OldHistory); } else { svn::cache::ReposLog rl(m_Client, reposRoot); if (rl.isValid()) { rl.simpleLog(m_OldHistory, startr, endr, (!Kdesvnsettings::network_on() || !Kdesvnsettings::fill_cache_on_tree()), ex); } else if (Kdesvnsettings::network_on()) { m_Client->log(params.excludeList(ex), m_OldHistory); } else { KMessageBox::error(nullptr, i18n("Could not retrieve logs, reason:\n%1", i18n("No log cache possible due broken database and networking not allowed."))); return false; } } } catch (const svn::Exception &ce) { KMessageBox::error(nullptr, i18n("Could not retrieve logs, reason:\n%1", ce.msg())); return false; } return true; } RevisionTree::RevisionTree(const svn::ClientP &aClient, CContextListener *aListener, const QString &reposRoot, const svn::Revision &startr, const svn::Revision &endr, const QString &origin, const svn::Revision &baserevision, QWidget *parent) : m_InitialRevsion(0), m_Path(origin), m_Valid(false) { m_Data = new RtreeData; m_Data->m_Client = aClient; m_Data->m_Listener = aListener; m_Data->dlgParent = parent; if (!m_Data->getLogs(reposRoot, startr, endr, origin)) { return; } long possible_rev = -1; m_Data->progress = new QProgressDialog(i18n("Scanning the logs for %1", origin), i18n("Cancel"), 0, m_Data->m_OldHistory.size(), parent); m_Data->progress->setWindowTitle(i18nc("@title:window", "Scanning logs")); m_Data->progress->setMinimumDuration(100); m_Data->progress->setAutoClose(false); m_Data->progress->setWindowModality(Qt::WindowModal); bool cancel = false; int count = 0; for (auto it = m_Data->m_OldHistory.begin(); it != m_Data->m_OldHistory.end(); ++it) { m_Data->progress->setValue(count); QCoreApplication::processEvents(); if (m_Data->progress->wasCanceled()) { cancel = true; break; } if (it.key() > m_Data->max_rev) { m_Data->max_rev = it.key(); } if (it.key() < m_Data->min_rev || m_Data->min_rev == -1) { m_Data->min_rev = it.key(); } if (baserevision.kind() == svn_opt_revision_date) { if ((baserevision.date() <= it.value().date && possible_rev == -1) || possible_rev > it.key()) { possible_rev = it.key(); } } ++count; } if (baserevision.kind() == svn_opt_revision_head || baserevision.kind() == svn_opt_revision_working) { m_Baserevision = m_Data->max_rev; } else if (baserevision.kind() == svn_opt_revision_number) { m_Baserevision = baserevision.revnum(); } else if (baserevision.kind() == svn_opt_revision_date) { m_Baserevision = possible_rev; } else { m_Baserevision = m_Data->min_rev; } if (!cancel) { if (topDownScan()) { m_Data->progress->setAutoReset(true); m_Data->progress->setRange(0, 100); m_Data->m_stopTick.restart(); m_Data->m_TreeDisplay = new RevTreeWidget(m_Data->m_Client); if (bottomUpScan(m_InitialRevsion, 0, m_Path, 0)) { m_Valid = true; m_Data->m_TreeDisplay->setBasePath(reposRoot); m_Data->m_TreeDisplay->dumpRevtree(); } else { delete m_Data->m_TreeDisplay; m_Data->m_TreeDisplay = nullptr; } } } m_Data->progress->hide(); } RevisionTree::~RevisionTree() { delete m_Data; } bool RevisionTree::isDeleted(long revision, const QString &path) { for (long i = 0; i < m_Data->m_History[revision].changedPaths.count(); ++i) { if (isParent(m_Data->m_History[revision].changedPaths[i].path, path) && m_Data->m_History[revision].changedPaths[i].action == 'D') { return true; } } return false; } bool RevisionTree::topDownScan() { m_Data->progress->setRange(0, m_Data->max_rev - m_Data->min_rev); bool cancel = false; QString label; QString olabel = m_Data->progress->labelText(); for (long j = m_Data->max_rev; j >= m_Data->min_rev; --j) { m_Data->progress->setValue(m_Data->max_rev - j); QCoreApplication::processEvents(); if (m_Data->progress->wasCanceled()) { cancel = true; break; } for (long i = 0; i < m_Data->m_OldHistory[j].changedPaths.count(); ++i) { if (i > 0 && i % 100 == 0) { if (m_Data->progress->wasCanceled()) { cancel = true; break; } label = i18n("%1
Check change entry %2 of %3", olabel, i, m_Data->m_OldHistory[j].changedPaths.count()); m_Data->progress->setLabelText(label); QCoreApplication::processEvents(); } /* find min revision of item */ if (m_Data->m_OldHistory[j].changedPaths[i].action == 'A' && isParent(m_Data->m_OldHistory[j].changedPaths[i].path, m_Path)) { if (!m_Data->m_OldHistory[j].changedPaths[i].copyFromPath.isEmpty()) { if (m_InitialRevsion < m_Data->m_OldHistory[j].revision) { QString r = m_Path.mid(m_Data->m_OldHistory[j].changedPaths[i].path.length()); m_Path = m_Data->m_OldHistory[j].changedPaths[i].copyFromPath; m_Path += r; } } else if (m_Data->m_OldHistory[j].changedPaths[i].path == m_Path && m_Data->m_OldHistory[j].changedPaths[i].copyToPath.isEmpty()) { // here it is added m_InitialRevsion = m_Data->m_OldHistory[j].revision; } } } } if (cancel == true) { return false; } m_Data->progress->setLabelText(olabel); /* find forward references and filter them out */ for (long j = m_Data->max_rev; j >= m_Data->min_rev; --j) { m_Data->progress->setValue(m_Data->max_rev - j); QCoreApplication::processEvents(); if (m_Data->progress->wasCanceled()) { cancel = true; break; } for (long i = 0; i < m_Data->m_OldHistory[j].changedPaths.count(); ++i) { if (i > 0 && i % 100 == 0) { if (m_Data->progress->wasCanceled()) { cancel = true; break; } label = i18n("%1
Check change entry %2 of %3", olabel, i, m_Data->m_OldHistory[j].changedPaths.count()); m_Data->progress->setLabelText(label); QCoreApplication::processEvents(); } if (!m_Data->m_OldHistory[j].changedPaths[i].copyFromPath.isEmpty()) { long r = m_Data->m_OldHistory[j].changedPaths[i].copyFromRevision; QString sourcepath = m_Data->m_OldHistory[j].changedPaths[i].copyFromPath; char a = m_Data->m_OldHistory[j].changedPaths[i].action; if (m_Data->m_OldHistory[j].changedPaths[i].path.isEmpty()) { continue; } if (a == 'R') { m_Data->m_OldHistory[j].changedPaths[i].action = 0; } else if (a == 'A') { a = INTERNALCOPY; for (long z = 0; z < m_Data->m_OldHistory[j].changedPaths.count(); ++z) { if (m_Data->m_OldHistory[j].changedPaths[z].action == 'D' && isParent(m_Data->m_OldHistory[j].changedPaths[z].path, sourcepath)) { a = INTERNALRENAME; m_Data->m_OldHistory[j].changedPaths[z].action = 0; break; } } m_Data->m_History[r].addCopyTo(sourcepath, m_Data->m_OldHistory[j].changedPaths[i].path, j, a, r); m_Data->m_OldHistory[j].changedPaths[i].action = 0; } } } } if (cancel == true) { return false; } m_Data->progress->setLabelText(olabel); for (long j = m_Data->max_rev; j >= m_Data->min_rev; --j) { m_Data->progress->setValue(m_Data->max_rev - j); QCoreApplication::processEvents(); if (m_Data->progress->wasCanceled()) { cancel = true; break; } for (long i = 0; i < m_Data->m_OldHistory[j].changedPaths.count(); ++i) { if (m_Data->m_OldHistory[j].changedPaths[i].action == 0) { continue; } if (i > 0 && i % 100 == 0) { if (m_Data->progress->wasCanceled()) { cancel = true; break; } label = i18n("%1
Check change entry %2 of %3", olabel, i, m_Data->m_OldHistory[j].changedPaths.count()); m_Data->progress->setLabelText(label); QCoreApplication::processEvents(); } m_Data->m_History[j].addCopyTo(m_Data->m_OldHistory[j].changedPaths[i].path, QString(), -1, m_Data->m_OldHistory[j].changedPaths[i].action); } m_Data->m_History[j].author = m_Data->m_OldHistory[j].author; m_Data->m_History[j].date = m_Data->m_OldHistory[j].date; m_Data->m_History[j].revision = m_Data->m_OldHistory[j].revision; m_Data->m_History[j].message = m_Data->m_OldHistory[j].message; } return !cancel; } bool RevisionTree::isParent(const QString &_par, const QString &tar) { if (_par == tar) { return true; } QString par = _par.endsWith(QLatin1Char('/')) ? _par : _par + QLatin1Char('/'); return tar.startsWith(par); } bool RevisionTree::isValid()const { return m_Valid; } static QString uniqueNodeName(long rev, const QString &path) { QString res = QString::fromUtf8(path.toLocal8Bit().toBase64()); res.replace(QLatin1Char('\"'), QLatin1String("_quot_")); res.replace(QLatin1Char(' '), QLatin1String("_space_")); - QString n; n.sprintf("%05ld", rev); + QString n = QString::asprintf("%05ld", rev); return QLatin1Char('\"') + n + QLatin1Char('_') + res + QLatin1Char('\"'); } bool RevisionTree::bottomUpScan(long startrev, unsigned recurse, const QString &_path, long _last) { #define REVENTRY m_Data->m_History[j] #define FORWARDENTRY m_Data->m_History[j].changedPaths[i] QString path = _path; long lastrev = _last; #ifdef DEBUG_PARSE qCDebug(KDESVN_LOG) << "Searching for " << path << " at revision " << startrev << " recursion " << recurse << endl; #endif bool cancel = false; for (long j = startrev; j <= m_Data->max_rev; ++j) { if (m_Data->m_stopTick.elapsed() > 500) { m_Data->progress->setValue(m_Data->progress->value() + 1); QCoreApplication::processEvents(); m_Data->m_stopTick.restart(); } if (m_Data->progress->wasCanceled()) { cancel = true; break; } for (long i = 0; i < REVENTRY.changedPaths.count(); ++i) { if (!isParent(FORWARDENTRY.path, path)) { continue; } QString n1, n2; if (isParent(FORWARDENTRY.path, path)) { bool get_out = false; if (FORWARDENTRY.path != path) { #ifdef DEBUG_PARSE qCDebug(KDESVN_LOG) << "Parent rename? " << FORWARDENTRY.path << " -> " << FORWARDENTRY.copyToPath << " -> " << FORWARDENTRY.copyFromPath << endl; #endif } if (FORWARDENTRY.action == INTERNALCOPY || FORWARDENTRY.action == INTERNALRENAME) { bool ren = FORWARDENTRY.action == INTERNALRENAME; QString tmpPath = path; QString recPath; if (FORWARDENTRY.copyToPath.length() == 0) { continue; } QString r = path.mid(FORWARDENTRY.path.length()); recPath = FORWARDENTRY.copyToPath; recPath += r; n1 = uniqueNodeName(lastrev, tmpPath); n2 = uniqueNodeName(FORWARDENTRY.copyToRevision, recPath); if (lastrev > 0) { m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[n1].targets.append(RevGraphView::targetData(n2, FORWARDENTRY.action)); } m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[n2].name = recPath; m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[n2].rev = FORWARDENTRY.copyToRevision; m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[n2].Action = FORWARDENTRY.action; m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[n2].Author = m_Data->m_History[FORWARDENTRY.copyToRevision].author; m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[n2].Message = m_Data->m_History[FORWARDENTRY.copyToRevision].message; m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[n2].Date = svn::DateTime(m_Data->m_History[FORWARDENTRY.copyToRevision].date).toString(); if (ren) { lastrev = FORWARDENTRY.copyToRevision; /* skip items between */ #ifdef DEBUG_PARSE qCDebug(KDESVN_LOG) << "Renamed to " << recPath << " at revision " << FORWARDENTRY.copyToRevision << endl; #endif j = lastrev; path = recPath; } else { #ifdef DEBUG_PARSE qCDebug(KDESVN_LOG) << "Copy to " << recPath << endl; #endif if (!bottomUpScan(FORWARDENTRY.copyToRevision, recurse + 1, recPath, FORWARDENTRY.copyToRevision)) { return false; } } } else if (FORWARDENTRY.path == path) { switch (FORWARDENTRY.action) { case 'A': #ifdef DEBUG_PARSE qCDebug(KDESVN_LOG) << "Inserting adding base item" << endl; #endif n1 = uniqueNodeName(j, FORWARDENTRY.path); m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[n1].Action = FORWARDENTRY.action; fillItem(j, i, n1, path); lastrev = j; break; case 'M': case 'R': #ifdef DEBUG_PARSE qCDebug(KDESVN_LOG) << "Item modified at revision " << j << " recurse " << recurse << endl; #endif n1 = uniqueNodeName(j, FORWARDENTRY.path); n2 = uniqueNodeName(lastrev, FORWARDENTRY.path); if (lastrev > 0) { m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[n2].targets.append(RevGraphView::targetData(n1, FORWARDENTRY.action)); } fillItem(j, i, n1, path); /* modify of same item (in same recurse) should be only once at a revision * so check if lastrev==j must not be done but will cost cpu ticks so I always * set trev and lastrev. */ lastrev = j; break; case 'D': #ifdef DEBUG_PARSE qCDebug(KDESVN_LOG) << "(Sloppy match) Item deleted at revision " << j << " recurse " << recurse << endl; #endif n1 = uniqueNodeName(j, path); n2 = uniqueNodeName(lastrev, path); if (n1 == n2) { /* cvs import - copy and deletion at same revision. * CVS sucks. */ n1 = uniqueNodeName(j, "D_" + path); } if (lastrev > 0) { m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[n2].targets.append(RevGraphView::targetData(n1, FORWARDENTRY.action)); } fillItem(j, i, n1, path); lastrev = j; get_out = true; break; default: break; } } else { switch (FORWARDENTRY.action) { case 'D': #ifdef DEBUG_PARSE qCDebug(KDESVN_LOG) << "(Exact match) Item deleted at revision " << j << " recurse " << recurse << endl; #endif n1 = uniqueNodeName(j, path); n2 = uniqueNodeName(lastrev, path); if (n1 == n2) { /* cvs import - copy and deletion at same revision. * CVS sucks. */ n1 = uniqueNodeName(j, "D_" + path); } if (lastrev > 0) { m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[n2].targets.append(RevGraphView::targetData(n1, FORWARDENTRY.action)); } fillItem(j, i, n1, path); lastrev = j; get_out = true; break; default: break; } } if (get_out) { return true; } } } } return !cancel; } RevTreeWidget *RevisionTree::getView() { return m_Data->m_TreeDisplay; } void RevisionTree::fillItem(long rev, int pathIndex, const QString &nodeName, const QString &path) { m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[nodeName].name = path; m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[nodeName].rev = rev; if (pathIndex >= 0) { m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[nodeName].Action = m_Data->m_History[rev].changedPaths[pathIndex].action; m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[nodeName].Author = m_Data->m_History[rev].author; m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[nodeName].Message = m_Data->m_History[rev].message; m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[nodeName].Date = svn::DateTime(m_Data->m_History[rev].date).toString(); } else { m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[nodeName].Action = 0; m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[nodeName].Author.clear(); m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[nodeName].Message.clear(); m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[nodeName].Date = svn::DateTime(0).toString(); } } diff --git a/src/svnfrontend/opencontextmenu.cpp b/src/svnfrontend/opencontextmenu.cpp index 70ed0044..5a18e7c9 100644 --- a/src/svnfrontend/opencontextmenu.cpp +++ b/src/svnfrontend/opencontextmenu.cpp @@ -1,79 +1,79 @@ /*************************************************************************** * Copyright (C) 2006-2009 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 "opencontextmenu.h" #include #include #include #include OpenContextmenu::OpenContextmenu(const QUrl &aPath, const KService::List &aList, QWidget *parent) : QMenu(parent) , m_Path(aPath) , m_List(aList) { setup(); } OpenContextmenu::~OpenContextmenu() { } void OpenContextmenu::setup() { m_mapPopup.clear(); QStringList _found; for (const KService::Ptr &ptr : qAsConst(m_List)) { if (_found.contains(ptr->name())) { continue; } _found.append(ptr->name()); QString actionName(ptr->name().replace(QLatin1Char('&'), QLatin1String("&&"))); - QAction *act = addAction(SmallIcon(ptr->icon()), actionName); + QAction *act = addAction(QIcon::fromTheme(ptr->icon()), actionName); act->setData(m_mapPopup.size()); m_mapPopup.push_back(ptr); } connect(this, &QMenu::triggered, this, &OpenContextmenu::slotRunService); if (!m_List.isEmpty()) { addSeparator(); } QAction *act = new QAction(i18n("Other..."), this); act->setData(-1); addAction(act); } void OpenContextmenu::slotRunService(QAction *act) { const int idx = act->data().toInt(); if (idx >= 0 && idx < m_mapPopup.size()) { KRun::runService(*m_mapPopup.at(idx), {m_Path}, parentWidget()); } else { slotOpenWith(); } } void OpenContextmenu::slotOpenWith() { QList lst; lst.append(m_Path); KRun::displayOpenWithDialog({m_Path}, parentWidget()); } diff --git a/src/svnfrontend/stopdlg.h b/src/svnfrontend/stopdlg.h index d32d56a4..06ed8f6f 100644 --- a/src/svnfrontend/stopdlg.h +++ b/src/svnfrontend/stopdlg.h @@ -1,83 +1,83 @@ /*************************************************************************** * Copyright (C) 2005-2009 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. * ***************************************************************************/ #ifndef STOPDLG_H #define STOPDLG_H #include "svnfrontend/fronthelpers/cursorstack.h" #include -#include +#include class QDialogButtonBox; class QTimer; class QLabel; class QProgressBar; class QTextBrowser; class QHideEvent; class QShowEvent; class QVBoxLayout; class CContextListener; /** @author Rajko Albrecht */ class StopDlg : public QDialog { Q_OBJECT public: StopDlg(CContextListener *listener, QWidget *parent, const QString &caption, const QString &text); ~StopDlg(); protected: int m_MinDuration; bool mCancelled; QTimer *mShowTimer; bool mShown, mWait; QLabel *mLabel; QProgressBar *m_ProgressBar; QProgressBar *m_NetBar; bool m_BarShown; bool m_netBarShown; - QTime m_StopTick; + QElapsedTimer m_StopTick; QTextBrowser *m_LogWindow; QVBoxLayout *layout, *mainLayout; QString m_lastLog; unsigned int m_lastLogLines; CursorStack *cstack; QDialogButtonBox *m_bBox; void showEvent(QShowEvent *e) override; void hideEvent(QHideEvent *e) override; public slots: virtual void slotTick(); virtual void slotWait(bool); virtual void slotExtraMessage(const QString &); protected slots: virtual void slotAutoShow(); virtual void slotCancel(); virtual void slotNetProgres(long long int, long long int); signals: void sigCancel(bool how); }; #endif diff --git a/src/svnfrontend/svnactions.cpp b/src/svnfrontend/svnactions.cpp index cf98ed8e..d304a158 100644 --- a/src/svnfrontend/svnactions.cpp +++ b/src/svnfrontend/svnactions.cpp @@ -1,2915 +1,2914 @@ /*************************************************************************** * Copyright (C) 2005-2009 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 "svnactions.h" #include "checkoutinfo_impl.h" #include "itemdisplay.h" #include "svnitem.h" #include "rangeinput_impl.h" #include "propertiesdlg.h" #include "ccontextlistener.h" #include "tcontextlistener.h" #include "modifiedthread.h" #include "fillcachethread.h" #include "svnlogdlgimp.h" #include "stopdlg.h" #include "blamedisplay.h" #include "ksvnwidgets/commitmsg_impl.h" #include "ksvnwidgets/models/commitmodelhelper.h" #include "ksvnwidgets/diffbrowser.h" #include "ksvnwidgets/encodingselector_impl.h" #include "ksvnwidgets/revertform.h" #include "graphtree/revisiontree.h" #include "graphtree/revtreewidget.h" #include "settings/kdesvnsettings.h" #include "svnqt/client.h" #include "svnqt/annotate_line.h" #include "svnqt/context_listener.h" #include "svnqt/dirent.h" #include "svnqt/targets.h" #include "svnqt/url.h" #include "svnqt/svnqttypes.h" #include "svnqt/svnqt_defines.h" #include "svnqt/client_parameter.h" #include "svnqt/client_commit_parameter.h" #include "svnqt/client_annotate_parameter.h" #include "svnqt/client_update_parameter.h" #include "svnqt/cache/LogCache.h" #include "svnqt/cache/ReposLog.h" #include "svnqt/cache/ReposConfig.h" #include "fronthelpers/watchedprocess.h" #include "helpers/stringhelper.h" #include "helpers/kdesvn_debug.h" #include "helpers/ktranslateurl.h" #include "helpers/windowgeometryhelper.h" #include "fronthelpers/cursorstack.h" #include "cacheentry.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /// @todo has to be removed for a real fix of ticket #613 #include // wait not longer than 10 seconds for a thread #define MAX_THREAD_WAITTIME 10000 class SvnActionsData { public: SvnActionsData() : m_ParentList(nullptr) , m_SvnContextListener(nullptr) , m_Svnclient(svn::Client::getobject(svn::ContextP())) , runblocked(false) { } ~SvnActionsData() { cleanDialogs(); delete m_SvnContextListener; } static bool isExternalDiff() { if (Kdesvnsettings::use_external_diff()) { const QString edisp = Kdesvnsettings::external_diff_display(); const QVector wlist = edisp.splitRef(QLatin1Char(' ')); if (wlist.count() >= 3 && edisp.contains(QLatin1String("%1")) && edisp.contains(QLatin1String("%2"))) { return true; } } return false; } void clearCaches() { QWriteLocker wl(&(m_InfoCacheLock)); m_PropertiesCache.clear(); m_contextData.clear(); m_InfoCache.clear(); } void cleanDialogs() { if (m_DiffDialog) { delete m_DiffDialog; m_DiffDialog = nullptr; } if (m_LogDialog) { m_LogDialog->saveSize(); delete m_LogDialog; m_LogDialog = nullptr; } } /// @todo set some standards options to svn::Context. This should made via a Config class in svnqt (future release 1.4) /// this is a workaround for ticket #613 void setStandards() { if (!m_CurrentContext) { return; } svn_config_t *cfg_config = static_cast(apr_hash_get(m_CurrentContext->ctx()->config, SVN_CONFIG_CATEGORY_CONFIG, APR_HASH_KEY_STRING)); if (!cfg_config) { return; } svn_config_set(cfg_config, SVN_CONFIG_SECTION_HELPERS, SVN_CONFIG_OPTION_DIFF_CMD, nullptr); } ItemDisplay *m_ParentList; CContextListener *m_SvnContextListener; svn::ContextP m_CurrentContext; svn::ClientP m_Svnclient; helpers::statusCache m_UpdateCache; helpers::statusCache m_Cache; helpers::statusCache m_conflictCache; helpers::statusCache m_repoLockCache; helpers::itemCache m_PropertiesCache; /// \todo as persistent cache (sqlite?) helpers::itemCache m_InfoCache; helpers::itemCache m_MergeInfoCache; QPointer m_DiffBrowserPtr; QPointer m_DiffDialog; QPointer m_LogDialog; QMap m_contextData; QReadWriteLock m_InfoCacheLock; bool runblocked; }; #define EMIT_FINISHED emit sendNotify(i18n("Finished")) #define EMIT_REFRESH emit sigRefreshAll() #define DIALOGS_SIZES "display_dialogs_sizes" SvnActions::SvnActions(ItemDisplay *parent, bool processes_blocked) : QObject(parent ? parent->realWidget() : nullptr) , SimpleLogCb() , m_CThread(nullptr) , m_UThread(nullptr) , m_FCThread(nullptr) { m_Data.reset(new SvnActionsData); m_Data->m_ParentList = parent; m_Data->m_SvnContextListener = new CContextListener(this); m_Data->runblocked = processes_blocked; connect(m_Data->m_SvnContextListener, &CContextListener::sendNotify, this, &SvnActions::slotNotifyMessage); } svn::ClientP SvnActions::svnclient() { return m_Data->m_Svnclient; } SvnActions::~SvnActions() { killallThreads(); } void SvnActions::slotNotifyMessage(const QString &aMsg) { emit sendNotify(aMsg); } void SvnActions::reInitClient() { m_Data->clearCaches(); m_Data->cleanDialogs(); if (m_Data->m_CurrentContext) { m_Data->m_CurrentContext->setListener(nullptr); } m_Data->m_CurrentContext = svn::ContextP(new svn::Context); m_Data->m_CurrentContext->setListener(m_Data->m_SvnContextListener); m_Data->m_Svnclient->setContext(m_Data->m_CurrentContext); ///@todo workaround has to be replaced m_Data->setStandards(); } void SvnActions::makeLog(const svn::Revision &start, const svn::Revision &end, const svn::Revision &peg, const QString &which, bool follow, bool list_files, int limit) { svn::LogEntriesMapPtr logs = getLog(start, end, peg, which, list_files, limit, follow); if (!logs) { return; } svn::InfoEntry info; if (!singleInfo(which, peg, info)) { return; } const QString reposRoot = info.reposRoot().toString(); bool need_modal = m_Data->runblocked || QApplication::activeModalWidget() != nullptr; if (need_modal || !m_Data->m_LogDialog) { m_Data->m_LogDialog = new SvnLogDlgImp(this, need_modal); connect(m_Data->m_LogDialog, &SvnLogDlgImp::makeDiff, this, QOverload::of(&SvnActions::makeDiff)); connect(m_Data->m_LogDialog, &SvnLogDlgImp::makeCat, this, &SvnActions::slotMakeCat); } if (m_Data->m_LogDialog) { m_Data->m_LogDialog->dispLog(logs, info.url().toString().mid(reposRoot.length()), reposRoot, ( peg == svn::Revision::UNDEFINED ? (svn::Url::isValid(which) ? svn::Revision::HEAD : svn::Revision::UNDEFINED) : peg ), which); if (need_modal) { m_Data->m_LogDialog->exec(); m_Data->m_LogDialog->saveSize(); delete m_Data->m_LogDialog; } else { m_Data->m_LogDialog->show(); m_Data->m_LogDialog->raise(); } } EMIT_FINISHED; } svn::LogEntriesMapPtr SvnActions::getLog(const svn::Revision &start, const svn::Revision &end, const svn::Revision &peg, const QString &which, bool list_files, int limit, QWidget *parent) { return getLog(start, end, peg, which, list_files, limit, Kdesvnsettings::log_follows_nodes(), parent); } svn::LogEntriesMapPtr SvnActions::getLog(const svn::Revision &start, const svn::Revision &end, const svn::Revision &peg, const QString &which, bool list_files, int limit, bool follow, QWidget *parent) { svn::LogEntriesMapPtr logs; if (!m_Data->m_CurrentContext) { return logs; } bool mergeinfo = hasMergeInfo(m_Data->m_ParentList->baseUri().isEmpty() ? which : m_Data->m_ParentList->baseUri()); svn::LogParameter params; params.targets(which).revisionRange(start, end).peg(peg).includeMergedRevisions(mergeinfo).limit(limit).discoverChangedPathes(list_files).strictNodeHistory(!follow); try { StopDlg sdlg(m_Data->m_SvnContextListener, (parent ? parent : m_Data->m_ParentList->realWidget()), i18nc("@title:window", "Logs"), i18n("Getting logs - hit Cancel for abort")); connect(this, &SvnActions::sigExtraLogMsg, &sdlg, &StopDlg::slotExtraMessage); logs = svn::LogEntriesMapPtr(new svn::LogEntriesMap); if (doNetworking()) { if (!m_Data->m_Svnclient->log(params, *logs)) { logs.clear(); return logs; } } else { svn::InfoEntry e; if (!singleInfo(m_Data->m_ParentList->baseUri(), svn::Revision::BASE, e)) { logs.clear(); return logs; } if (svn::Url::isLocal(e.reposRoot().toString())) { if (!m_Data->m_Svnclient->log(params, *logs)) { logs.clear(); return logs; } } else { svn::cache::ReposLog rl(m_Data->m_Svnclient, e.reposRoot().toString()); QString what; const QString s1 = e.url().toString().mid(e.reposRoot().toString().length()); if (which == QLatin1String(".")) { what = s1; } else { const QString s2 = which.mid(m_Data->m_ParentList->baseUri().length()); what = s1 + QLatin1Char('/') + s2; } rl.log(what, start, end, peg, *logs, !follow, limit); } } } catch (const svn::Exception &e) { emit clientException(e.msg()); logs.clear(); } if (logs && logs->isEmpty()) { logs.clear(); emit clientException(i18n("Got no logs")); } return logs; } bool SvnActions::getSingleLog(svn::LogEntry &t, const svn::Revision &r, const QString &what, const svn::Revision &peg, QString &root) { bool res = false; if (what.isEmpty()) { return res; } if (root.isEmpty()) { svn::InfoEntry inf; if (!singleInfo(what, peg, inf)) { return res; } root = inf.reposRoot().toString(); } if (!svn::Url::isLocal(root)) { svn::LogEntriesMap _m; try { svn::cache::ReposLog rl(m_Data->m_Svnclient , root); if (rl.isValid() && rl.simpleLog(_m, r, r, true)) { const svn::LogEntriesMap::const_iterator it = _m.constFind(r.revnum()); if (it != _m.constEnd()) { t = it.value(); res = true; } } } catch (const svn::Exception &e) { emit clientException(e.msg()); } } if (!res) { svn::LogEntriesMapPtr log = getLog(r, r, peg, root, true, 1); if (log) { const svn::LogEntriesMap::const_iterator it = log->constFind(r.revnum()); if (it != log->constEnd()) { t = it.value(); res = true; } } } return res; } bool SvnActions::hasMergeInfo(const QString &originpath) { QVariant _m(false); QString path; svn::InfoEntry e; if (!singleInfo(originpath, svn::Revision::UNDEFINED, e)) { return false; } path = e.reposRoot().toString(); if (!m_Data->m_MergeInfoCache.findSingleValid(path, _m)) { bool mergeinfo; try { mergeinfo = m_Data->m_Svnclient->RepoHasCapability(path, svn::CapabilityMergeinfo); } catch (const svn::ClientException &e) { emit sendNotify(e.msg()); return false; } _m.setValue(mergeinfo); m_Data->m_MergeInfoCache.insertKey(_m, path); } return _m.toBool(); } bool SvnActions::singleInfo(const QString &what, const svn::Revision &_rev, svn::InfoEntry &target, const svn::Revision &_peg) { QString url; QString cacheKey; - QTime d; d.start(); svn::Revision peg = _peg; if (!m_Data->m_CurrentContext) { return false; } #ifdef DEBUG_TIMER QTime _counttime; _counttime.start(); #endif if (!svn::Url::isValid(what)) { // working copy // url = svn::Wc::getUrl(what); url = what; if (_rev != svn::Revision::WORKING && url.contains(QLatin1Char('@'))) { url += QStringLiteral("@BASE"); } peg = svn::Revision::UNDEFINED; cacheKey = url; } else { // valid url QUrl _uri(what); QString prot = svn::Url::transformProtokoll(_uri.scheme()); _uri.setScheme(prot); url = _uri.toString(); if (peg == svn::Revision::UNDEFINED) { peg = _rev; } if (peg == svn::Revision::UNDEFINED) { peg = svn::Revision::HEAD; } cacheKey = _rev.toString() + QLatin1Char('/') + url; } svn::InfoEntries e; bool must_write = false; { QReadLocker rl(&(m_Data->m_InfoCacheLock)); if (cacheKey.isEmpty() || !m_Data->m_InfoCache.findSingleValid(cacheKey, target)) { must_write = true; try { e = (m_Data->m_Svnclient->info(url, svn::DepthEmpty, _rev, peg)); } catch (const svn::Exception &ce) { qCDebug(KDESVN_LOG) << "single info: " << ce.msg() << endl; emit clientException(ce.msg()); return false; } if (e.isEmpty() || e[0].reposRoot().isEmpty()) { emit clientException(i18n("Got no info.")); return false; } target = e[0]; } } if (must_write) { QWriteLocker wl(&(m_Data->m_InfoCacheLock)); if (!cacheKey.isEmpty()) { m_Data->m_InfoCache.insertKey(e[0], cacheKey); if (peg != svn::Revision::UNDEFINED && peg.kind() != svn::Revision::NUMBER && peg.kind() != svn::Revision::DATE) { // for persistent storage, store head into persistent cache makes no sense. cacheKey = e[0].revision().toString() + QLatin1Char('/') + url; m_Data->m_InfoCache.insertKey(e[0], cacheKey); } } } #ifdef DEBUG_TIMER qCDebug(KDESVN_LOG) << "Time getting info for " << cacheKey << ": " << _counttime.elapsed(); #endif return true; } void SvnActions::makeTree(const QString &what, const svn::Revision &_rev, const svn::Revision &startr, const svn::Revision &endr) { svn::InfoEntry info; if (!singleInfo(what, _rev, info)) { return; } const QString reposRoot = info.reposRoot().toString(); if (Kdesvnsettings::fill_cache_on_tree()) { stopFillCache(); } QPointer dlg(new KSvnSimpleOkDialog(QStringLiteral("revisiontree_dlg"), m_Data->m_ParentList->realWidget())); dlg->setWindowTitle(i18nc("@title:window", "History of %1", info.url().toString().mid(reposRoot.length()))); RevisionTree *rt(new RevisionTree(m_Data->m_Svnclient, m_Data->m_SvnContextListener, reposRoot, startr, endr, info.url().toString().mid(reposRoot.length()), _rev, dlg)); if (rt->isValid()) { RevTreeWidget *disp = rt->getView(); if (disp) { dlg->addWidget(disp); connect(disp, &RevTreeWidget::makeNorecDiff, this, &SvnActions::makeNorecDiff); connect(disp, &RevTreeWidget::makeRecDiff, this, QOverload::of(&SvnActions::makeDiff)); connect(disp, &RevTreeWidget::makeCat, this, &SvnActions::slotMakeCat); dlg->exec(); } } delete dlg; } void SvnActions::makeBlame(const svn::Revision &start, const svn::Revision &end, SvnItem *k) { if (k) { makeBlame(start, end, k->fullName(), m_Data->m_ParentList->realWidget()); } } void SvnActions::makeBlame(const svn::Revision &start, const svn::Revision &end, const QString &k, QWidget *_p, const svn::Revision &_peg, SimpleLogCb *_acb) { if (!m_Data->m_CurrentContext) { return; } svn::AnnotatedFile blame; QWidget *_parent = _p ? _p : m_Data->m_ParentList->realWidget(); bool mergeinfo = hasMergeInfo(m_Data->m_ParentList->baseUri().isEmpty() ? k : m_Data->m_ParentList->baseUri()); svn::AnnotateParameter params; params.path(k).pegRevision(_peg == svn::Revision::UNDEFINED ? end : _peg).revisionRange(svn::RevisionRange(start, end)).includeMerged(mergeinfo); try { CursorStack a(Qt::BusyCursor); StopDlg sdlg(m_Data->m_SvnContextListener, _parent, i18nc("@title:window", "Annotate"), i18n("Annotate lines - hit Cancel for abort")); connect(this, &SvnActions::sigExtraLogMsg, &sdlg, &StopDlg::slotExtraMessage); m_Data->m_Svnclient->annotate(blame, params); } catch (const svn::Exception &e) { emit clientException(e.msg()); return; } if (blame.isEmpty()) { QString ex = i18n("Got no annotate"); emit clientException(ex); return; } EMIT_FINISHED; BlameDisplay::displayBlame(_acb ? _acb : this, k, blame, _p); } bool SvnActions::makeGet(const svn::Revision &start, const QString &what, const QString &target, const svn::Revision &peg, QWidget *_dlgparent) { if (!m_Data->m_CurrentContext) { return false; } CursorStack a(Qt::BusyCursor); QWidget *dlgp = _dlgparent ? _dlgparent : m_Data->m_ParentList->realWidget(); svn::Path p(what); try { StopDlg sdlg(m_Data->m_SvnContextListener, dlgp, i18nc("@title:window", "Content Get"), i18n("Getting content - hit Cancel for abort")); connect(this, &SvnActions::sigExtraLogMsg, &sdlg, &StopDlg::slotExtraMessage); m_Data->m_Svnclient->get(p, target, start, peg); } catch (const svn::Exception &e) { emit clientException(e.msg()); return false; } catch (...) { QString ex = i18n("Error getting content"); emit clientException(ex); return false; } return true; } void SvnActions::slotMakeCat(const svn::Revision &start, const QString &what, const QString &disp, const svn::Revision &peg, QWidget *_dlgparent) { QTemporaryFile content; content.setAutoRemove(true); // required otherwise it will not generate a unique name... if (!content.open()) { emit clientException(i18n("Error while open temporary file")); return; } QString tname = content.fileName(); content.close(); QWidget *parent = _dlgparent ? _dlgparent : m_Data->m_ParentList->realWidget(); if (!makeGet(start, what, tname, peg, parent)) { return; } EMIT_FINISHED; QMimeDatabase db; const QMimeType mimeType(db.mimeTypeForFile(tname)); KService::List offers = KMimeTypeTrader::self()->query(mimeType.name(), QLatin1String("Application"), QLatin1String("Type == 'Application' or (exist Exec)")); if (offers.isEmpty() || offers.first()->exec().isEmpty()) { offers = KMimeTypeTrader::self()->query(mimeType.name(), QLatin1String("Application"), QLatin1String("Type == 'Application'")); } KService::List::ConstIterator it = offers.constBegin(); for (const auto &offer : qAsConst(offers)) { if (offer->noDisplay()) { continue; } break; } if (it != offers.constEnd()) { content.setAutoRemove(false); KRun::runService(**it, QList() << QUrl::fromLocalFile(tname), QApplication::activeWindow(), true); return; } QFile file(tname); file.open(QIODevice::ReadOnly); const QByteArray co = file.readAll(); if (!co.isEmpty()) { QPointer dlg = new KSvnSimpleOkDialog(QStringLiteral("cat_display_dlg"), parent); dlg->setWindowTitle(i18nc("@title:window", "Content of %1", disp)); QTextBrowser *ptr = new QTextBrowser(dlg); ptr->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); ptr->setWordWrapMode(QTextOption::NoWrap); ptr->setReadOnly(true); ptr->setText(QString::fromUtf8(co, co.size())); dlg->addWidget(ptr); dlg->exec(); delete dlg; } else { KMessageBox::information(parent, i18n("Got no content.")); } } bool SvnActions::makeMkdir(const svn::Targets &targets, const QString &logMessage) { if (!m_Data->m_CurrentContext || targets.targets().isEmpty()) { return false; } try { m_Data->m_Svnclient->mkdir(targets, logMessage); } catch (const svn::Exception &e) { emit clientException(e.msg()); return false; } return true; } QString SvnActions::makeMkdir(const QString &parentDir) { if (!m_Data->m_CurrentContext) { return QString(); } bool isOk = false; const QString ex = QInputDialog::getText(m_Data->m_ParentList->realWidget(), i18n("New folder"), i18n("Enter folder name:"), QLineEdit::Normal, QString(), &isOk); if (!isOk || ex.isEmpty()) { return QString(); } svn::Path target(parentDir); target.addComponent(ex); try { m_Data->m_Svnclient->mkdir(target, QString()); } catch (const svn::Exception &e) { emit clientException(e.msg()); return QString(); } return target.path(); } QString SvnActions::getInfo(const SvnItemList &lst, const svn::Revision &rev, const svn::Revision &peg, bool recursive, bool all) { QString res; for (const SvnItem *item : lst) { if (all) { res += QStringLiteral("

%1

").arg(item->fullName()); } res += getInfo(item->fullName(), rev, peg, recursive, all); } return res; } QString SvnActions::getInfo(const QString &_what, const svn::Revision &rev, const svn::Revision &peg, bool recursive, bool all) { if (!m_Data->m_CurrentContext) { return QString(); } svn::InfoEntries entries; if (recursive) { try { StopDlg sdlg(m_Data->m_SvnContextListener, m_Data->m_ParentList->realWidget(), i18nc("@title:window", "Details"), i18n("Retrieving information - hit Cancel for abort")); connect(this, &SvnActions::sigExtraLogMsg, &sdlg, &StopDlg::slotExtraMessage); QString path = _what; if (_what.contains(QLatin1Char('@')) && !svn::Url::isValid(_what)) { path += QLatin1String("@BASE"); } entries = (m_Data->m_Svnclient->info(path, recursive ? svn::DepthInfinity : svn::DepthEmpty, rev, peg)); } catch (const svn::Exception &e) { emit clientException(e.msg()); return QString(); } } else { svn::InfoEntry info; if (!singleInfo(_what, rev, info, peg)) { return QString(); } entries.append(info); } return getInfo(entries, _what, all); } QString SvnActions::getInfo(const svn::InfoEntries &entries, const QString &_what, bool all) { QString text; static QString rb(QStringLiteral("")); static QString re(QStringLiteral("\n")); static QString cs(QStringLiteral(":")); unsigned int val = 0; for (const svn::InfoEntry &entry : entries) { if (val > 0) { text += QStringLiteral(""); } ++val; text += QStringLiteral("

"); text += QStringLiteral(""); if (!entry.Name().isEmpty()) { text += rb + i18n("Name") + cs + (entry.Name()) + re; } if (all) { text += rb + i18n("URL") + cs + (entry.url().toDisplayString()) + re; if (!entry.reposRoot().toString().isEmpty()) { text += rb + i18n("Canonical repository URL") + cs + (entry.reposRoot().toDisplayString()) + re; } if (!entry.checksum().isEmpty()) { text += rb + i18n("Checksum") + cs + (entry.checksum()) + re; } } text += rb + i18n("Type") + cs; switch (entry.kind()) { case svn_node_none: text += i18n("Absent"); break; case svn_node_file: text += i18n("File"); break; case svn_node_dir: text += i18n("Folder"); break; case svn_node_unknown: default: text += i18n("Unknown"); break; } text += re; if (entry.kind() == svn_node_file) { text += rb + i18n("Size") + cs; if (entry.size() != svn::InfoEntry::SVNQT_SIZE_UNKNOWN) { text += helpers::ByteToString(entry.size()); } else if (entry.working_size() != svn::InfoEntry::SVNQT_SIZE_UNKNOWN) { text += helpers::ByteToString(entry.working_size()); } text += re; } if (all) { text += rb + i18n("Schedule") + cs; switch (entry.Schedule()) { case svn_wc_schedule_normal: text += i18n("Normal"); break; case svn_wc_schedule_add: text += i18n("Addition"); break; case svn_wc_schedule_delete: text += i18n("Deletion"); break; case svn_wc_schedule_replace: text += i18n("Replace"); break; default: text += i18n("Unknown"); break; } text += re; text += rb + i18n("UUID") + cs + (entry.uuid()) + re; } text += rb + i18n("Last author") + cs + (entry.cmtAuthor()) + re; if (entry.cmtDate().IsValid()) { text += rb + i18n("Last committed") + cs + entry.cmtDate().toString() + re; } text += rb + i18n("Last revision") + cs + entry.cmtRev().toString() + re; if (entry.textTime().IsValid()) { text += rb + i18n("Content last changed") + cs + entry.textTime().toString() + re; } if (all) { if (entry.propTime().IsValid()) { text += rb + i18n("Property last changed") + cs + entry.propTime().toString() + re; } for (const auto & _cfi : entry.conflicts()) { text += rb + i18n("New version of conflicted file") + cs + (_cfi->theirFile()); } if (entry.prejfile().length()) { text += rb + i18n("Property reject file") + cs + (entry.prejfile()) + re; } if (!entry.copyfromUrl().isEmpty()) { text += rb + i18n("Copy from URL") + cs + (entry.copyfromUrl().toDisplayString()) + re; } if (entry.lockEntry().Locked()) { text += rb + i18n("Lock token") + cs + (entry.lockEntry().Token()) + re; text += rb + i18n("Owner") + cs + (entry.lockEntry().Owner()) + re; text += rb + i18n("Locked on") + cs + entry.lockEntry().Date().toString() + re; text += rb + i18n("Lock comment") + cs + entry.lockEntry().Comment() + re; } else { svn::StatusPtr d; if (checkReposLockCache(_what, d) && d && d->lockEntry().Locked()) { text += rb + i18n("Lock token") + cs + (d->lockEntry().Token()) + re; text += rb + i18n("Owner") + cs + (d->lockEntry().Owner()) + re; text += rb + i18n("Locked on") + cs + d->lockEntry().Date().toString() + re; text += rb + i18n("Lock comment") + cs + d->lockEntry().Comment() + re; } } } text += QStringLiteral("

\n"); } return text; } void SvnActions::makeInfo(const SvnItemList &lst, const svn::Revision &rev, const svn::Revision &peg, bool recursive) { QStringList infoList; infoList.reserve(lst.size()); for (const auto item : lst) { const QString text = getInfo(item->fullName(), rev, peg, recursive, true); if (!text.isEmpty()) { infoList += text; } } showInfo(infoList); } void SvnActions::makeInfo(const QStringList &lst, const svn::Revision &rev, const svn::Revision &peg, bool recursive) { QStringList infoList; infoList.reserve(lst.size()); for (const QString &l : lst) { const QString text = getInfo(l, rev, peg, recursive, true); if (!text.isEmpty()) { infoList += text; } } showInfo(infoList); } void SvnActions::showInfo(const QStringList &infoList) { if (infoList.isEmpty()) { return; } QString text(QLatin1String("")); for (const QString &info : infoList) { text += QLatin1String("

") + info + QLatin1String("

"); } text += QLatin1String(""); QPointer dlg = new KSvnSimpleOkDialog(QStringLiteral("info_dialog"), QApplication::activeModalWidget()); dlg->setWindowTitle(i18nc("@title:window", "Infolist")); QTextBrowser *ptr = new QTextBrowser(dlg); dlg->addWidget(ptr); ptr->setReadOnly(true); ptr->setText(text); dlg->exec(); delete dlg; } void SvnActions::editProperties(SvnItem *k, const svn::Revision &rev) { if (!m_Data->m_CurrentContext) { return; } if (!k) { return; } QPointer dlg(new PropertiesDlg(k, svnclient(), rev)); connect(dlg, SIGNAL(clientException(QString)), m_Data->m_ParentList->realWidget(), SLOT(slotClientException(QString))); if (dlg->exec() != QDialog::Accepted) { delete dlg; return; } svn::PropertiesMap setList; QStringList delList; dlg->changedItems(setList, delList); changeProperties(setList, delList, k->fullName()); k->refreshStatus(); EMIT_FINISHED; delete dlg; } bool SvnActions::changeProperties(const svn::PropertiesMap &setList, const QStringList &delList, const QString &path, const svn::Depth &depth) { try { svn::PropertiesParameter params; params.path(path).depth(depth); StopDlg sdlg(m_Data->m_SvnContextListener, m_Data->m_ParentList->realWidget(), i18nc("@title:window", "Applying Properties"), i18n("
Applying
hit cancel for abort
")); connect(this, &SvnActions::sigExtraLogMsg, &sdlg, &StopDlg::slotExtraMessage); // propertyValue == QString::null -> delete property for (int pos = 0; pos < delList.size(); ++pos) { m_Data->m_Svnclient->propset(params.propertyName(delList.at(pos))); } for (svn::PropertiesMap::ConstIterator it = setList.begin(); it != setList.end(); ++it) { m_Data->m_Svnclient->propset(params.propertyName(it.key()).propertyValue(it.value())); } } catch (const svn::Exception &e) { emit clientException(e.msg()); return false; } return true; } /*! \fn SvnActions::slotCommit() */ void SvnActions::doCommit(const SvnItemList &which) { if (!m_Data->m_CurrentContext || !m_Data->m_ParentList->isWorkingCopy()) { return; } svn::Paths targets; if (which.isEmpty()) { targets.push_back(svn::Path(QStringLiteral("."))); } else { targets.reserve(which.size()); for (const SvnItem *item : which) { targets.push_back(svn::Path( m_Data->m_ParentList->relativePath(item) )); } } if (!m_Data->m_ParentList->baseUri().isEmpty()) { if (!QDir::setCurrent(m_Data->m_ParentList->baseUri())) { QString msg = i18n("Could not change to folder %1\n", m_Data->m_ParentList->baseUri()) + QString::fromLocal8Bit(strerror(errno)); emit sendNotify(msg); } } if (makeCommit(svn::Targets(targets)) && Kdesvnsettings::log_cache_on_open()) { startFillCache(m_Data->m_ParentList->baseUri(), true); } } bool SvnActions::makeCommit(const svn::Targets &targets) { bool ok, keeplocks; svn::Depth depth; svn::Revision nnum; bool review = Kdesvnsettings::review_commit(); QString msg; if (!doNetworking()) { emit clientException(i18n("Not commit because networking is disabled")); return false; } svn::CommitParameter commit_parameters; stopFillCache(); if (!review) { msg = Commitmsg_impl::getLogmessage(&ok, &depth, &keeplocks, m_Data->m_ParentList->realWidget()); if (!ok) { return false; } commit_parameters.targets(targets); } else { CommitActionEntries _check, _uncheck, _result; svn::StatusEntries _Cache; depth = svn::DepthEmpty; svn::StatusParameter params; params.depth(svn::DepthInfinity).all(false).update(false).noIgnore(false).revision(svn::Revision::HEAD); /// @todo filter out double entries for (const auto &tgt : targets.targets()) { try { StopDlg sdlg(m_Data->m_SvnContextListener, m_Data->m_ParentList->realWidget(), i18nc("@title:window", "Status / List"), i18n("Creating list / check status")); _Cache = m_Data->m_Svnclient->status(params.path(tgt.path())); } catch (const svn::Exception &e) { emit clientException(e.msg()); return false; } for (const svn::StatusPtr &ptr : qAsConst(_Cache)) { const QString _p = ptr->path(); // check the node status, not the text status (it does not cover the prop status) if (ptr->isRealVersioned() && ( ptr->nodeStatus() == svn_wc_status_modified || ptr->nodeStatus() == svn_wc_status_added || ptr->nodeStatus() == svn_wc_status_replaced || ptr->nodeStatus() == svn_wc_status_deleted || ptr->nodeStatus() == svn_wc_status_modified )) { if (ptr->nodeStatus() == svn_wc_status_deleted) { _check.append(CommitActionEntry(_p, i18n("Delete"), CommitActionEntry::DELETE)); } else { _check.append(CommitActionEntry(_p, i18n("Commit"), CommitActionEntry::COMMIT)); } } else if (ptr->nodeStatus() == svn_wc_status_missing) { _uncheck.append(CommitActionEntry(_p, i18n("Delete and Commit"), CommitActionEntry::MISSING_DELETE)); } else if (!ptr->isVersioned()) { _uncheck.append(CommitActionEntry(_p, i18n("Add and Commit"), CommitActionEntry::ADD_COMMIT)); } } } msg = Commitmsg_impl::getLogmessage(_check, _uncheck, this, _result, &ok, &keeplocks, m_Data->m_ParentList->realWidget()); if (!ok || _result.isEmpty()) { return false; } svn::Paths _add, _commit, _delete; depth = svn::DepthInfinity; for (const CommitActionEntry &entry : qAsConst(_result)) { _commit.append(entry.name()); if (entry.type() == CommitActionEntry::ADD_COMMIT) { _add.append(entry.name()); } else if (entry.type() == CommitActionEntry::MISSING_DELETE) { _delete.append(entry.name()); } } if (!_add.isEmpty()) { if (!addItems(_add, svn::DepthEmpty)) { return false; } } if (!_delete.isEmpty()) { makeDelete(svn::Targets(_delete)); } commit_parameters.targets(svn::Targets(_commit)); } commit_parameters.keepLocks(keeplocks).depth(depth).message(msg); try { StopDlg sdlg(m_Data->m_SvnContextListener, m_Data->m_ParentList->realWidget(), i18nc("@title:window", "Commit"), i18n("Commit - hit Cancel for abort")); connect(this, &SvnActions::sigExtraLogMsg, &sdlg, &StopDlg::slotExtraMessage); nnum = m_Data->m_Svnclient->commit(commit_parameters); } catch (const svn::Exception &e) { emit clientException(e.msg()); return false; } EMIT_REFRESH; emit sendNotify(i18n("Committed revision %1.", nnum.toString())); return true; } void SvnActions::slotProcessDataRead(const QByteArray &data, WatchedProcess *) { emit sendNotify(QString::fromLocal8Bit(data)); } bool SvnActions::get(const QString &what, const QString &to, const svn::Revision &rev, const svn::Revision &peg, QWidget *p) { svn::Revision _peg = peg; if (_peg == svn::Revision::UNDEFINED) { _peg = rev; } try { StopDlg sdlg(m_Data->m_SvnContextListener, p ? p : m_Data->m_ParentList->realWidget(), i18nc("@title:window", "Downloading"), i18n("Download - hit Cancel for abort")); connect(this, &SvnActions::sigExtraLogMsg, &sdlg, &StopDlg::slotExtraMessage); m_Data->m_Svnclient->get(svn::Path(what), to, rev, _peg); } catch (const svn::Exception &e) { emit clientException(e.msg()); return false; } return true; } /*! \fn SvnActions::makeDiff(const QString&,const svn::Revision&start,const svn::Revision&end) */ void SvnActions::makeDiff(const QString &what, const svn::Revision &start, const svn::Revision &end, const svn::Revision &_peg, bool isDir) { makeDiff(what, start, what, end, _peg, isDir, m_Data->m_ParentList->realWidget()); } void SvnActions::makeDiff(const QString &p1, const svn::Revision &start, const QString &p2, const svn::Revision &end) { makeDiff(p1, start, p2, end, nullptr); } void SvnActions::makeDiff(const QString &p1, const svn::Revision &start, const QString &p2, const svn::Revision &end, QWidget *p) { if (!doNetworking() && start != svn::Revision::BASE && end != svn::Revision::WORKING) { emit sendNotify(i18n("Can not do this diff because networking is disabled.")); return; } if (m_Data->isExternalDiff()) { svn::InfoEntry info; if (singleInfo(p1, start, info)) { makeDiff(p1, start, p2, end, end, info.isDir(), p); } return; } makeDiffinternal(p1, start, p2, end, p); } void SvnActions::makeDiffExternal(const QString &p1, const svn::Revision &start, const QString &p2, const svn::Revision &end, const svn::Revision &_peg, bool isDir, QWidget *p, bool rec) { QFileInfo f1(p1); QFileInfo f2(p2); QTemporaryFile tfile(QDir::tempPath() + QLatin1Char('/') + f1.fileName() + QLatin1Char('-') + start.toString()); QTemporaryFile tfile2(QDir::tempPath() + QLatin1Char('/') + f2.fileName() + QLatin1Char('-') + end.toString()); QString s1 = f1.fileName() + QLatin1Char('-') + start.toString(); QString s2 = f2.fileName() + QLatin1Char('-') + end.toString(); if (f1.fileName() == f2.fileName() && p1 != p2) { s2.append(QStringLiteral("-sec")); } QTemporaryDir tdir1; tdir1.setAutoRemove(true); tfile.setAutoRemove(true); tfile2.setAutoRemove(true); tfile.open(); tfile2.open(); QString first, second; svn::Revision peg = _peg; if (start != svn::Revision::WORKING) { first = isDir ? tdir1.path() + QLatin1Char('/') + s1 : tfile.fileName(); } else { first = p1; } if (end != svn::Revision::WORKING) { second = isDir ? tdir1.path() + QLatin1Char('/') + s2 : tfile2.fileName(); } else { second = p2; } if (second == first) { KMessageBox::error(m_Data->m_ParentList->realWidget(), i18n("Both entries seems to be the same, can not diff.")); return; } if (start != svn::Revision::WORKING) { if (!isDir) { if (!get(p1, tfile.fileName(), start, peg, p)) { return; } } else { if (!makeCheckout(p1, first, start, peg, rec ? svn::DepthInfinity : svn::DepthFiles, true, false, false, false, false, p)) { return; } } } if (end != svn::Revision::WORKING) { if (!isDir) { if (!get(p2, tfile2.fileName(), end, peg, p)) { return; } } else { if (!makeCheckout(p2, second, end, peg, rec ? svn::DepthInfinity : svn::DepthFiles, true, false, false, false, false, p)) { return; } } } const QString edisp = Kdesvnsettings::external_diff_display(); const QVector wlist = edisp.splitRef(QLatin1Char(' ')); WatchedProcess *proc = new WatchedProcess(this); for (const QStringRef &str : wlist) { if (str == QLatin1String("%1")) { *proc << first; } else if (str == QLatin1String("%2")) { *proc << second; } else { *proc << str.toString(); } } proc->setAutoDelete(true); proc->setOutputChannelMode(KProcess::MergedChannels); connect(proc, &WatchedProcess::dataStderrRead, this, &SvnActions::slotProcessDataRead); connect(proc, &WatchedProcess::dataStdoutRead, this, &SvnActions::slotProcessDataRead); if (!isDir) { tfile2.setAutoRemove(false); tfile.setAutoRemove(false); proc->appendTempFile(tfile.fileName()); proc->appendTempFile(tfile2.fileName()); } else { tdir1.setAutoRemove(false); proc->appendTempDir(tdir1.path()); } tfile.close(); tfile2.close(); proc->start(); if (proc->waitForStarted(-1)) { if (m_Data->runblocked) { proc->waitForFinished(-1); } return; } else { emit sendNotify(i18n("Diff-process could not started, check command.")); } } void SvnActions::makeDiff(const QString &p1, const svn::Revision &start, const QString &p2, const svn::Revision &end, const svn::Revision &_peg, bool isDir, QWidget *p) { if (m_Data->isExternalDiff()) { makeDiffExternal(p1, start, p2, end, _peg, isDir, p); } else { makeDiffinternal(p1, start, p2, end, p, _peg); } } void SvnActions::makeDiffinternal(const QString &p1, const svn::Revision &r1, const QString &p2, const svn::Revision &r2, QWidget *p, const svn::Revision &_peg) { if (!m_Data->m_CurrentContext) { return; } QByteArray ex; QTemporaryDir tdir; tdir.setAutoRemove(true); QString tn(tdir.path() + QLatin1String("/svndiff")); QDir d1(tdir.path()); d1.mkdir(QStringLiteral("svndiff")); bool ignore_content = Kdesvnsettings::diff_ignore_content(); bool gitformat = Kdesvnsettings::diff_gitformat_default(); bool copy_as_add = Kdesvnsettings::diff_copies_as_add(); QWidget *parent = p ? p : m_Data->m_ParentList->realWidget(); QStringList extraOptions; if (Kdesvnsettings::diff_ignore_spaces()) { extraOptions.append(QStringLiteral("-b")); } if (Kdesvnsettings::diff_ignore_all_white_spaces()) { extraOptions.append(QStringLiteral("-w")); } svn::Revision peg = _peg == svn::Revision::UNDEFINED ? r2 : _peg; svn::DiffParameter _opts; _opts.path1(p1).path2(p2).tmpPath(tn). peg(peg).rev1(r1).rev2(r2). ignoreContentType(ignore_content).extra(svn::StringArray(extraOptions)).depth(svn::DepthInfinity).ignoreAncestry(false).noDiffDeleted(false).changeList(svn::StringArray()). git_diff_format(gitformat).copies_as_adds(copy_as_add); try { StopDlg sdlg(m_Data->m_SvnContextListener, parent, i18nc("@title:window", "Diffing"), i18n("Diffing - hit Cancel for abort")); connect(this, &SvnActions::sigExtraLogMsg, &sdlg, &StopDlg::slotExtraMessage); if (p1 == p2 && (r1.isRemote() || r2.isRemote())) { ex = m_Data->m_Svnclient->diff_peg(_opts); } else { ex = m_Data->m_Svnclient->diff(_opts.relativeTo(p1 == p2 ? svn::Path(p1) : svn::Path())); } } catch (const svn::Exception &e) { emit clientException(e.msg()); return; } EMIT_FINISHED; if (ex.isEmpty()) { emit clientException(i18n("No difference to display")); return; } dispDiff(ex); } void SvnActions::makeNorecDiff(const QString &p1, const svn::Revision &r1, const QString &p2, const svn::Revision &r2, QWidget *_p) { if (!m_Data->m_CurrentContext) { return; } if (m_Data->isExternalDiff()) { svn::InfoEntry info; if (singleInfo(p1, r1, info)) { makeDiffExternal(p1, r1, p2, r2, r2, info.isDir(), _p, false); } return; } QStringList extraOptions; if (Kdesvnsettings::diff_ignore_spaces()) { extraOptions.append(QStringLiteral("-b")); } if (Kdesvnsettings::diff_ignore_all_white_spaces()) { extraOptions.append(QStringLiteral("-w")); } QByteArray ex; QTemporaryDir tdir; tdir.setAutoRemove(true); QString tn(tdir.path() + QLatin1String("/svndiff")); QDir d1(tdir.path()); d1.mkdir(QStringLiteral("svndiff")); bool ignore_content = Kdesvnsettings::diff_ignore_content(); svn::DiffParameter _opts; // no peg revision required _opts.path1(p1).path2(p2).tmpPath(tn). rev1(r1).rev2(r2). ignoreContentType(ignore_content).extra(svn::StringArray(extraOptions)).depth(svn::DepthEmpty).ignoreAncestry(false).noDiffDeleted(false).changeList(svn::StringArray()); try { StopDlg sdlg(m_Data->m_SvnContextListener, _p ? _p : m_Data->m_ParentList->realWidget(), i18nc("@title:window", "Diffing"), i18n("Diffing - hit cancel for abort")); connect(this, &SvnActions::sigExtraLogMsg, &sdlg, &StopDlg::slotExtraMessage); ex = m_Data->m_Svnclient->diff(_opts); } catch (const svn::Exception &e) { emit clientException(e.msg()); return; } EMIT_FINISHED; if (ex.isEmpty()) { emit clientException(i18n("No difference to display")); return; } dispDiff(ex); } void SvnActions::dispDiff(const QByteArray &ex) { QString what = Kdesvnsettings::external_diff_display(); if (Kdesvnsettings::use_external_diff() && (!what.contains(QLatin1String("%1")) || !what.contains(QLatin1String("%2")))) { const QVector wlist = what.splitRef(QLatin1Char(' ')); WatchedProcess *proc = new WatchedProcess(this); bool fname_used = false; for (const QStringRef &str : wlist) { if (str == QLatin1String("%f")) { QTemporaryFile tfile; tfile.setAutoRemove(false); tfile.open(); fname_used = true; QDataStream ds(&tfile); ds.writeRawData(ex, ex.size()); *proc << tfile.fileName(); proc->appendTempFile(tfile.fileName()); tfile.close(); } else { *proc << str.toString(); } } proc->setAutoDelete(true); proc->setOutputChannelMode(KProcess::MergedChannels); connect(proc, &WatchedProcess::dataStderrRead, this, &SvnActions::slotProcessDataRead); connect(proc, &WatchedProcess::dataStdoutRead, this, &SvnActions::slotProcessDataRead); proc->start(); if (proc->waitForStarted(-1)) { if (!fname_used) { proc->write(ex); proc->closeWriteChannel(); } if (m_Data->runblocked) { proc->waitForFinished(-1); } return; } else { emit sendNotify(i18n("Display process could not started, check command.")); } } bool need_modal = m_Data->runblocked || QApplication::activeModalWidget() != nullptr; if (need_modal || !m_Data->m_DiffBrowserPtr || !m_Data->m_DiffDialog) { if (!need_modal && m_Data->m_DiffBrowserPtr) { delete m_Data->m_DiffBrowserPtr; } QPointer dlg(new KSvnSimpleOkDialog(QStringLiteral("diff_display"))); if (!need_modal) { dlg->setParent(nullptr); } dlg->setWindowTitle(i18nc("@title:window", "Diff Display")); DiffBrowser *ptr(new DiffBrowser(dlg)); ptr->setText(ex); dlg->addWidget(ptr); EncodingSelector_impl *enc(new EncodingSelector_impl(dlg)); dlg->addWidget(enc); connect(enc, &EncodingSelector_impl::TextCodecChanged, ptr, &DiffBrowser::slotTextCodecChanged); enc->setCurrentEncoding(Kdesvnsettings::locale_for_diff()); // saveAs QPushButton *pbSaveAs = new QPushButton(dlg->buttonBox()); KStandardGuiItem::assign(pbSaveAs, KStandardGuiItem::SaveAs); dlg->buttonBox()->addButton(pbSaveAs, QDialogButtonBox::ActionRole); connect(pbSaveAs, &QAbstractButton::clicked, ptr, &DiffBrowser::saveDiff); dlg->buttonBox()->setStandardButtons(QDialogButtonBox::Close); dlg->addButtonBox(); if (need_modal) { ptr->setFocus(); dlg->exec(); delete dlg; return; } else { m_Data->m_DiffBrowserPtr = ptr; m_Data->m_DiffDialog = dlg; } } else { m_Data->m_DiffBrowserPtr->setText(ex); m_Data->m_DiffBrowserPtr->setFocus(); } if (m_Data->m_DiffDialog) { m_Data->m_DiffDialog->show(); m_Data->m_DiffDialog->raise(); } } /*! \fn SvnActions::makeUpdate(const QString&what,const svn::Revision&rev,bool recurse) */ void SvnActions::makeUpdate(const svn::Targets &targets, const svn::Revision &rev, svn::Depth depth) { if (!m_Data->m_CurrentContext) { return; } svn::Revisions ret; stopCheckUpdateThread(); try { StopDlg sdlg(m_Data->m_SvnContextListener, m_Data->m_ParentList->realWidget(), i18nc("@title:window", "Making update"), i18n("Making update - hit Cancel for abort")); connect(this, &SvnActions::sigExtraLogMsg, &sdlg, &StopDlg::slotExtraMessage); svn::UpdateParameter _params; m_Data->m_SvnContextListener->cleanUpdatedItems(); _params.targets(targets).revision(rev).depth(depth).ignore_externals(false).allow_unversioned(false).sticky_depth(true); ret = m_Data->m_Svnclient->update(_params); } catch (const svn::Exception &e) { emit clientException(e.msg()); return; } removeFromUpdateCache(m_Data->m_SvnContextListener->updatedItems(), true); //removeFromUpdateCache(what,depth==svn::DepthFiles); EMIT_REFRESH; EMIT_FINISHED; m_Data->clearCaches(); } /*! \fn SvnActions::slotUpdateHeadRec() */ void SvnActions::slotUpdateHeadRec() { prepareUpdate(false); } /*! \fn SvnActions::prepareUpdate(bool ask) */ void SvnActions::prepareUpdate(bool ask) { if (!m_Data->m_ParentList || !m_Data->m_ParentList->isWorkingCopy()) { return; } const SvnItemList k = m_Data->m_ParentList->SelectionList(); svn::Paths what; if (k.isEmpty()) { what.append(svn::Path(m_Data->m_ParentList->baseUri())); } else { what.reserve(k.size()); for (const SvnItem *item : k) what.append(svn::Path(item->fullName())); } svn::Revision r(svn::Revision::HEAD); if (ask) { Rangeinput_impl::revision_range range; if (!Rangeinput_impl::getRevisionRange(range, true, true)) { return; } r = range.first; } makeUpdate(svn::Targets(what), r, svn::DepthUnknown); } /*! \fn SvnActions::slotUpdateTo() */ void SvnActions::slotUpdateTo() { prepareUpdate(true); } /*! \fn SvnActions::slotAdd() */ void SvnActions::slotAdd() { makeAdd(false); } void SvnActions::slotAddRec() { makeAdd(true); } void SvnActions::makeAdd(bool rec) { if (!m_Data->m_CurrentContext) { return; } if (!m_Data->m_ParentList) { return; } const SvnItemList lst = m_Data->m_ParentList->SelectionList(); if (lst.isEmpty()) { KMessageBox::error(m_Data->m_ParentList->realWidget(), i18n("Which files or directories should I add?")); return; } svn::Paths items; items.reserve(lst.size()); for (const SvnItem *cur : lst) { if (cur->isVersioned()) { KMessageBox::error(m_Data->m_ParentList->realWidget(), i18n("
The entry
%1
is versioned - break.
", cur->fullName())); return; } items.push_back(svn::Path(cur->fullName())); } addItems(items, (rec ? svn::DepthInfinity : svn::DepthEmpty)); emit sigRefreshCurrent(nullptr); } bool SvnActions::addItems(const svn::Paths &items, svn::Depth depth) { try { for (const svn::Path &item : items) { m_Data->m_Svnclient->add(item, depth); } } catch (const svn::Exception &e) { emit clientException(e.msg()); return false; } return true; } bool SvnActions::makeDelete(const QStringList &w) { KMessageBox::ButtonCode answer = KMessageBox::questionYesNoList(nullptr, i18n("Really delete these entries?"), w, i18n("Delete from repository")); if (answer != KMessageBox::Yes) { return false; } return makeDelete(svn::Targets::fromStringList(w)); } /*! \fn SvnActions::makeDelete() */ bool SvnActions::makeDelete(const svn::Targets &target, bool keep_local, bool force) { if (!m_Data->m_CurrentContext) { return false; } try { m_Data->m_Svnclient->remove(target, force, keep_local); } catch (const svn::Exception &e) { emit clientException(e.msg()); return false; } EMIT_FINISHED; return true; } void SvnActions::slotCheckout() { CheckoutExport(QUrl(), false); } void SvnActions::slotExport() { CheckoutExport(QUrl(), true); } void SvnActions::slotCheckoutCurrent() { CheckoutExportCurrent(false); } void SvnActions::slotExportCurrent() { CheckoutExportCurrent(true); } void SvnActions::CheckoutExport(const QUrl &what, bool _exp, bool urlisTarget) { QPointer dlg(new KSvnSimpleOkDialog(QStringLiteral("checkout_export_dialog"))); CheckoutInfo_impl *ptr(new CheckoutInfo_impl(dlg)); dlg->setWindowTitle(_exp ? i18nc("@title:window", "Export a Repository") : i18nc("@title:window", "Checkout a Repository")); dlg->setWithCancelButton(); if (!what.isEmpty()) { if (!urlisTarget) { ptr->setStartUrl(what); } else { ptr->setTargetUrl(what); } } ptr->hideIgnoreKeywords(!_exp); ptr->hideOverwrite(!_exp); dlg->addWidget(ptr); if (dlg->exec() == QDialog::Accepted) { svn::Revision r = ptr->toRevision(); bool openit = ptr->openAfterJob(); bool ignoreExternal = ptr->ignoreExternals(); if (!ptr->reposURL().isValid()) { KMessageBox::error(QApplication::activeModalWidget(), i18n("Invalid url given!"), _exp ? i18n("Export repository") : i18n("Checkout a repository")); delete dlg; return; } // svn::Path should not take a QString but a QByteArray ... const QString rUrl(QString::fromUtf8(ptr->reposURL().toEncoded())); makeCheckout(rUrl, ptr->targetDir(), r, r, ptr->getDepth(), _exp, openit, ignoreExternal, ptr->overwrite(), ptr->ignoreKeywords(), nullptr); } delete dlg; } void SvnActions::CheckoutExportCurrent(bool _exp) { // checkout export only on repo, not wc if (!m_Data->m_ParentList || m_Data->m_ParentList->isWorkingCopy()) { return; } SvnItem *k = m_Data->m_ParentList->Selected(); if (k && !k->isDir()) { KMessageBox::error(m_Data->m_ParentList->realWidget(), _exp ? i18n("Exporting a file?") : i18n("Checking out a file?")); return; } QUrl what; if (!k) { what = QUrl(m_Data->m_ParentList->baseUri()); } else { what = QUrl(k->fullName()); } // what is always remote, so QUrl(what) is fine CheckoutExport(QUrl(what), _exp); } bool SvnActions::makeCheckout(const QString &rUrl, const QString &tPath, const svn::Revision &r, const svn::Revision &_peg, svn::Depth depth, // kind of operation bool _exp, // open after job bool openIt, // ignore externals bool ignoreExternal, // overwrite/force not versioned items bool overwrite, // do not replace svn:keywords on export bool ignoreKeywords, QWidget *_p ) { QString fUrl = rUrl; while (fUrl.endsWith(QLatin1Char('/'))) { fUrl.chop(1); } // can only be a local target dir svn::Path p(tPath); svn::Revision peg = _peg; if (r != svn::Revision::BASE && r != svn::Revision::WORKING && _peg == svn::Revision::UNDEFINED) { peg = r; } if (!_exp || !m_Data->m_CurrentContext) { reInitClient(); } svn::CheckoutParameter cparams; cparams.moduleName(fUrl).destination(p).revision(r).peg(peg).depth(depth).ignoreExternals(ignoreExternal).overWrite(overwrite).ignoreKeywords(ignoreKeywords); try { StopDlg sdlg(m_Data->m_SvnContextListener, _p ? _p : m_Data->m_ParentList->realWidget(), _exp ? i18nc("@title:window", "Export") : i18nc("@title:window", "Checkout"), _exp ? i18n("Exporting") : i18n("Checking out")); connect(this, &SvnActions::sigExtraLogMsg, &sdlg, &StopDlg::slotExtraMessage); if (_exp) { /// @todo setup parameter for export operation m_Data->m_Svnclient->doExport(cparams.nativeEol(QString())); } else { m_Data->m_Svnclient->checkout(cparams); } } catch (const svn::Exception &e) { emit clientException(e.msg()); return false; } if (openIt) { const QUrl url(QUrl::fromLocalFile(tPath)); if (!_exp) { emit sigGotourl(url); } else { QDesktopServices::openUrl(url); } } EMIT_FINISHED; return true; } void SvnActions::slotRevert() { if (!m_Data->m_ParentList || !m_Data->m_ParentList->isWorkingCopy()) { return; } const SvnItemList lst = m_Data->m_ParentList->SelectionList(); QStringList displist; if (!lst.isEmpty()) { svn::StatusParameter params; params.depth(svn::DepthInfinity).all(false).update(false).noIgnore(false).revision(svn::Revision::HEAD); for (const SvnItem *cur : lst) { if (!cur->isVersioned()) { KMessageBox::error(m_Data->m_ParentList->realWidget(), i18n("
The entry
%1
is not versioned - break.
", cur->fullName())); return; } displist.append(cur->fullName()); } } else { displist.push_back(m_Data->m_ParentList->baseUri()); } slotRevertItems(displist); EMIT_REFRESH; } void SvnActions::slotRevertItems(const QStringList &displist) { if (!m_Data->m_CurrentContext) { return; } if (displist.isEmpty()) { return; } QPointer dlg(new RevertForm(displist, QApplication::activeModalWidget())); if (dlg->exec() != QDialog::Accepted) { delete dlg; return; } const svn::Depth depth = dlg->getDepth(); delete dlg; const svn::Targets target(svn::Targets::fromStringList(displist)); try { StopDlg sdlg(m_Data->m_SvnContextListener, m_Data->m_ParentList->realWidget(), i18nc("@title:window", "Revert"), i18n("Reverting items")); connect(this, &SvnActions::sigExtraLogMsg, &sdlg, &StopDlg::slotExtraMessage); m_Data->m_Svnclient->revert(target, depth); } catch (const svn::Exception &e) { emit clientException(e.msg()); return; } // remove them from cache for (const auto &tgt : target.targets()) { m_Data->m_Cache.deleteKey(tgt.path(), depth != svn::DepthInfinity); } emit sigItemsReverted(displist); EMIT_FINISHED; } bool SvnActions::makeSwitch(const QUrl &rUrl, const QString &tPath, const svn::Revision &r, svn::Depth depth, const svn::Revision &peg, bool stickydepth, bool ignore_externals, bool allow_unversioned) { if (!m_Data->m_CurrentContext) { return false; } svn::Path p(tPath); try { StopDlg sdlg(m_Data->m_SvnContextListener, m_Data->m_ParentList->realWidget(), i18nc("@title:window", "Switch URL"), i18n("Switching URL")); connect(this, &SvnActions::sigExtraLogMsg, &sdlg, &StopDlg::slotExtraMessage); m_Data->m_Svnclient->doSwitch(p, svn::Url(rUrl), r, depth, peg, stickydepth, ignore_externals, allow_unversioned); } catch (const svn::Exception &e) { emit clientException(e.msg()); return false; } m_Data->clearCaches(); EMIT_FINISHED; return true; } bool SvnActions::makeRelocate(const QUrl &fUrl, const QUrl &tUrl, const QString &path, bool recursive, bool ignore_externals) { if (!m_Data->m_CurrentContext) { return false; } svn::Path p(path); try { StopDlg sdlg(m_Data->m_SvnContextListener, m_Data->m_ParentList->realWidget(), i18nc("@title:window", "Relocate Repository"), i18n("Relocate repository to new URL")); connect(this, &SvnActions::sigExtraLogMsg, &sdlg, &StopDlg::slotExtraMessage); m_Data->m_Svnclient->relocate(p, svn::Url(fUrl), svn::Url(tUrl), recursive, ignore_externals); } catch (const svn::Exception &e) { emit clientException(e.msg()); return false; } m_Data->clearCaches(); EMIT_FINISHED; return true; } void SvnActions::slotSwitch() { if (!m_Data->m_CurrentContext) { return; } if (!m_Data->m_ParentList || !m_Data->m_ParentList->isWorkingCopy()) { return; } const SvnItemList lst = m_Data->m_ParentList->SelectionList(); if (lst.count() > 1) { KMessageBox::error(nullptr, i18n("Can only switch one item at time")); return; } SvnItem *k = m_Data->m_ParentList->SelectedOrMain(); if (!k) { KMessageBox::error(nullptr, i18n("Error getting entry to switch")); return; } const QUrl what = k->Url(); if (makeSwitch(k->fullName(), what)) { emit reinitItem(k); } } bool SvnActions::makeSwitch(const QString &path, const QUrl &what) { QPointer dlg(new KSvnSimpleOkDialog(QStringLiteral("switch_url_dlg"))); CheckoutInfo_impl *ptr(new CheckoutInfo_impl(dlg)); dlg->setWindowTitle(i18nc("@title:window", "Switch URL")); dlg->setWithCancelButton(); ptr->setStartUrl(what); ptr->disableAppend(true); ptr->disableTargetDir(true); ptr->disableOpen(true); dlg->addWidget(ptr); bool done = false; if (dlg->exec() == QDialog::Accepted) { if (!ptr->reposURL().isValid()) { KMessageBox::error(QApplication::activeModalWidget(), i18n("Invalid url given!"), i18n("Switch URL")); delete dlg; return false; } svn::Revision r = ptr->toRevision(); done = makeSwitch(ptr->reposURL(), path, r, ptr->getDepth(), r, true, ptr->ignoreExternals(), ptr->overwrite()); } delete dlg; return done; } bool SvnActions::makeCleanup(const QString &path) { if (!m_Data->m_CurrentContext) { return false; } try { StopDlg sdlg(m_Data->m_SvnContextListener, m_Data->m_ParentList->realWidget(), i18nc("@title:window", "Cleanup"), i18n("Cleaning up folder")); connect(this, &SvnActions::sigExtraLogMsg, &sdlg, &StopDlg::slotExtraMessage); m_Data->m_Svnclient->cleanup(svn::Path(path)); } catch (const svn::Exception &e) { emit clientException(e.msg()); return false; } return true; } void SvnActions::slotResolved(const QString &path) { if (!m_Data->m_CurrentContext) { return; } try { StopDlg sdlg(m_Data->m_SvnContextListener, m_Data->m_ParentList->realWidget(), i18nc("@title:window", "Resolve"), i18n("Marking resolved")); connect(this, &SvnActions::sigExtraLogMsg, &sdlg, &StopDlg::slotExtraMessage); m_Data->m_Svnclient->resolve(svn::Path(path), svn::DepthEmpty); } catch (const svn::Exception &e) { emit clientException(e.msg()); return; } m_Data->m_conflictCache.deleteKey(path, false); emit sigRefreshItem(path); } void SvnActions::slotResolve(const QString &p) { if (!m_Data->m_CurrentContext) { return; } const QString eresolv = Kdesvnsettings::conflict_resolver(); const QVector wlist = eresolv.splitRef(QLatin1Char(' ')); if (wlist.isEmpty()) { return; } svn::InfoEntry i1; if (!singleInfo(p, svn::Revision::UNDEFINED, i1)) { return; } QFileInfo fi(p); QString base; if (fi.isRelative()) { base = fi.absolutePath() + QLatin1Char('/'); } if (i1.conflicts().isEmpty()) { emit sendNotify(i18n("Could not retrieve conflict information - giving up.")); return; } WatchedProcess *proc = new WatchedProcess(this); for (const QStringRef &str : wlist) { if (str == QLatin1String("%o") || str == QLatin1String("%l")) { *proc << i1.conflicts()[0]->baseFile(); } else if (str == QLatin1String("%m") || str == QLatin1String("%w")) { *proc << i1.conflicts()[0]->myFile(); } else if (str == QLatin1String("%n") || str == QLatin1String("%r")) { *proc << i1.conflicts()[0]->theirFile(); } else if (str == QLatin1String("%t")) { *proc << p; } else { *proc << str.toString(); } } proc->setAutoDelete(true); proc->setOutputChannelMode(KProcess::MergedChannels); connect(proc, &WatchedProcess::dataStderrRead, this, &SvnActions::slotProcessDataRead); connect(proc, &WatchedProcess::dataStdoutRead, this, &SvnActions::slotProcessDataRead); proc->start(); if (!proc->waitForStarted(-1)) { emit sendNotify(i18n("Resolve-process could not started, check command.")); } } void SvnActions::slotImport(const QString &path, const QUrl &target, const QString &message, svn::Depth depth, bool noIgnore, bool noUnknown) { if (!m_Data->m_CurrentContext) { return; } try { StopDlg sdlg(m_Data->m_SvnContextListener, m_Data->m_ParentList->realWidget(), i18nc("@title:window", "Import"), i18n("Importing items")); connect(this, &SvnActions::sigExtraLogMsg, &sdlg, &StopDlg::slotExtraMessage); m_Data->m_Svnclient->import(svn::Path(path), svn::Url(target), message, depth, noIgnore, noUnknown); } catch (const svn::Exception &e) { emit clientException(e.msg()); return; } } void SvnActions::slotMergeExternal(const QString &_src1, const QString &_src2, const QString &_target, const svn::Revision &rev1, const svn::Revision &rev2, const svn::Revision &_peg, bool rec) { Q_UNUSED(_peg); QTemporaryDir tdir1; tdir1.setAutoRemove(true); QString src1 = _src1; QString src2 = _src2; QString target = _target; bool singleMerge = false; if (rev1 == rev2 && (src2.isEmpty() || src1 == src2)) { singleMerge = true; } if (src1.isEmpty()) { emit clientException(i18n("Nothing to merge.")); return; } if (target.isEmpty()) { emit clientException(i18n("No destination to merge.")); return; } QFileInfo f1(src1); QFileInfo f2(src2); bool isDir = true; svn::InfoEntry i1, i2; if (!singleInfo(src1, rev1, i1)) { return; } isDir = i1.isDir(); if (!singleMerge && src1 != src2) { if (!singleInfo(src2, rev2, i2)) { return; } if (i2.isDir() != isDir) { emit clientException(i18n("Both sources must be same type.")); return; } } QFileInfo ti(target); if (ti.isDir() != isDir) { emit clientException(i18n("Target for merge must same type like sources.")); return; } QString s1 = f1.fileName() + QLatin1Char('-') + rev1.toString(); QString s2 = f2.fileName() + QLatin1Char('-') + rev2.toString(); QString first, second; if (rev1 != svn::Revision::WORKING) { first = tdir1.path() + QLatin1Char('/') + s1; } else { first = src1; } if (!singleMerge) { if (rev2 != svn::Revision::WORKING) { second = tdir1.path() + QLatin1Char('/') + s2; } else { second = src2; } } else { // only two-way merge second.clear(); } if (second == first) { KMessageBox::error(m_Data->m_ParentList->realWidget(), i18n("Both entries seems to be the same, will not do a merge.")); return; } if (rev1 != svn::Revision::WORKING) { if (isDir) { if (!makeCheckout(src1, first, rev1, svn::Revision::UNDEFINED, rec ? svn::DepthInfinity : svn::DepthFiles, true, false, false, false, false, nullptr)) { return; } } else { if (!get(src1, first, rev1, svn::Revision::UNDEFINED, m_Data->m_ParentList->realWidget())) { return; } } } if (!singleMerge) { if (rev2 != svn::Revision::WORKING) { if (isDir) { if (!makeCheckout(src2, second, rev2, svn::Revision::UNDEFINED, rec ? svn::DepthInfinity : svn::DepthFiles, true, false, false, false, false, nullptr)) { return; } } else { if (!get(src2, second, rev2, svn::Revision::UNDEFINED, m_Data->m_ParentList->realWidget())) { return; } } } } const QString edisp = Kdesvnsettings::external_merge_program(); const QVector wlist = edisp.splitRef(QLatin1Char(' ')); WatchedProcess *proc = new WatchedProcess(this); for (const QStringRef &str : wlist) { if (str == QLatin1String("%s1")) { *proc << first; } else if (str == QLatin1String("%s2")) { if (!second.isEmpty()) { *proc << second; } } else if (str == QLatin1String("%t")) { *proc << target; } else { *proc << str.toString(); } } tdir1.setAutoRemove(false); proc->setAutoDelete(true); proc->appendTempDir(tdir1.path()); proc->setOutputChannelMode(KProcess::MergedChannels); connect(proc, &WatchedProcess::dataStderrRead, this, &SvnActions::slotProcessDataRead); connect(proc, &WatchedProcess::dataStdoutRead, this, &SvnActions::slotProcessDataRead); proc->start(); if (proc->waitForStarted(-1)) { if (m_Data->runblocked) { proc->waitForFinished(-1); } } else { emit sendNotify(i18n("Merge process could not started, check command.")); } } void SvnActions::slotMergeWcRevisions(const QString &_entry, const svn::Revision &rev1, const svn::Revision &rev2, bool rec, bool ancestry, bool forceIt, bool dry, bool allow_mixed_rev) { slotMerge(_entry, _entry, _entry, rev1, rev2, svn::Revision::UNDEFINED, rec, ancestry, forceIt, dry, false, false, allow_mixed_rev); } void SvnActions::slotMerge(const QString &src1, const QString &src2, const QString &target, const svn::Revision &rev1, const svn::Revision &rev2, const svn::Revision &_peg, bool rec, bool ancestry, bool forceIt, bool dry, bool recordOnly, bool reintegrate, bool allow_mixed_rev) { Q_UNUSED(_peg); if (!m_Data->m_CurrentContext) { return; } svn::Revision peg = svn::Revision::HEAD; svn::Revision tpeg; svn::RevisionRanges ranges; svn::Path p1; try { svn::Path::parsePeg(src1, p1, tpeg); } catch (const svn::Exception &e) { emit clientException(e.msg()); return; } if (tpeg != svn::Revision::UNDEFINED) { peg = tpeg; } svn::Path p2(src2); bool pegged_merge = false; // build merge Parameters svn::MergeParameter _merge_parameter; ranges.append(svn::RevisionRange(rev1, rev2)); _merge_parameter.revisions(ranges).path1(p1).path2(p2).depth(rec ? svn::DepthInfinity : svn::DepthFiles).notice_ancestry(ancestry).force(forceIt) .dry_run(dry).record_only(recordOnly).reintegrate(reintegrate).allow_mixed_rev(allow_mixed_rev) .localPath(svn::Path(target)).merge_options(svn::StringArray()); if (!reintegrate && (!p2.isSet() || src1 == src2)) { // pegged merge pegged_merge = true; if (peg == svn::Revision::UNDEFINED) { if (p1.isUrl()) { peg = rev2; } else { peg = svn::Revision::WORKING; } } _merge_parameter.peg(peg); } try { StopDlg sdlg(m_Data->m_SvnContextListener, m_Data->m_ParentList->realWidget(), i18nc("@title:window", "Merge"), i18n("Merging items")); connect(this, &SvnActions::sigExtraLogMsg, &sdlg, &StopDlg::slotExtraMessage); if (pegged_merge) { m_Data->m_Svnclient->merge_peg(_merge_parameter); } else { m_Data->m_Svnclient->merge(_merge_parameter); } } catch (const svn::Exception &e) { emit clientException(e.msg()); return; } m_Data->clearCaches(); } /*! \fn SvnActions::slotCopyMove(bool,const QString&,const QString&) */ bool SvnActions::makeMove(const QString &Old, const QString &New) { if (!m_Data->m_CurrentContext) { return false; } svn::CopyParameter params(Old, New); svn::Revision nnum; try { StopDlg sdlg(m_Data->m_SvnContextListener, m_Data->m_ParentList->realWidget(), i18nc("@title:window", "Move"), i18n("Moving/Rename item")); connect(this, &SvnActions::sigExtraLogMsg, &sdlg, &StopDlg::slotExtraMessage); nnum = m_Data->m_Svnclient->move(params.asChild(false).makeParent(false)); } catch (const svn::Exception &e) { emit clientException(e.msg()); return false; } if (nnum != svn::Revision::UNDEFINED) { emit sendNotify(i18n("Committed revision %1.", nnum.toString())); } EMIT_REFRESH; return true; } bool SvnActions::makeMove(const QList &Old, const QString &New) { try { StopDlg sdlg(m_Data->m_SvnContextListener, m_Data->m_ParentList->realWidget(), i18nc("@title:window", "Move"), i18n("Moving entries")); connect(this, &SvnActions::sigExtraLogMsg, &sdlg, &StopDlg::slotExtraMessage); const svn::Path pNew(New); // either both are local paths -> move in wc, or both are urls -> move in repository const svn::Targets t(svn::Targets::fromUrlList(Old, pNew.isUrl() ? svn::Targets::UrlConversion::KeepUrl : svn::Targets::UrlConversion::PreferLocalPath)); m_Data->m_Svnclient->move(svn::CopyParameter(t, pNew).asChild(true).makeParent(false)); } catch (const svn::Exception &e) { emit clientException(e.msg()); return false; } return true; } bool SvnActions::makeCopy(const QString &Old, const QString &New, const svn::Revision &rev) { if (!m_Data->m_CurrentContext) { return false; } try { StopDlg sdlg(m_Data->m_SvnContextListener, m_Data->m_ParentList->realWidget(), i18nc("@title:window", "Copy / Move"), i18n("Copy or Moving entries")); connect(this, &SvnActions::sigExtraLogMsg, &sdlg, &StopDlg::slotExtraMessage); m_Data->m_Svnclient->copy(svn::Path(Old), rev, svn::Path(New)); } catch (const svn::Exception &e) { emit clientException(e.msg()); return false; } EMIT_REFRESH; return true; } bool SvnActions::makeCopy(const QList &Old, const QString &New, const svn::Revision &rev) { try { StopDlg sdlg(m_Data->m_SvnContextListener, m_Data->m_ParentList->realWidget(), i18nc("@title:window", "Copy / Move"), i18n("Copy or Moving entries")); connect(this, &SvnActions::sigExtraLogMsg, &sdlg, &StopDlg::slotExtraMessage); const svn::Path pNew(New); // either both are local paths -> copy in wc, or both are urls -> copy in repository const svn::Targets t(svn::Targets::fromUrlList(Old, pNew.isUrl() ? svn::Targets::UrlConversion::KeepUrl : svn::Targets::UrlConversion::PreferLocalPath)); m_Data->m_Svnclient->copy(svn::CopyParameter(t, pNew).srcRevision(rev).pegRevision(rev).asChild(true)); } catch (const svn::Exception &e) { emit clientException(e.msg()); return false; } return true; } /*! \fn SvnActions::makeLock(const QStringList&) */ void SvnActions::makeLock(const QStringList &what, const QString &_msg, bool breakit) { if (!m_Data->m_CurrentContext) { return; } try { m_Data->m_Svnclient->lock(svn::Targets::fromStringList(what), _msg, breakit); } catch (const svn::Exception &e) { emit clientException(e.msg()); return; } } /*! \fn SvnActions::makeUnlock(const QStringList&) */ void SvnActions::makeUnlock(const QStringList &what, bool breakit) { if (!m_Data->m_CurrentContext) { return; } try { m_Data->m_Svnclient->unlock(svn::Targets::fromStringList(what), breakit); } catch (const svn::Exception &e) { emit clientException(e.msg()); return; } for (const QString &key : what) { m_Data->m_repoLockCache.deleteKey(key, true); } // m_Data->m_repoLockCache.dump_tree(); } /*! \fn SvnActions::makeStatus(const QString&what, svn::StatusEntries&dlist) */ bool SvnActions::makeStatus(const QString &what, svn::StatusEntries &dlist, const svn::Revision &where, bool rec, bool all) { bool display_ignores = Kdesvnsettings::display_ignored_files(); return makeStatus(what, dlist, where, rec, all, display_ignores); } bool SvnActions::makeStatus(const QString &what, svn::StatusEntries &dlist, const svn::Revision &where, bool rec, bool all, bool display_ignores, bool updates) { svn::Depth _d = rec ? svn::DepthInfinity : svn::DepthImmediates; return makeStatus(what, dlist, where, _d, all, display_ignores, updates); } bool SvnActions::makeStatus(const QString &what, svn::StatusEntries &dlist, const svn::Revision &where, svn::Depth _d, bool all, bool display_ignores, bool updates) { bool disp_remote_details = Kdesvnsettings::details_on_remote_listing(); try { #ifdef DEBUG_TIMER QTime _counttime; _counttime.start(); #endif svn::StatusParameter params(what); StopDlg sdlg(m_Data->m_SvnContextListener, m_Data->m_ParentList->realWidget(), i18nc("@title:window", "Status / List"), i18n("Creating list / check status")); connect(this, &SvnActions::sigExtraLogMsg, &sdlg, &StopDlg::slotExtraMessage); // rec all up noign dlist = m_Data->m_Svnclient->status(params.depth(_d).all(all).update(updates).noIgnore(display_ignores).revision(where).detailedRemote(disp_remote_details).ignoreExternals(false)); #ifdef DEBUG_TIMER qCDebug(KDESVN_LOG) << "Time for getting status: " << _counttime.elapsed(); #endif } catch (const svn::Exception &e) { emit clientException(e.msg()); return false; } return true; } void SvnActions::checkAddItems(const QString &path, bool print_error_box) { svn::StatusEntries dlist; svn::StatusEntries rlist; QStringList displist; svn::Revision where = svn::Revision::HEAD; if (!makeStatus(path, dlist, where, true, true, false, false)) { return; } for (const auto &entry : qAsConst(dlist)) { if (!entry->isVersioned()) { rlist.append(entry); displist.append(entry->path()); } } if (rlist.isEmpty()) { if (print_error_box) { KMessageBox::error(m_Data->m_ParentList->realWidget(), i18n("No unversioned items found.")); } } else { QPointer dlg(new KSvnSimpleOkDialog(QStringLiteral("add_items_dlg"))); dlg->setWindowTitle(i18nc("@title:window", "Add Unversioned Items")); dlg->setWithCancelButton(); QTreeWidget *ptr(new QTreeWidget(dlg)); ptr->headerItem()->setText(0, i18n("Item")); for (const QString &text : qAsConst(displist)) { QTreeWidgetItem *n = new QTreeWidgetItem(ptr); n->setText(0, text); n->setCheckState(0, Qt::Checked); } ptr->resizeColumnToContents(0); dlg->addWidget(ptr); if (dlg->exec() == QDialog::Accepted) { QTreeWidgetItemIterator it(ptr); displist.clear(); while (*it) { QTreeWidgetItem *t = (*it); if (t->checkState(0) == Qt::Checked) { displist.append(t->text(0)); } ++it; } if (!displist.isEmpty()) { addItems(svn::Targets::fromStringList(displist).targets(), svn::DepthEmpty); } } delete dlg; } } void SvnActions::stopCheckModifiedThread() { if (m_CThread) { m_CThread->cancelMe(); if (!m_CThread->wait(MAX_THREAD_WAITTIME)) { m_CThread->terminate(); m_CThread->wait(MAX_THREAD_WAITTIME); } delete m_CThread; m_CThread = nullptr; } } void SvnActions::stopCheckUpdateThread() { if (m_UThread) { m_UThread->cancelMe(); if (!m_UThread->wait(MAX_THREAD_WAITTIME)) { m_UThread->terminate(); m_UThread->wait(MAX_THREAD_WAITTIME); } delete m_UThread; m_UThread = nullptr; } } void SvnActions::stopFillCache() { if (m_FCThread) { m_FCThread->cancelMe(); if (!m_FCThread->wait(MAX_THREAD_WAITTIME)) { m_FCThread->terminate(); m_FCThread->wait(MAX_THREAD_WAITTIME); } delete m_FCThread; m_FCThread = nullptr; emit sigThreadsChanged(); emit sigCacheStatus(-1, -1); } } void SvnActions::stopMain() { if (m_Data->m_CurrentContext) { m_Data->m_SvnContextListener->setCanceled(true); sleep(1); m_Data->m_SvnContextListener->contextCancel(); } } void SvnActions::killallThreads() { stopMain(); stopCheckModifiedThread(); stopCheckUpdateThread(); stopFillCache(); } bool SvnActions::createModifiedCache(const QString &what) { stopCheckModifiedThread(); m_CThread = new CheckModifiedThread(this, what, false); connect(m_CThread, &CheckModifiedThread::checkModifiedFinished, this, &SvnActions::checkModifiedThread); m_CThread->start(); return true; } void SvnActions::checkModifiedThread() { if (!m_CThread) { return; } if (m_CThread->isRunning()) { QTimer::singleShot(2, this, &SvnActions::checkModifiedThread); return; } m_Data->m_Cache.clear(); m_Data->m_conflictCache.clear(); const svn::StatusEntries &sEntries = m_CThread->getList(); for (const auto &ptr : sEntries) { if (ptr->isRealVersioned() && ( ptr->nodeStatus() == svn_wc_status_modified || ptr->nodeStatus() == svn_wc_status_added || ptr->nodeStatus() == svn_wc_status_deleted || ptr->nodeStatus() == svn_wc_status_replaced || ptr->nodeStatus() == svn_wc_status_modified )) { m_Data->m_Cache.insertKey(ptr, ptr->path()); } else if (ptr->nodeStatus() == svn_wc_status_conflicted) { m_Data->m_conflictCache.insertKey(ptr, ptr->path()); } emit sigRefreshItem(ptr->path()); } emit sigExtraStatusMessage(i18np("Found %1 modified item", "Found %1 modified items", sEntries.size())); delete m_CThread; m_CThread = nullptr; emit sigCacheDataChanged(); } void SvnActions::checkUpdateThread() { if (!m_UThread || m_UThread->isRunning()) { if (m_UThread) { QTimer::singleShot(2, this, &SvnActions::checkUpdateThread); } return; } bool newer = false; const svn::StatusEntries &sEntries = m_UThread->getList(); for (const auto &ptr : sEntries) { if (ptr->validReposStatus()) { m_Data->m_UpdateCache.insertKey(ptr, ptr->path()); if (!(ptr->validLocalStatus())) { newer = true; } } if (ptr->isLocked() && !(ptr->entry().lockEntry().Locked())) { m_Data->m_repoLockCache.insertKey(ptr, ptr->path()); } emit sigRefreshItem(ptr->path()); } emit sigExtraStatusMessage(i18n("Checking for updates finished")); if (newer) { emit sigExtraStatusMessage(i18n("There are new items in repository")); } delete m_UThread; m_UThread = nullptr; emit sigCacheDataChanged(); } void SvnActions::getaddedItems(const QString &path, svn::StatusEntries &target) { helpers::ValidRemoteOnly vro; m_Data->m_UpdateCache.listsubs_if(path, vro); target = vro.liste(); } bool SvnActions::checkUpdatesRunning() { return m_UThread && m_UThread->isRunning(); } void SvnActions::addModifiedCache(const svn::StatusPtr &what) { if (what->nodeStatus() == svn_wc_status_conflicted) { m_Data->m_conflictCache.insertKey(what, what->path()); emit sigRefreshItem(what->path()); } else { m_Data->m_Cache.insertKey(what, what->path()); } } void SvnActions::deleteFromModifiedCache(const QString &what) { m_Data->m_Cache.deleteKey(what, true); m_Data->m_conflictCache.deleteKey(what, true); //m_Data->m_Cache.dump_tree(); emit sigRefreshItem(what); } bool SvnActions::checkModifiedCache(const QString &path) const { return m_Data->m_Cache.find(path); } bool SvnActions::checkReposLockCache(const QString &path) const { return m_Data->m_repoLockCache.findSingleValid(path, false); } bool SvnActions::checkReposLockCache(const QString &path, svn::StatusPtr &t) const { /// @todo create a method where svn::Status* will be a parameter so no copy is needed but just reading content return m_Data->m_repoLockCache.findSingleValid(path, t); } bool SvnActions::checkConflictedCache(const QString &path) const { return m_Data->m_conflictCache.find(path); } void SvnActions::startFillCache(const QString &path, bool startup) { #ifdef DEBUG_TIMER QTime _counttime; _counttime.start(); #endif stopFillCache(); #ifdef DEBUG_TIMER qCDebug(KDESVN_LOG) << "Stopped cache " << _counttime.elapsed(); _counttime.restart(); #endif if (!doNetworking()) { emit sendNotify(i18n("Not filling log cache because networking is disabled")); return; } m_FCThread = new FillCacheThread(this, path, startup); connect(m_FCThread, &FillCacheThread::fillCacheStatus, this, &SvnActions::sigCacheStatus); connect(m_FCThread, &FillCacheThread::fillCacheFinished, this, &SvnActions::stopFillCache); m_FCThread->start(); } bool SvnActions::doNetworking() { // if networking is allowd we don't need extra checks, second is just for avoiding segfaults if (Kdesvnsettings::network_on() || !m_Data->m_ParentList) { return true; } bool is_url = false; if (m_Data->m_ParentList->isNetworked()) { // if called http:// etc.pp. is_url = true; } else if (m_Data->m_ParentList->baseUri().startsWith(QLatin1Char('/'))) { // if opened a working copy we must check if it points to a networking repository svn::InfoEntry e; if (!singleInfo(m_Data->m_ParentList->baseUri(), svn::Revision::UNDEFINED, e)) { return false; } is_url = !e.reposRoot().isLocalFile(); } return !is_url; } /*! \fn SvnActions::createUpdateCache(const QString&what) */ bool SvnActions::createUpdateCache(const QString &what) { clearUpdateCache(); m_Data->m_repoLockCache.clear(); stopCheckUpdateThread(); if (!doNetworking()) { emit sigExtraStatusMessage(i18n("Not checking for updates because networking is disabled")); return false; } m_UThread = new CheckModifiedThread(this, what, true); connect(m_UThread, &CheckModifiedThread::checkModifiedFinished, this, &SvnActions::checkUpdateThread); m_UThread->start(); emit sigExtraStatusMessage(i18n("Checking for updates started in background")); return true; } bool SvnActions::checkUpdateCache(const QString &path)const { return m_Data->m_UpdateCache.find(path); } void SvnActions::removeFromUpdateCache(const QStringList &what, bool exact_only) { for (const QString &key : what) { m_Data->m_UpdateCache.deleteKey(key, exact_only); } } bool SvnActions::isUpdated(const QString &path)const { svn::StatusPtr d; return getUpdated(path, d); } bool SvnActions::getUpdated(const QString &path, svn::StatusPtr &d)const { return m_Data->m_UpdateCache.findSingleValid(path, d); } void SvnActions::clearUpdateCache() { m_Data->m_UpdateCache.clear(); } bool SvnActions::makeIgnoreEntry(const svn::Path &item, const QStringList &ignorePattern, bool unignore) { svn::Revision r(svn::Revision::UNDEFINED); QPair pmp; try { pmp = m_Data->m_Svnclient->propget(QStringLiteral("svn:ignore"), item, r, r); } catch (const svn::Exception &e) { emit clientException(e.msg()); return false; } svn::PathPropertiesMapList pm = pmp.second; QString data; if (!pm.isEmpty()) { const svn::PropertiesMap &mp = pm[0].second; data = mp[QStringLiteral("svn:ignore")]; } bool result = false; QStringList lst = data.split(QLatin1Char('\n'), QString::SkipEmptyParts); for (const QString &ignore : ignorePattern) { int it = lst.indexOf(ignore); if (it != -1) { if (unignore) { lst.removeAt(it); result = true; } } else { if (!unignore) { lst.append(ignore); result = true; } } } if (result) { data = lst.join(QLatin1Char('\n')); try { m_Data->m_Svnclient->propset(svn::PropertiesParameter().propertyName(QStringLiteral("svn:ignore")).propertyValue(data).path(item)); } catch (const svn::Exception &e) { emit clientException(e.msg()); return false; } } return result; } bool SvnActions::makeIgnoreEntry(SvnItem *which, bool unignore) { if (!which) { return false; } QString parentName = which->getParentDir(); if (parentName.isEmpty()) { return false; } QString name = which->shortName(); return makeIgnoreEntry(svn::Path(parentName), QStringList(name), unignore); } svn::PathPropertiesMapListPtr SvnActions::propList(const QString &which, const svn::Revision &where, bool cacheOnly) { svn::PathPropertiesMapListPtr pm; if (!which.isEmpty()) { QString fk = where.toString() + QLatin1Char('/') + which; svn::Path p(which); if (where != svn::Revision::WORKING) { m_Data->m_PropertiesCache.findSingleValid(fk, pm); } if (!pm && !cacheOnly) { try { pm = m_Data->m_Svnclient->proplist(p, where, where); } catch (const svn::Exception &e) { /* no messagebox needed */ if (e.apr_err() != SVN_ERR_WC_NOT_DIRECTORY) { emit sendNotify(e.msg()); } } if (where != svn::Revision::WORKING && pm) { m_Data->m_PropertiesCache.insertKey(pm, fk); } } } return pm; } bool SvnActions::isLockNeeded(SvnItem *which, const svn::Revision &where) { if (!which) { return false; } svn::Path p(which->fullName()); QPair pmp; try { pmp = m_Data->m_Svnclient->propget(QStringLiteral("svn:needs-lock"), p, where, where); } catch (const svn::Exception &) { /* no messagebox needed */ //emit clientException(e.msg()); return false; } const svn::PathPropertiesMapList pm = pmp.second; if (!pm.isEmpty()) { const svn::PropertiesMap &mp = pm.at(0).second; if (mp.contains(QStringLiteral("svn:needs-lock"))) { return true; } } return false; } QString SvnActions::searchProperty(QString &Store, const QString &property, const QString &start, const svn::Revision &where, bool up) { svn::Path pa(start); svn::InfoEntry inf; if (!singleInfo(start, where, inf)) { return QString(); } while (pa.length() > 0) { const svn::PathPropertiesMapListPtr pm = propList(pa.path(), where, false); if (!pm) { return QString(); } if (!pm->isEmpty()) { const svn::PropertiesMap &mp = pm->at(0).second; const svn::PropertiesMap::ConstIterator it = mp.find(property); if (it != mp.end()) { Store = *it; return pa.path(); } } if (up) { pa.removeLast(); if (pa.isUrl() && inf.reposRoot().toString().length() > pa.path().length()) { break; } } else { break; } } return QString(); } bool SvnActions::makeList(const QString &url, svn::DirEntries &dlist, const svn::Revision &where, svn::Depth depth) { if (!m_Data->m_CurrentContext) { return false; } try { dlist = m_Data->m_Svnclient->list(url, where, where, depth, false); } catch (const svn::Exception &e) { qCDebug(KDESVN_LOG) << "List fehler: " << e.msg(); emit clientException(e.msg()); return false; } return true; } bool SvnActions::isLocalWorkingCopy(const QString &path, QUrl &repoUrl) { if (path.isEmpty()) { return false; } const QUrl url = helpers::KTranslateUrl::string2Uri(path); if (!url.isLocalFile()) { qCDebug(KDESVN_LOG) << "isLocalWorkingCopy no local file: " << path << " - " << url.toString(); return false; } QString cleanpath = url.adjusted(QUrl::StripTrailingSlash|QUrl::NormalizePathSegments).path(); qCDebug(KDESVN_LOG) << "isLocalWorkingCopy for " << cleanpath; repoUrl.clear(); svn::Revision peg(svn_opt_revision_unspecified); svn::Revision rev(svn_opt_revision_unspecified); svn::InfoEntries e; try { e = m_Data->m_Svnclient->info(cleanpath, svn::DepthEmpty, rev, peg); } catch (const svn::Exception &e) { if (SVN_ERR_WC_NOT_DIRECTORY == e.apr_err()) { return false; } return true; } if (!e.isEmpty()) repoUrl = e.at(0).url(); return true; } void SvnActions::slotExtraLogMsg(const QString &msg) { emit sigExtraLogMsg(msg); } void SvnActions::slotCancel(bool how) { if (!m_Data->m_CurrentContext) { return; } m_Data->m_SvnContextListener->setCanceled(how); } void SvnActions::setContextData(const QString &aKey, const QString &aValue) { if (aValue.isNull()) { m_Data->m_contextData.remove(aKey); } else { m_Data->m_contextData[aKey] = aValue; } } void SvnActions::clearContextData() { m_Data->m_contextData.clear(); } QString SvnActions::getContextData(const QString &aKey)const { if (m_Data->m_contextData.find(aKey) != m_Data->m_contextData.end()) { return m_Data->m_contextData[aKey]; } return QString(); } bool SvnActions::threadRunning(ThreadType which) const { switch (which) { case checkupdatethread: return (m_UThread && m_UThread->isRunning()); case fillcachethread: return (m_FCThread && m_FCThread->isRunning()); case checkmodifiedthread: return (m_CThread && m_CThread->isRunning()); } return false; } diff --git a/src/svnqt/cache/ReposConfig.h b/src/svnqt/cache/ReposConfig.h index f0df9e62..e8d6ac51 100644 --- a/src/svnqt/cache/ReposConfig.h +++ b/src/svnqt/cache/ReposConfig.h @@ -1,106 +1,106 @@ /*************************************************************************** * Copyright (C) 2005-2009 by Rajko Albrecht ral@alwins-world.de * * https://kde.org/applications/development/org.kde.kdesvn * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public * * License as published by the Free Software Foundation; either * * version 2.1 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 * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this program (in the file LGPL.txt); if not, * * write to the Free Software Foundation, Inc., 51 Franklin St, * * Fifth Floor, Boston, MA 02110-1301 USA * * * * This software consists of voluntary contributions made by many * * individuals. For exact contribution history, see the revision * * history and logs, available at https://commits.kde.org/kdesvn. * ***************************************************************************/ /*************************************************************************** * Lot of code and ideas taken from KDE 4 KConfigGroup source code * * ksvn://anonsvn.kde.org/home/kde/branches/KDE/4.2/kdelibs/kdecore/config * ***************************************************************************/ #ifndef REPOSCONFIG_HPP #define REPOSCONFIG_HPP #include #include #include #include #include "svnqt/svnqt_defines.h" namespace svn { namespace cache { class SVNQT_EXPORT ReposConfig { private: static ReposConfig *mSelf; protected: ReposConfig(); template void writeCheck(const QString &repository, const QString &key, const T &value); template void writeListCheck(const QString &repository, const QString &key, const QList &value); public: static ReposConfig *self(); template void setValue(const QString &repository, const QString &key, const T &value); template void setValue(const QString &repository, const QString &key, const QList &value); void setValue(const QString &repository, const QString &key, const QStringList &value); void setValue(const QString &repository, const QString &key, const QVariant &value); void setValue(const QString &repository, const QString &key, const QVariantList &list); void setValue(const QString &repository, const QString &key, const QString &value); //! special setter void eraseValue(const QString &repository, const QString &key); QVariant readEntry(const QString &repository, const QString &key, const QVariant &aDefault); int readEntry(const QString &repository, const QString &key, int aDefault); bool readEntry(const QString &repository, const QString &key, bool aDefault); QStringList readEntry(const QString &repository, const QString &key, const QStringList &aDefault); }; template inline void ReposConfig::setValue(const QString &repository, const QString &key, const T &value) { writeCheck(repository, key, value); } template inline void ReposConfig::writeCheck(const QString &repository, const QString &key, const T &value) { ConversionCheck::to_QVariant(); - setValue(repository, key, qVariantFromValue(value)); + setValue(repository, key, QVariant::fromValue(value)); } template inline void ReposConfig::setValue(const QString &repository, const QString &key, const QList &value) { writeListCheck(repository, key, value); } template inline void ReposConfig::writeListCheck(const QString &repository, const QString &key, const QList &list) { ConversionCheck::to_QVariant(); ConversionCheck::to_QString(); QVariantList data; for (const T &value : list) data.append(qVariantFromValue(value)); setValue(repository, key, data); } } // namespace cache } // namespace svn #endif diff --git a/src/svnqt/revision.cpp b/src/svnqt/revision.cpp index ea708ad0..1b0a03e7 100644 --- a/src/svnqt/revision.cpp +++ b/src/svnqt/revision.cpp @@ -1,283 +1,283 @@ /* * Port for usage with qt-framework and development for kdesvn * Copyright (C) 2005-2009 by Rajko Albrecht (ral@alwins-world.de) * https://kde.org/applications/development/org.kde.kdesvn */ /* * ==================================================================== * Copyright (c) 2002-2005 The RapidSvn Group. All rights reserved. * dev@rapidsvn.tigris.org * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library (in the file LGPL.txt); if not, * write to the Free Software Foundation, Inc., 51 Franklin St, * Fifth Floor, Boston, MA 02110-1301 USA * * This software consists of voluntary contributions made by many * individuals. For exact contribution history, see the revision * history and logs, available at http://rapidsvn.tigris.org/. * ==================================================================== */ // svncpp #include "revision.h" #include "pool.h" namespace svn { const svn_opt_revision_kind Revision::START = svn_opt_revision_number; const svn_opt_revision_kind Revision::BASE = svn_opt_revision_base; const svn_opt_revision_kind Revision::HEAD = svn_opt_revision_head; const svn_opt_revision_kind Revision::WORKING = svn_opt_revision_working; const svn_opt_revision_kind Revision::UNDEFINED = svn_opt_revision_unspecified; const svn_opt_revision_kind Revision::PREV = svn_opt_revision_previous; const svn_opt_revision_kind Revision::DATE = svn_opt_revision_date; const svn_opt_revision_kind Revision::NUMBER = Revision::START; Revision::Revision(const svn_opt_revision_t *revision) { init(revision); } Revision::Revision(const svn_revnum_t revnum) { if (revnum < 0) { m_revision.kind = svn_opt_revision_unspecified; m_revision.value.number = 0; } else { m_revision.kind = svn_opt_revision_number; m_revision.value.number = revnum; } } Revision::Revision(const svn_opt_revision_kind kind) { m_revision.kind = kind; m_revision.value.number = 0; } Revision::Revision(const int revnum, const QString &revstring) { m_revision.kind = svn_opt_revision_unspecified; if (revnum > -1) { m_revision.kind = svn_opt_revision_number; m_revision.value.number = revnum; } else { assign(revstring); } } Revision::Revision(const QString &revstring) { assign(revstring); } void Revision::assign(const QString &revstring) { m_revision.kind = svn_opt_revision_unspecified; if (revstring.isEmpty()) { return; } if (revstring == QLatin1String("WORKING")) { m_revision.kind = WORKING; } else if (revstring == QLatin1String("BASE")) { m_revision.kind = BASE; } else if (revstring == QLatin1String("START")) { m_revision.kind = Revision::START; m_revision.value.number = 0; } else if (revstring == QLatin1String("PREV")) { m_revision.kind = Revision::PREV; } else if (!revstring.isNull()) { Pool pool; svn_opt_revision_t endrev; svn_opt_parse_revision(&m_revision, &endrev, revstring.toUtf8(), pool); } } void Revision::assign(const QDateTime &dateTime) { m_revision.kind = svn_opt_revision_date; DateTime dt(dateTime); m_revision.value.date = dt.GetAPRTimeT(); } Revision &Revision::operator=(const QString &what) { assign(what); return *this; } Revision::Revision(const QDateTime &dateTime) { assign(dateTime); } void Revision::init(const svn_opt_revision_t *revision) { if (!revision) { m_revision.kind = svn_opt_revision_unspecified; } else { m_revision.kind = revision->kind; // m_revision.value is a union so we are not // allowed to set number if we want to use date // and vice versa switch (revision->kind) { case svn_opt_revision_number: m_revision.value.number = revision->value.number; break; case svn_opt_revision_date: m_revision.value.date = revision->value.date; break; default: m_revision.value.number = 0; } } } Revision::operator QString()const { return toString(); } QString Revision::toString()const { QString value; switch (m_revision.kind) { case svn_opt_revision_number: - value.sprintf("%li", m_revision.value.number); + value = QString::asprintf("%li", m_revision.value.number); break; case svn_opt_revision_date: value = DateTime(m_revision.value.date).toString(QStringLiteral("{yyyy-MM-dd}")); break; case svn_opt_revision_base: value = QLatin1String("BASE"); break; case svn_opt_revision_head: value = QLatin1String("HEAD"); break; case svn_opt_revision_working: value = QLatin1String("WORKING"); break; case svn_opt_revision_previous: value = QLatin1String("PREVIOUS"); break; case svn_opt_revision_unspecified: default: - value =QLatin1String( "-1"); + value = QLatin1String("-1"); break; } return value; } const svn_opt_revision_t * Revision::revision() const { return &m_revision; } svn_revnum_t Revision::revnum() const { if (m_revision.kind == svn_opt_revision_number) { return m_revision.value.number; } return SVN_INVALID_REVNUM; } apr_time_t Revision::date() const { return m_revision.value.date; } svn_opt_revision_kind Revision::kind() const { return m_revision.kind; } bool Revision::operator==(const Revision &r)const { if (r.kind() != kind()) { return false; } if (m_revision.kind == svn_opt_revision_number) { return revnum() == r.revnum(); } else if (m_revision.kind == svn_opt_revision_date) { return date() == r.date(); } return true; } bool Revision::operator==(int value)const { if (m_revision.kind != svn_opt_revision_number || value != revnum()) { return false; } return true; } bool Revision::operator!=(const svn_opt_revision_kind t)const { return kind() != t; } bool Revision::operator==(const svn_opt_revision_kind t)const { return kind() == t; } bool Revision::operator!()const { return kind() == UNDEFINED; } bool Revision::operator!() { return kind() == UNDEFINED; } Revision::operator bool()const { return kind() != UNDEFINED; } Revision::operator bool() { return kind() != UNDEFINED; } bool Revision::isRemote()const { return kind() != UNDEFINED && kind() != BASE && kind() != WORKING; } bool Revision::isValid() const { return kind() != UNDEFINED; } } /* ----------------------------------------------------------------- * local variables: * eval: (load-file "../../rapidsvn-dev.el") * end: */ diff --git a/src/svnqt/svnstream.cpp b/src/svnqt/svnstream.cpp index c30b0b21..7bc6ad83 100644 --- a/src/svnqt/svnstream.cpp +++ b/src/svnqt/svnstream.cpp @@ -1,202 +1,202 @@ /*************************************************************************** * Copyright (C) 2006-2009 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 Lesser General Public * * License as published by the Free Software Foundation; either * * version 2.1 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 * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this program (in the file LGPL.txt); if not, * * write to the Free Software Foundation, Inc., 51 Franklin St, * * Fifth Floor, Boston, MA 02110-1301 USA * * * * This software consists of voluntary contributions made by many * * individuals. For exact contribution history, see the revision * * history and logs, available at https://commits.kde.org/kdesvn. * ***************************************************************************/ #include "svnstream.h" #include "pool.h" #include "apr.h" // Subversion api #include #include -#include +#include namespace svn { namespace stream { class SVNQT_NOEXPORT SvnStream_private { public: SvnStream_private() { m_Stream = nullptr; _context = nullptr;/*cancel_timeout.start();*/ } ~SvnStream_private() { /*qDebug("Time elapsed: %i ",cancel_timeout.elapsed());*/ } static svn_error_t *stream_write(void *baton, const char *data, apr_size_t *len); static svn_error_t *stream_read(void *baton, char *data, apr_size_t *len); Pool m_Pool; svn_stream_t *m_Stream; QString m_LastError; svn_client_ctx_t *_context; - QTime cancel_timeout; + QElapsedTimer cancel_timeout; }; svn_error_t *SvnStream_private::stream_read(void *baton, char *data, apr_size_t *len) { SvnStream *b = (SvnStream *)baton; svn_client_ctx_t *ctx = b->context(); if (ctx && ctx->cancel_func) { SVN_ERR(ctx->cancel_func(ctx->cancel_baton)); } long res = b->isOk() ? b->read(data, *len) : -1; if (res < 0) { *len = 0; return svn_error_create(SVN_ERR_MALFORMED_FILE, nullptr, b->lastError().toUtf8()); } *len = res; return SVN_NO_ERROR; } svn_error_t *SvnStream_private::stream_write(void *baton, const char *data, apr_size_t *len) { SvnStream *b = (SvnStream *)baton; svn_client_ctx_t *ctx = b->context(); if (ctx && ctx->cancel_func && b->cancelElapsed() > 50) { //qDebug("Check cancel"); SVN_ERR(ctx->cancel_func(ctx->cancel_baton)); b->cancelTimeReset(); } long res = b->isOk() ? b->write(data, *len) : -1; if (res < 0) { *len = 0; return svn_error_create(SVN_ERR_MALFORMED_FILE, nullptr, b->lastError().toUtf8()); } *len = res; return SVN_NO_ERROR; } SvnStream::SvnStream(bool read, bool write, svn_client_ctx_t *ctx) { m_Data = new SvnStream_private; m_Data->m_Stream = svn_stream_create(this, m_Data->m_Pool); m_Data->_context = ctx; if (read) { svn_stream_set_read(m_Data->m_Stream, SvnStream_private::stream_read); } if (write) { svn_stream_set_write(m_Data->m_Stream, SvnStream_private::stream_write); } } SvnStream::~SvnStream() { delete m_Data; } int SvnStream::cancelElapsed()const { return m_Data->cancel_timeout.elapsed(); } void SvnStream::cancelTimeReset() { m_Data->cancel_timeout.restart(); } SvnStream::operator svn_stream_t *()const { return m_Data->m_Stream; } svn_client_ctx_t *SvnStream::context() { return m_Data->_context; } long SvnStream::write(const char *, const unsigned long) { m_Data->m_LastError = QLatin1String("Write not supported with that stream"); return -1; } long SvnStream::read(char *, const unsigned long) { m_Data->m_LastError = QLatin1String("Read not supported with that stream"); return -1; } const QString &SvnStream::lastError()const { return m_Data->m_LastError; } void SvnStream::setError(const QString &aError)const { m_Data->m_LastError = aError; } /* ByteStream implementation start */ SvnByteStream::SvnByteStream(svn_client_ctx_t *ctx) : SvnStream(false, true, ctx) , m_ByteData(new QBuffer) { m_ByteData->open(QIODevice::ReadWrite); if (!m_ByteData->isOpen()) { setError(m_ByteData->errorString()); } } SvnByteStream::~SvnByteStream() { delete m_ByteData; } long SvnByteStream::write(const char *aData, const unsigned long max) { qint64 i = m_ByteData->write(aData, max); if (i < 0) { setError(m_ByteData->errorString()); } return i; } QByteArray SvnByteStream::content()const { return m_ByteData->buffer(); } bool SvnByteStream::isOk()const { return m_ByteData->isOpen(); } /* ByteStream implementation end */ } // namespace stream } // namespace svn