diff --git a/Messages.sh b/Messages.sh index 0b4e5b78e0..edb9874577 100644 --- a/Messages.sh +++ b/Messages.sh @@ -1,16 +1,16 @@ #! /usr/bin/env bash subdirs="debugger interfaces language outputview project shell sublime util vcs" rcfiles="`find $subdirs -name \*.rc`" uifiles="`find $subdirs -name \*.ui`" kcfgfiles="`find $subdirs -name \*.kcfg`" if [[ "$rcfiles" != "" ]] ; then $EXTRACTRC $rcfiles >> rc.cpp || exit 11 fi if [[ "$uifiles" != "" ]] ; then $EXTRACTRC $uifiles >> rc.cpp || exit 12 fi if [[ "$kcfgfiles" != "" ]] ; then $EXTRACTRC $kcfgfiles >> rc.cpp || exit 13 fi -$XGETTEXT -kaliasLocal `find $subdirs -name \*.cc -o -name \*.cpp -o -name \*.h` rc.cpp -o $podir/kdevplatform.pot +$XGETTEXT -kaliasLocal `find $subdirs -name \*.cc -o -name \*.cpp -o -name \*.h | grep -v '/tests/'` rc.cpp -o $podir/kdevplatform.pot rm -f rc.cpp diff --git a/interfaces/foregroundlock.cpp b/interfaces/foregroundlock.cpp index 0631cb2d25..ada7db9285 100644 --- a/interfaces/foregroundlock.cpp +++ b/interfaces/foregroundlock.cpp @@ -1,213 +1,225 @@ /* Copyright 2010 David Nolden 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 "foregroundlock.h" #include #include #include using namespace KDevelop; namespace { -QMutex mutex(QMutex::Recursive); +QMutex internalMutex; QMutex tryLockMutex; QMutex waitMutex; QMutex finishMutex; QWaitCondition condition; volatile QThread* holderThread = 0; volatile int recursion = 0; void lockForegroundMutexInternal() { - mutex.lock(); - if(recursion > 0) - Q_ASSERT(holderThread == QThread::currentThread()); - else - Q_ASSERT(holderThread == 0); - holderThread = QThread::currentThread(); - recursion += 1; + if(holderThread == QThread::currentThread()) + { + // We already have the mutex + ++recursion; + }else{ + internalMutex.lock(); + Q_ASSERT(recursion == 0 && holderThread == 0); + holderThread = QThread::currentThread(); + recursion = 1; + } } bool tryLockForegroundMutexInternal(int interval = 0) { - if(mutex.tryLock(interval)) + if(holderThread == QThread::currentThread()) { - lockForegroundMutexInternal(); - Q_ASSERT(holderThread == QThread::currentThread()); - mutex.unlock(); // We've acquired one lock more than required, so unlock again + // We already have the mutex + ++recursion; return true; }else{ - return false; + if(internalMutex.tryLock(interval)) + { + Q_ASSERT(recursion == 0 && holderThread == 0); + holderThread = QThread::currentThread(); + recursion = 1; + return true; + }else{ + return false; + } } } void unlockForegroundMutexInternal() { Q_ASSERT(holderThread == QThread::currentThread()); Q_ASSERT(recursion > 0); recursion -= 1; if(recursion == 0) + { holderThread = 0; - mutex.unlock(); + internalMutex.unlock(); + } } } ForegroundLock::ForegroundLock(bool lock) : m_locked(false) { if(lock) relock(); } void KDevelop::ForegroundLock::relock() { Q_ASSERT(!m_locked); if(!QApplication::instance() || QThread::currentThread() == QApplication::instance()->thread()) { lockForegroundMutexInternal(); }else{ QMutexLocker lock(&tryLockMutex); while(!tryLockForegroundMutexInternal(10)) { // In case an additional event-loop was started from within the foreground, we send // events to the foreground to temporarily release the lock. class ForegroundReleaser : public DoInForeground { public: virtual void doInternal() { // By locking the mutex, we make sure that the requester is actually waiting for the condition waitMutex.lock(); // Now we release the foreground lock TemporarilyReleaseForegroundLock release; // And signalize to the requester that we've released it condition.wakeAll(); // Allow the requester to actually wake up, by unlocking m_waitMutex waitMutex.unlock(); // Now wait until the requester is ready QMutexLocker lock(&finishMutex); } }; static ForegroundReleaser releaser; QMutexLocker lockWait(&waitMutex); QMutexLocker lockFinish(&finishMutex); QMetaObject::invokeMethod(&releaser, "doInternalSlot", Qt::QueuedConnection); // We limit the waiting time here, because sometimes it may happen that the foreground-lock is released, // and the foreground is waiting without an event-loop running. (For example through TemporarilyReleaseForegroundLock) condition.wait(&waitMutex, 30); if(tryLockForegroundMutexInternal()) { //success break; }else{ //Probably a third thread has creeped in and //got the foreground lock before us. Just try again. } } } m_locked = true; Q_ASSERT(holderThread == QThread::currentThread()); Q_ASSERT(recursion > 0); } bool KDevelop::ForegroundLock::isLockedForThread() { return QThread::currentThread() == holderThread; } bool KDevelop::ForegroundLock::tryLock() { if(tryLockForegroundMutexInternal()) { m_locked = true; return true; } return false; } void KDevelop::ForegroundLock::unlock() { Q_ASSERT(m_locked); unlockForegroundMutexInternal(); m_locked = false; } TemporarilyReleaseForegroundLock::TemporarilyReleaseForegroundLock() { Q_ASSERT(holderThread == QThread::currentThread()); m_recursion = 0; while(holderThread == QThread::currentThread()) { unlockForegroundMutexInternal(); ++m_recursion; } } TemporarilyReleaseForegroundLock::~TemporarilyReleaseForegroundLock() { for(int a = 0; a < m_recursion; ++a) lockForegroundMutexInternal(); Q_ASSERT(recursion == m_recursion && holderThread == QThread::currentThread()); } KDevelop::ForegroundLock::~ForegroundLock() { if(m_locked) unlock(); } bool KDevelop::ForegroundLock::isLocked() const { return m_locked; } namespace KDevelop { const int __fg_dummy1 = 0, __fg_dummy2 = 0, __fg_dummy3 = 0, __fg_dummy4 = 0, __fg_dummy5 = 0, __fg_dummy6 = 0, __fg_dummy7 = 0, __fg_dummy8 = 0, __fg_dummy9 = 0; void DoInForeground::doIt() { if(QThread::currentThread() == QApplication::instance()->thread()) { // We're already in the foreground, just call the handler code doInternal(); }else{ QMutexLocker lock(&m_mutex); QMetaObject::invokeMethod(this, "doInternalSlot", Qt::QueuedConnection); m_wait.wait(&m_mutex); } } DoInForeground::~DoInForeground() { } DoInForeground::DoInForeground() { moveToThread(QApplication::instance()->thread()); } void DoInForeground::doInternalSlot() { VERIFY_FOREGROUND_LOCKED doInternal(); QMutexLocker lock(&m_mutex); m_wait.wakeAll(); } } diff --git a/interfaces/ilauncher.h b/interfaces/ilauncher.h index d0a61a265f..7ab51b980e 100644 --- a/interfaces/ilauncher.h +++ b/interfaces/ilauncher.h @@ -1,90 +1,90 @@ /* This file is part of KDevelop Copyright 2009 Andreas Pakulat This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef ILAUNCHER_H #define ILAUNCHER_H #include "interfacesexport.h" #include #include "iruncontroller.h" class KJob; class QString; namespace KDevelop { class ILaunchConfiguration; class LaunchConfigurationPageFactory; /** * The ILauncher interface allows to actually run launch configurations. * Additionally it allows to provide configuration pages specific to the launcher * and to identify for which modes this launcher may be used */ class KDEVPLATFORMINTERFACES_EXPORT ILauncher { public: virtual ~ILauncher(); /** - * a unique identifier for the launcher, used for example for ILaunchConfigurationType::launcherForId() - * @returns an identifier for this launcher - */ + * Returns a unique identifier for the launcher, + * used for example for ILaunchConfigurationType::launcherForId(). + */ virtual QString id() = 0; /** * a user visible name for the launcher * @returns a translatable string as description for the launcher */ virtual QString name() const = 0; /** * provides a short description about this launcher * @returns a description of what the launcher can do */ virtual QString description() const = 0; /** * returns the ids of the supported launch modes * @returns a list of id's for ILaunchMode's that are supported */ virtual QStringList supportedModes() const = 0; /** * provide a list of config pages for this launcher * @returns the config pages that this launcher needs */ virtual QList configPages() const = 0; /** * create a KJob that executes @p cfg in @p launchMode when started. * @param launchMode the mode in which the launcher should execute the configuration * @param cfg the launch configuration to be executed * @returns a KJob that starts the configured launchable */ virtual KJob* start( const QString& launchMode, ILaunchConfiguration* cfg ) = 0; }; } #endif diff --git a/plugins/externalscript/kdevexternalscript.desktop b/plugins/externalscript/kdevexternalscript.desktop index c8b6689fbe..33954d0c13 100644 --- a/plugins/externalscript/kdevexternalscript.desktop +++ b/plugins/externalscript/kdevexternalscript.desktop @@ -1,75 +1,75 @@ [Desktop Entry] Type=Service Icon=system-run Exec= Comment=Run external scripts or applications to manipulate the editor contents or do other arbitrary actions. Comment[ca]=Executa scripts externs o aplicacions per a manipular el contingut de l'editor o altres accions arbitràries. Comment[da]=Kør eksterne scripts eller programmer til manipulering af editor-indholdet eller foretag andre handlinger. -Comment[de]=Führen Sie externe Skripte oder Programme zum Verändern des Editorinhalts oder um andere Aktionen durchzuführen aus. +Comment[de]=Führen Sie externe Skripte oder Programme zum Verändern des Editorinhalts oder für beliebige andere Aktionen aus. Comment[en_GB]=Run external scripts or applications to manipulate the editor contents or do other arbitrary actions. Comment[es]=Ejecutar aplicaciones o scripts externos para manipular el contenido del editor o realizar otras acciones. Comment[et]=Välised skriptid või rakendused, mis võimaldavad muuta redaktori sisu või ette võtta mingeid muid toiminguid. Comment[fr]=Lancer des scripts ou des applications externes pour manipuler le contenu de l'éditeur ou effectuer d'autres actions arbitraires. Comment[it]=Eseguire script o applicazioni esterne per manipolare il contenuto dell'editor o per fare altre azioni. Comment[nb]=Kjør eksterne skripter eller programmer for å behandle redigeringsinnholdet eller utføre andre vilkårlige handlinger. Comment[nds]=Utföhren vun extern Skripten oder Programmen för't Ännern vun den Editor-Inholt oder anner Akschonen. Comment[nl]=Externe scripts of programma's uitvoeren om de inhoud van de bewerker te manipuleren of andere acties uit te voeren. Comment[pt]=Executa programas ou aplicações externa para manipular o conteúdo do editor ou efectua outras acções arbitrárias. Comment[pt_BR]=Execute scripts externos ou aplicativos para manipular os conteúdos do editor ou para fazer outras ações ordinárias. Comment[sv]=Kör externa skript eller program för att behandla editorns innehåll eller utför andra godtyckliga åtgärder. Comment[uk]=Запускає зовнішні скрипти або програми для обробки текстових даних редактора або виконання інших потрібних дій. Comment[x-test]=xxRun external scripts or applications to manipulate the editor contents or do other arbitrary actions.xx Comment[zh_CN]=运行外部脚本或应用程序来处理编辑器内容或者执行其它任意动作。 Comment[zh_TW]=執行外部文稿或應用程式來運用編輯器內的內容,或是做各種動作。 Name=External Scripts Name[bg]=Външни скриптове Name[ca]=Scripts externs Name[da]=Eksterne scripts Name[de]=Externe Skripte Name[en_GB]=External Scripts Name[es]=Scripts externos Name[et]=Välised skriptid Name[fr]=Scripts externes Name[it]=Script esterni Name[ja]=外部スクリプト Name[nb]=Eksterne skripter Name[nds]=Extern Skripten Name[nl]=Externe scripts Name[pt]=Programas Externos Name[pt_BR]=Scripts externos Name[sv]=Externa skript Name[uk]=Зовнішні скрипти Name[x-test]=xxExternal Scriptsxx Name[zh_CN]=外部脚本 Name[zh_TW]=外部文稿 GenericName=External Scripts GenericName[bg]=Външни скриптове GenericName[ca]=Scripts externs GenericName[da]=Eksterne scripts GenericName[de]=Externe Skripte GenericName[en_GB]=External Scripts GenericName[es]=Scripts externos GenericName[et]=Välised skriptid GenericName[fr]=Scripts externes GenericName[it]=Script esterni GenericName[ja]=外部スクリプト GenericName[nb]=Eksterne skripter GenericName[nds]=Extern Skripten GenericName[nl]=Externe scripts GenericName[pt]=Programas Externos GenericName[pt_BR]=Scripts externos GenericName[sv]=Externa skript GenericName[uk]=Зовнішні скрипти GenericName[x-test]=xxExternal Scriptsxx GenericName[zh_CN]=外部脚本 GenericName[zh_TW]=外部文稿 ServiceTypes=KDevelop/Plugin X-KDE-Library=kdevexternalscript X-KDevelop-Version=11 X-KDevelop-Category=Global X-KDE-PluginInfo-Name=kdevexternalscript X-KDE-PluginInfo-Author=Milian Wolff X-KDE-PluginInfo-License=GPL X-KDevelop-Interfaces=org.kdevelop.IPlugin X-KDevelop-Mode=GUI X-KDevelop-IRequired=org.kdevelop.IOutputView diff --git a/plugins/filemanager/kdevfilemanager.desktop b/plugins/filemanager/kdevfilemanager.desktop index 87385495fa..8de67ab76f 100644 --- a/plugins/filemanager/kdevfilemanager.desktop +++ b/plugins/filemanager/kdevfilemanager.desktop @@ -1,105 +1,105 @@ [Desktop Entry] Type=Service Icon=system-file-manager Exec=blubb Comment=This plugin brings a filemanager to KDevelop. Comment[bg]=Тaзи приставка вгражда файловия манипулатор към KDevelop. Comment[ca]=Aquest connector proporciona un gestor de fitxers a KDevelop. Comment[ca@valencia]=Este connector proporciona un gestor de fitxers a KDevelop. Comment[da]=Dette plugin bringer filhåndtering til KDevelop. Comment[de]=Dieses Modul integriert einen Datei-Browser in KDevelop. Comment[el]=Αυτό το πρόσθετο ενσωματώνει ένα διαχειριστή αρχείων στο KDevelop Comment[en_GB]=This plugin brings a filemanager to KDevelop. Comment[es]=Este complemento proporciona un gestor de archivos a KDevelop. Comment[et]=See plugin võimaldab kasutada KDevelopis failihaldurit. Comment[fr]=Ce module externe offre un gestionnaire de fichiers pour KDevelop. Comment[gl]=Este engadido incorpora un xestor de ficheiros no KDevelop. Comment[it]=Questa estensione porta un gestore di file in KDevelop. Comment[ja]=このプラグインはファイルマネージャを KDevelop に統合します Comment[lv]=Šis spraudnis nodrošina failu pārvaldnieka funkcionalitāti iekš KDevelop. Comment[nb]=Dette programtillegget tar inn en filbehandler i KDevelop. Comment[nds]=Dit Moduul stellt en Dateipleger för KDevelop praat. Comment[nl]=Deze plugin brengt een bestandsbeheerder in KDevelop. Comment[pl]=Ta wtyczka udostępnia menadżer plików w KDevelopie. Comment[pt]=Este 'plugin' oferece um gestor de ficheiros para o KDevelop. Comment[pt_BR]=Esta extensão provê um gerenciador de arquivos ao KDevelop Comment[ru]=Этот модуль добавляет диспетчер файлов в KDevelop Comment[sl]=Vstavek v KDevelop prinese upravljalnik datotek. Comment[sv]=Insticksprogrammet ger KDevelop en filhanterare. Comment[tr]=Bu eklenti KDevelop için bir dosya yönetici sağlar. Comment[uk]=За допомогою цього додатка можна отримати доступ до менеджера файлів у KDevelop. Comment[x-test]=xxThis plugin brings a filemanager to KDevelop.xx Comment[zh_CN]=此插件对 KDevelop 整合一个文件管理器。 Comment[zh_TW]=此外掛程式讓 KDevelop 可使用檔案管理員。 Name=KDE File Manager Integration Name[bg]=Интегриране на файлов манипулатор на KDE Name[ca]=Integració del gestor de fitxers del KDE Name[ca@valencia]=Integració del gestor de fitxers del KDE Name[da]=Integration af KDE filhåndtering -Name[de]=KDE-Dateimanager-Integration +Name[de]=Integration der KDE-Dateiverwaltung Name[en_GB]=KDE File Manager Integration Name[es]=Integración del gestor de archivos de KDE Name[et]=KDE lõimimine failihalduriga Name[fr]=Intégration du gestionnaire de fichiers de KDE Name[gl]=Integración co xestor de ficheiros do KDE Name[it]=Integrazione gestore di file di KDE Name[ja]=KDE のファイルマネージャの統合 Name[nb]=Integrasjon med KDE filbehandler Name[nds]=KDE-Dateiplegerünnerstütten Name[nl]=KDE Bestandsbeheerder-integratie Name[pl]=Integracja menadżera plików KDE Name[pt]=Integração com o Gestor de Ficheiros do KDE Name[pt_BR]=Integração com o gerenciador de arquivos do KDE Name[ru]=Интеграция диспетчера файлов KDE Name[sl]=Integracija KDE-jevega upravljalnika datotek Name[sv]=Integrering med KDE:s filhanterare Name[tr]=KDE Dosya Yöneticisi Bütünleşmesi Name[uk]=Інтеграція засобу керування файлами KDE Name[x-test]=xxKDE File Manager Integrationxx Name[zh_CN]=KDE 文件管理器整合 Name[zh_TW]=KDE 檔案管理員整合 GenericName=File Manager GenericName[bg]=Файлов манипулатор GenericName[ca]=Gestor de fitxers GenericName[ca@valencia]=Gestor de fitxers GenericName[cs]=Správce souborů GenericName[da]=Filhåndtering -GenericName[de]=Dateimanager +GenericName[de]=Dateiverwaltung GenericName[el]=Διαχειριστής αρχείων GenericName[en_GB]=File Manager GenericName[eo]=Dosieradministrilo GenericName[es]=Gestor de archivos GenericName[et]=Failihaldur GenericName[fr]=Gestionnaire de fichiers GenericName[ga]=Bainisteoir Comhad GenericName[gl]=Xestor de ficheiros GenericName[it]=Gestore di file GenericName[ja]=ファイルマネージャ GenericName[lt]=Failų tvarkyklė GenericName[lv]=Failu pārvaldnieks GenericName[ms]=Pengurus Fail GenericName[nb]=Filbehandler GenericName[nds]=Dateipleger GenericName[nl]=Bestandsbeheerder GenericName[pa]=ਫਾਇਲ ਮੈਨੇਜਰ GenericName[pl]=Menadżer plików GenericName[pt]=Gestor de Ficheiros GenericName[pt_BR]=Gerenciador de arquivos GenericName[ro]=Gestionar de fișiere GenericName[ru]=Диспетчер файлов GenericName[sl]=Upravljalnik datotek GenericName[sv]=Filhanterare GenericName[tr]=Dosya Yönetici GenericName[uk]=Менеджер файлів GenericName[x-test]=xxFile Managerxx GenericName[zh_CN]=文件管理器 GenericName[zh_TW]=檔案管理員 ServiceTypes=KDevelop/Plugin X-KDE-Library=kdevfilemanager X-KDE-PluginInfo-Name=kdevfilemanager X-KDE-PluginInfo-Author=Alexander Dymo X-KDE-PluginInfo-Version=0.1 X-KDE-PluginInfo-License=GPL X-KDevelop-Version=11 X-KDevelop-Category=Global X-KDevelop-Mode=GUI diff --git a/plugins/git/gitplugin.cpp b/plugins/git/gitplugin.cpp index c26e0ff77f..449d2a59f5 100644 --- a/plugins/git/gitplugin.cpp +++ b/plugins/git/gitplugin.cpp @@ -1,1170 +1,1170 @@ /*************************************************************************** * Copyright 2008 Evgeniy Ivanov * * Copyright 2009 Hugo Parente Lima * * Copyright 2010 Aleix Pol Gonzalez * * * * 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) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * 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, see . * ***************************************************************************/ #include "gitplugin.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gitclonejob.h" #include #include #include #include "stashmanagerdialog.h" #include #include "gitjob.h" K_PLUGIN_FACTORY(KDevGitFactory, registerPlugin(); ) K_EXPORT_PLUGIN(KDevGitFactory(KAboutData("kdevgit","kdevgit",ki18n("Git"),"0.1",ki18n("A plugin to support git version control systems"), KAboutData::License_GPL))) using namespace KDevelop; namespace { QDir dotGitDirectory(const KUrl& dirPath) { const QFileInfo finfo(dirPath.toLocalFile()); QDir dir = finfo.isDir() ? QDir(dirPath.toLocalFile()) : finfo.absoluteDir(); static const QString gitDir(".git"); while (!dir.exists(gitDir) && dir.cdUp()) {} // cdUp, until there is a sub-directory called .git return dir; } /** * Whenever a directory is provided, change it for all the files in it but not inner directories, * that way we make sure we won't get into recursion, */ static KUrl::List preventRecursion(const KUrl::List& urls) { KUrl::List ret; foreach(const KUrl& url, urls) { QDir d(url.toLocalFile()); if(d.exists()) { QStringList entries = d.entryList(QDir::Files | QDir::NoDotAndDotDot); foreach(const QString& entry, entries) { KUrl entryUrl = d.absoluteFilePath(entry); ret += entryUrl; } } else ret += url; } return ret; } QString toRevisionName(const KDevelop::VcsRevision& rev, QString currentRevision=QString()) { switch(rev.revisionType()) { case VcsRevision::Special: switch(rev.revisionValue().value()) { case VcsRevision::Head: return "^HEAD"; case VcsRevision::Base: return ""; case VcsRevision::Working: return ""; case VcsRevision::Previous: Q_ASSERT(!currentRevision.isEmpty()); return currentRevision + "^1"; case VcsRevision::Start: return ""; case VcsRevision::UserSpecialType: //Not used Q_ASSERT(false && "i don't know how to do that"); } break; case VcsRevision::GlobalNumber: return rev.revisionValue().toString(); case VcsRevision::Date: case VcsRevision::FileNumber: case VcsRevision::Invalid: case VcsRevision::UserSpecialType: Q_ASSERT(false); } return QString(); } QString revisionInterval(const KDevelop::VcsRevision& rev, const KDevelop::VcsRevision& limit) { QString ret; // qDebug() << "prrrrrrrrrr" << toRevisionName(rev, "xxx") << toRevisionName(limit, "yyy"); if(rev.revisionType()==VcsRevision::Special && rev.revisionValue().value()==VcsRevision::Start) //if we want it to the begining just put the revisionInterval ret = toRevisionName(limit, QString()); else { QString dst = toRevisionName(limit); if(dst.isEmpty()) ret = dst; else { QString src = toRevisionName(rev, dst); if(src.isEmpty()) ret = src; else ret = src+".."+dst; } } // qDebug() << "=======>" << ret; return ret; } QDir urlDir(const KUrl& url) { return QFileInfo(url.toLocalFile()).absoluteDir(); } QDir urlDir(const KUrl::List& urls) { return urlDir(urls.first()); } //TODO: could be improved } GitPlugin::GitPlugin( QObject *parent, const QVariantList & ) : DistributedVersionControlPlugin(parent, KDevGitFactory::componentData()), m_oldVersion(false) { KDEV_USE_EXTENSION_INTERFACE( KDevelop::IBasicVersionControl ) KDEV_USE_EXTENSION_INTERFACE( KDevelop::IDistributedVersionControl ) core()->uiController()->addToolView(i18n("Git"), dvcsViewFactory()); setObjectName("Git"); DVcsJob* versionJob = new DVcsJob(QDir::tempPath(), this, KDevelop::OutputJob::Silent); *versionJob << "git" << "--version"; connect(versionJob, SIGNAL(readyForParsing(KDevelop::DVcsJob*)), SLOT(parseGitVersionOutput(KDevelop::DVcsJob*))); ICore::self()->runController()->registerJob(versionJob); } GitPlugin::~GitPlugin() {} bool emptyOutput(DVcsJob* job) { QScopedPointer _job(job); if(job->exec() && job->status()==VcsJob::JobSucceeded) return job->rawOutput().trimmed().isEmpty(); return false; } bool GitPlugin::hasStashes(const QDir& repository) { return !emptyOutput(gitStash(repository, QStringList("list"), KDevelop::OutputJob::Silent)); } bool GitPlugin::hasModifications(const QDir& d) { return !emptyOutput(lsFiles(d, QStringList("-m"), OutputJob::Silent)); } void GitPlugin::additionalMenuEntries(QMenu* menu, const KUrl::List& urls) { m_urls = urls; QDir dir=urlDir(urls); bool modif = hasModifications(dotGitDirectory(urls.first())); bool canApply = !modif && hasStashes(dir); menu->addSeparator()->setText(i18n("Git Stashes")); menu->addAction(i18n("Stash Manager"), this, SLOT(ctxStashManager()))->setEnabled(canApply); menu->addAction(i18n("Push Stash"), this, SLOT(ctxPushStash()))->setEnabled(modif); menu->addAction(i18n("Pop Stash"), this, SLOT(ctxPopStash()))->setEnabled(canApply); } void GitPlugin::ctxPushStash() { VcsJob* job = gitStash(urlDir(m_urls), QStringList(), KDevelop::OutputJob::Verbose); ICore::self()->runController()->registerJob(job); } void GitPlugin::ctxPopStash() { VcsJob* job = gitStash(urlDir(m_urls), QStringList("pop"), KDevelop::OutputJob::Verbose); ICore::self()->runController()->registerJob(job); } void GitPlugin::ctxStashManager() { QPointer d = new StashManagerDialog(urlDir(m_urls), this, 0); d->exec(); delete d; } DVcsJob* GitPlugin::errorsFound(const QString& error, KDevelop::OutputJob::OutputJobVerbosity verbosity=OutputJob::Verbose) { DVcsJob* j = new DVcsJob(QDir::temp(), this, verbosity); *j << "echo" << i18n("error: %1", error) << "-n"; return j; } void GitPlugin::unload() { core()->uiController()->removeToolView( dvcsViewFactory() ); } QString GitPlugin::name() const { return QLatin1String("Git"); } KUrl GitPlugin::repositoryRoot(const KUrl& path) { return KUrl(dotGitDirectory(path).absolutePath()); } bool GitPlugin::isValidDirectory(const KUrl & dirPath) { QDir dir=dotGitDirectory(dirPath); return dir.exists(".git"); } bool GitPlugin::isVersionControlled(const KUrl &path) { QFileInfo fsObject(path.toLocalFile()); if (fsObject.isDir()) { return isValidDirectory(path); } QString filename = fsObject.fileName(); QStringList otherFiles = getLsFiles(fsObject.dir(), QStringList("--") << filename, KDevelop::OutputJob::Silent); return !otherFiles.empty(); } VcsJob* GitPlugin::init(const KUrl &directory) { DVcsJob* job = new DVcsJob(urlDir(directory), this); *job << "git" << "init"; return job; } VcsJob* GitPlugin::createWorkingCopy(const KDevelop::VcsLocation & source, const KUrl& dest, KDevelop::IBasicVersionControl::RecursionMode) { DVcsJob* job = new GitCloneJob(urlDir(dest), this); *job << "git" << "clone" << "--progress" << "--" << source.localUrl().url() << dest; return job; } VcsJob* GitPlugin::add(const KUrl::List& localLocations, KDevelop::IBasicVersionControl::RecursionMode recursion) { if (localLocations.empty()) return errorsFound(i18n("Did not specify the list of files"), OutputJob::Verbose); DVcsJob* job = new GitJob(dotGitDirectory(localLocations.front()), this); *job << "git" << "add" << "--" << (recursion == IBasicVersionControl::Recursive ? localLocations : preventRecursion(localLocations)); return job; } KDevelop::VcsJob* GitPlugin::status(const KUrl::List& localLocations, KDevelop::IBasicVersionControl::RecursionMode recursion) { if (localLocations.empty()) return errorsFound(i18n("Did not specify the list of files"), OutputJob::Verbose); DVcsJob* job = new GitJob(urlDir(localLocations), this, OutputJob::Silent); if(m_oldVersion) { *job << "git" << "ls-files" << "-t" << "-m" << "-c" << "-o" << "-d" << "-k" << "--directory"; connect(job, SIGNAL(readyForParsing(KDevelop::DVcsJob*)), SLOT(parseGitStatusOutput_old(KDevelop::DVcsJob*))); } else { *job << "git" << "status" << "--porcelain"; connect(job, SIGNAL(readyForParsing(KDevelop::DVcsJob*)), SLOT(parseGitStatusOutput(KDevelop::DVcsJob*))); } *job << "--" << (recursion == IBasicVersionControl::Recursive ? localLocations : preventRecursion(localLocations)); return job; } VcsJob* GitPlugin::diff(const KUrl& fileOrDirectory, const KDevelop::VcsRevision& srcRevision, const KDevelop::VcsRevision& dstRevision, VcsDiff::Type type, IBasicVersionControl::RecursionMode recursion) { //TODO: control different types DVcsJob* job = new GitJob(dotGitDirectory(fileOrDirectory), this); *job << "git" << "diff" << "--no-prefix"; QString revstr = revisionInterval(srcRevision, dstRevision); if(!revstr.isEmpty()) *job << revstr; *job << "--" << (recursion == IBasicVersionControl::Recursive ? fileOrDirectory : preventRecursion(fileOrDirectory)); connect(job, SIGNAL(readyForParsing(KDevelop::DVcsJob*)), SLOT(parseGitDiffOutput(KDevelop::DVcsJob*))); return job; } VcsJob* GitPlugin::revert(const KUrl::List& localLocations, IBasicVersionControl::RecursionMode recursion) { if(localLocations.isEmpty() ) return errorsFound(i18n("Could not revert changes"), OutputJob::Verbose); DVcsJob* job = new GitJob(dotGitDirectory(localLocations.front()), this); *job << "git" << "checkout" << "--"; *job << (recursion == IBasicVersionControl::Recursive ? localLocations : preventRecursion(localLocations)); return job; } //TODO: git doesn't like empty messages, but "KDevelop didn't provide any message, it may be a bug" looks ugly... //If no files specified then commit already added files VcsJob* GitPlugin::commit(const QString& message, const KUrl::List& localLocations, KDevelop::IBasicVersionControl::RecursionMode recursion) { if (localLocations.empty() || message.isEmpty()) return errorsFound(i18n("No files or message specified")); QDir dir = dotGitDirectory(localLocations.front()); DVcsJob* job = new DVcsJob(dir, this); KUrl::List files = (recursion == IBasicVersionControl::Recursive ? localLocations : preventRecursion(localLocations)); addNotVersionedFiles(dir, files); *job << "git" << "commit" << "-m" << message; *job << "--" << files; return job; } void GitPlugin::addNotVersionedFiles(const QDir& dir, const KUrl::List& files) { QStringList otherStr = getLsFiles(dir, QStringList() << "--others", KDevelop::OutputJob::Silent); KUrl::List toadd, otherFiles; foreach(const QString& file, otherStr) { KUrl v(dir.absolutePath()); v.addPath(file); otherFiles += v; } //We add the files that are not versioned foreach(const KUrl& file, files) { if(otherFiles.contains(file) && QFileInfo(file.toLocalFile()).isFile()) toadd += file; } if(!toadd.isEmpty()) { VcsJob* job = add(toadd); job->exec(); } } VcsJob* GitPlugin::remove(const KUrl::List& files) { if (files.isEmpty()) return errorsFound(i18n("No files to remove")); QDir dir = dotGitDirectory(files.front()); QStringList otherStr = getLsFiles(dir, QStringList() << "--others" << "--" << files.front().toLocalFile(), KDevelop::OutputJob::Silent); if(otherStr.isEmpty()) { DVcsJob* job = new GitJob(dir, this); *job << "git" << "rm" << "-r"; *job << "--" << files; return job; } else { return new StandardJob(this, KIO::trash(files), KDevelop::OutputJob::Silent); } } VcsJob* GitPlugin::log(const KUrl& localLocation, const KDevelop::VcsRevision& src, const KDevelop::VcsRevision& dst) { DVcsJob* job = new GitJob(dotGitDirectory(localLocation), this); *job << "git" << "log" << "--date=raw"; QString rev = revisionInterval(dst, src); if(!rev.isEmpty()) *job << rev; *job << "--" << localLocation; connect(job, SIGNAL(readyForParsing(KDevelop::DVcsJob*)), this, SLOT(parseGitLogOutput(KDevelop::DVcsJob*))); return job; } VcsJob* GitPlugin::log(const KUrl& localLocation, const KDevelop::VcsRevision& rev, unsigned long int limit) { DVcsJob* job = new GitJob(dotGitDirectory(localLocation), this, KDevelop::OutputJob::Silent); *job << "git" << "log" << "--date=raw" << toRevisionName(rev, QString()); if(limit>0) *job << QString("-%1").arg(limit); *job << "--" << localLocation; connect(job, SIGNAL(readyForParsing(KDevelop::DVcsJob*)), this, SLOT(parseGitLogOutput(KDevelop::DVcsJob*))); return job; } KDevelop::VcsJob* GitPlugin::annotate(const KUrl &localLocation, const KDevelop::VcsRevision&) { DVcsJob* job = new GitJob(dotGitDirectory(localLocation), this, KDevelop::OutputJob::Silent); *job << "git" << "blame" << "--porcelain"; *job << "--" << localLocation; connect(job, SIGNAL(readyForParsing(KDevelop::DVcsJob*)), this, SLOT(parseGitBlameOutput(KDevelop::DVcsJob*))); return job; } void GitPlugin::parseGitBlameOutput(DVcsJob *job) { QVariantList results; VcsAnnotationLine* annotation; QStringList lines = job->output().split('\n'); bool skipNext=false; QMap definedRevisions; for(QStringList::const_iterator it=lines.constBegin(), itEnd=lines.constEnd(); it!=itEnd; ++it) { if(skipNext) { skipNext=false; results += qVariantFromValue(*annotation); continue; } if(it->isEmpty()) continue; QString name = it->left(it->indexOf(' ')); QString value = it->right(it->size()-name.size()-1); kDebug() << "last line" << *it; if(name=="author") annotation->setAuthor(value); else if(name=="author-mail") {} //TODO: do smth with the e-mail? else if(name=="author-tz") {} //TODO: does it really matter? else if(name=="author-time") annotation->setDate(QDateTime::fromTime_t(value.toUInt())); else if(name=="summary") annotation->setCommitMessage(value); else if(name.startsWith("committer")) {} //We will just store the authors else if(name=="previous") {} //We don't need that either else if(name=="filename") { skipNext=true; } else if(name=="boundary") { definedRevisions.insert("boundary", VcsAnnotationLine()); } else { QStringList values = value.split(' '); VcsRevision rev; rev.setRevisionValue(name.left(8), KDevelop::VcsRevision::GlobalNumber); skipNext = definedRevisions.contains(name); if(!skipNext) definedRevisions.insert(name, VcsAnnotationLine()); annotation = &definedRevisions[name]; - annotation->setLineNumber(values[1].toInt()); + annotation->setLineNumber(values[1].toInt() - 1); annotation->setRevision(rev); } } job->setResults(results); } DVcsJob* GitPlugin::switchBranch(const QString &repository, const QString &branch) { QDir d(repository); if(hasModifications(d) && KMessageBox::questionYesNo(0, i18n("There are pending changes, do you want to stash them first?"))==KMessageBox::Yes) { QScopedPointer stash(gitStash(d, QStringList(), KDevelop::OutputJob::Verbose)); stash->exec(); } DVcsJob* job = new DVcsJob(d, this); *job << "git" << "checkout" << branch; return job; } DVcsJob* GitPlugin::branch(const QString &repository, const QString &basebranch, const QString &branch, const QStringList &args) { DVcsJob* job = new DVcsJob(QDir(repository), this, KDevelop::OutputJob::Silent); *job << "git" << "branch" << args; *job << "--"; if (!branch.isEmpty()) *job << branch; if (!basebranch.isEmpty()) *job << basebranch; return job; } DVcsJob* GitPlugin::lsFiles(const QDir &repository, const QStringList &args, OutputJob::OutputJobVerbosity verbosity) { DVcsJob* job = new DVcsJob(repository, this, verbosity); *job << "git" << "ls-files" << args; return job; } DVcsJob* GitPlugin::gitStash(const QDir& repository, const QStringList& args, OutputJob::OutputJobVerbosity verbosity) { DVcsJob* job = new DVcsJob(repository, this, verbosity); *job << "git" << "stash" << args; return job; } QString GitPlugin::curBranch(const QString &repository) { kDebug() << "Getting branch list"; QScopedPointer job(new DVcsJob(QDir(repository), this, OutputJob::Silent)); *job << "git" << "symbolic-ref" << "HEAD"; if (job->exec() && job->status() == KDevelop::VcsJob::JobSucceeded) { QString out = job->output().trimmed(); kDebug() << "Getting branch list" << out.right(out.size()-11); return out.right(out.size()-11); } return QString(); } QStringList GitPlugin::branches(const QString &repository) { QStringList branchListDirty; QScopedPointer job(branch(repository)); kDebug() << "Getting branch list"; if (job->exec() && job->status() == KDevelop::VcsJob::JobSucceeded) branchListDirty = job->output().split('\n', QString::SkipEmptyParts); else return QStringList(); QStringList branchList; foreach(QString branch, branchListDirty) { if (branch.contains('*')) { branch = branch.prepend('\n').section("\n*", 1); } else { branch = branch.prepend('\n').section('\n', 1); } branch = branch.trimmed(); branchList< GitPlugin::getAllCommits(const QString &repo) { static bool hasHash = false; if (!hasHash) { initBranchHash(repo); hasHash = true; } QStringList args; args << "--all" << "--pretty" << "--parents"; QScopedPointer job(gitRevList(repo, args)); bool ret = job->exec(); Q_ASSERT(ret && job->status()==VcsJob::JobSucceeded && "TODO: provide a fall back in case of failing"); QStringList commits = job->output().split('\n', QString::SkipEmptyParts); static QRegExp rx_com("commit \\w{40,40}"); QListcommitList; DVcsEvent item; //used to keep where we have empty/cross/branch entry //true if it's an active branch (then cross or branch) and false if not QVector additionalFlags(branchesShas.count()); foreach(int flag, additionalFlags) flag = false; //parse output for(int i = 0; i < commits.count(); ++i) { if (commits[i].contains(rx_com)) { kDebug() << "commit found in " << commits[i]; item.setCommit(commits[i].section(' ', 1, 1).trimmed()); // kDebug() << "commit is: " << commits[i].section(' ', 1); QStringList parents; QString parent = commits[i].section(' ', 2); int section = 2; while (!parent.isEmpty()) { /* kDebug() << "Parent is: " << parent;*/ parents.append(parent.trimmed()); section++; parent = commits[i].section(' ', section); } item.setParents(parents); //Avoid Merge string while (!commits[i].contains("Author: ")) ++i; item.setAuthor(commits[i].section("Author: ", 1).trimmed()); // kDebug() << "author is: " << commits[i].section("Author: ", 1); item.setDate(commits[++i].section("Date: ", 1).trimmed()); // kDebug() << "date is: " << commits[i].section("Date: ", 1); QString log; i++; //next line! while (i < commits.count() && !commits[i].contains(rx_com)) log += commits[i++]; --i; //while took commit line item.setLog(log.trimmed()); // kDebug() << "log is: " << log; //mask is used in CommitViewDelegate to understand what we should draw for each branch QList mask; //set mask (properties for each graph column in row) for(int i = 0; i < branchesShas.count(); ++i) { kDebug()<<"commit: " << item.getCommit(); if (branchesShas[i].contains(item.getCommit())) { mask.append(item.getType()); //we set type in setParents //check if parent from the same branch, if not then we have found a root of the branch //and will use empty column for all futher (from top to bottom) revisions //FIXME: we should set CROSS between parent and child (and do it when find merge point) additionalFlags[i] = false; foreach(const QString &sha, item.getParents()) { if (branchesShas[i].contains(sha)) additionalFlags[i] = true; } if (additionalFlags[i] == false) item.setType(DVcsEvent::INITIAL); //hasn't parents from the same branch, used in drawing } else { if (additionalFlags[i] == false) mask.append(DVcsEvent::EMPTY); else mask.append(DVcsEvent::CROSS); } kDebug() << "mask " << i << "is " << mask[i]; } item.setProperties(mask); commitList.append(item); } } //find and set merges, HEADS, require refactoring! for(QList::iterator iter = commitList.begin(); iter != commitList.end(); ++iter) { QStringList parents = iter->getParents(); //we need only only child branches if (parents.count() != 1) break; QString parent = parents[0]; QString commit = iter->getCommit(); bool parent_checked = false; int heads_checked = 0; for(int i = 0; i < branchesShas.count(); ++i) { //check parent if (branchesShas[i].contains(commit)) { if (!branchesShas[i].contains(parent)) { //parent and child are not in same branch //since it is list, than parent has i+1 index //set CROSS and HCROSS for(QList::iterator f_iter = iter; f_iter != commitList.end(); ++f_iter) { if (parent == f_iter->getCommit()) { for(int j = 0; j < i; ++j) { if(branchesShas[j].contains(parent)) f_iter->setPropetry(j, DVcsEvent::MERGE); else f_iter->setPropetry(j, DVcsEvent::HCROSS); } f_iter->setType(DVcsEvent::MERGE); f_iter->setPropetry(i, DVcsEvent::MERGE_RIGHT); kDebug() << parent << " is parent of " << commit; kDebug() << f_iter->getCommit() << " is merge"; parent_checked = true; break; } else f_iter->setPropetry(i, DVcsEvent::CROSS); } } } //mark HEADs if (!branchesShas[i].empty() && commit == branchesShas[i][0]) { iter->setType(DVcsEvent::HEAD); iter->setPropetry(i, DVcsEvent::HEAD); heads_checked++; kDebug() << "HEAD found"; } //some optimization if (heads_checked == branchesShas.count() && parent_checked) break; } } return commitList; } void GitPlugin::initBranchHash(const QString &repo) { QStringList branches = GitPlugin::branches(repo); kDebug() << "BRANCHES: " << branches; //Now root branch is the current branch. In future it should be the longest branch //other commitLists are got with git-rev-lits branch ^br1 ^ br2 QString root = GitPlugin::curBranch(repo); QScopedPointer job(gitRevList(repo, QStringList(root))); bool ret = job->exec(); Q_ASSERT(ret && job->status()==VcsJob::JobSucceeded && "TODO: provide a fall back in case of failing"); QStringList commits = job->output().split('\n', QString::SkipEmptyParts); // kDebug() << "\n\n\n commits" << commits << "\n\n\n"; branchesShas.append(commits); foreach(const QString &branch, branches) { if (branch == root) continue; QStringList args(branch); foreach(const QString &branch_arg, branches) { if (branch_arg != branch) //man gitRevList for '^' args<<'^' + branch_arg; } QScopedPointer job(gitRevList(repo, args)); bool ret = job->exec(); Q_ASSERT(ret && job->status()==VcsJob::JobSucceeded && "TODO: provide a fall back in case of failing"); QStringList commits = job->output().split('\n', QString::SkipEmptyParts); // kDebug() << "\n\n\n commits" << commits << "\n\n\n"; branchesShas.append(commits); } } //Actually we can just copy the output without parsing. So it's a kind of draft for future void GitPlugin::parseLogOutput(const DVcsJob * job, QList& commits) const { // static QRegExp rx_sep( "[-=]+" ); // static QRegExp rx_date( "date:\\s+([^;]*);\\s+author:\\s+([^;]*).*" ); static QRegExp rx_com( "commit \\w{1,40}" ); QStringList lines = job->output().split('\n', QString::SkipEmptyParts); DVcsEvent item; QString commitLog; for (int i=0; i commits; static QRegExp commitRegex( "^commit (\\w{8})\\w{32}" ); static QRegExp infoRegex( "^(\\w+):(.*)" ); QString contents = job->output(); QTextStream s(&contents); VcsEvent item; QString message; bool pushCommit = false; while (!s.atEnd()) { QString line = s.readLine(); if (commitRegex.exactMatch(line)) { if (pushCommit) { item.setMessage(message.trimmed()); commits.append(QVariant::fromValue(item)); } else { pushCommit = true; } VcsRevision rev; rev.setRevisionValue(commitRegex.cap(1), KDevelop::VcsRevision::GlobalNumber); item.setRevision(rev); message.clear(); } else if (infoRegex.exactMatch(line)) { QString cap1 = infoRegex.cap(1); if (cap1 == "Author") { item.setAuthor(infoRegex.cap(2).trimmed()); } else if (cap1 == "Date") { item.setDate(QDateTime::fromTime_t(infoRegex.cap(2).trimmed().split(' ')[0].toUInt())); } } else if (line.startsWith(" ")) { message += line.remove(0, 4); message += '\n'; } } item.setMessage(message.trimmed()); commits.append(QVariant::fromValue(item)); job->setResults(commits); } void GitPlugin::parseGitDiffOutput(DVcsJob* job) { VcsDiff diff; diff.setDiff(job->output()); diff.setBaseDiff(repositoryRoot(KUrl(job->directory().absolutePath()))); job->setResults(qVariantFromValue(diff)); } static VcsStatusInfo::State lsfilesToState(char id) { switch(id) { case 'H': return VcsStatusInfo::ItemUpToDate; //Cached case 'S': return VcsStatusInfo::ItemUpToDate; //Skip work tree case 'M': return VcsStatusInfo::ItemHasConflicts; //unmerged case 'R': return VcsStatusInfo::ItemDeleted; //removed/deleted case 'C': return VcsStatusInfo::ItemModified; //modified/changed case 'K': return VcsStatusInfo::ItemDeleted; //to be killed case '?': return VcsStatusInfo::ItemUnknown; //other } Q_ASSERT(false); return VcsStatusInfo::ItemUnknown; } void GitPlugin::parseGitStatusOutput_old(DVcsJob* job) { QStringList outputLines = job->output().split('\n', QString::SkipEmptyParts); KUrl d = job->directory().absolutePath(); QMap allStatus; foreach(const QString& line, outputLines) { VcsStatusInfo::State status = lsfilesToState(line[0].toAscii()); KUrl url = d; url.addPath(line.right(line.size()-2)); allStatus[url] = status; } QVariantList statuses; QMap< KUrl, VcsStatusInfo::State >::const_iterator it = allStatus.constBegin(), itEnd=allStatus.constEnd(); for(; it!=itEnd; ++it) { VcsStatusInfo status; status.setUrl(it.key()); status.setState(it.value()); statuses.append(qVariantFromValue(status)); } job->setResults(statuses); } void GitPlugin::parseGitStatusOutput(DVcsJob* job) { QStringList outputLines = job->output().split('\n', QString::SkipEmptyParts); const KUrl workingDir = job->directory().absolutePath(); const KUrl dotGit = dotGitDirectory(workingDir).absolutePath(); QVariantList statuses; QList processedFiles; foreach(const QString& line, outputLines) { //every line is 2 chars for the status, 1 space then the file desc QString curr=line.right(line.size()-3); QString state = line.left(2); int arrow = curr.indexOf("-> "); if(arrow>=0) curr = curr.right(curr.size()-arrow-3); KUrl fileUrl = dotGit; fileUrl.addPath(curr); processedFiles.append(fileUrl); VcsStatusInfo status; status.setUrl(fileUrl); status.setState(messageToState(state)); kDebug() << "Checking git status for " << line << curr << messageToState(state); statuses.append(qVariantFromValue(status)); } QStringList paths; QStringList oldcmd=job->dvcsCommand(); QStringList::const_iterator it=oldcmd.constBegin()+oldcmd.indexOf("--")+1, itEnd=oldcmd.constEnd(); for(; it!=itEnd; ++it) paths += *it; //here we add the already up to date files QStringList files = getLsFiles(job->directory(), QStringList() << "-c" << "--" << paths, OutputJob::Silent); foreach(const QString& file, files) { KUrl fileUrl = workingDir; fileUrl.addPath(file); if(!processedFiles.contains(fileUrl)) { VcsStatusInfo status; status.setUrl(fileUrl); status.setState(VcsStatusInfo::ItemUpToDate); statuses.append(qVariantFromValue(status)); } } job->setResults(statuses); } void GitPlugin::parseGitVersionOutput(DVcsJob* job) { QStringList versionString = job->output().trimmed().split(' ').last().split('.'); static QList minimumVersion = QList() << 1 << 7; kDebug() << "checking git version" << versionString << "against" << minimumVersion; m_oldVersion = false; foreach(int num, minimumVersion) { QString curr = versionString.takeFirst(); int valcurr = curr.toInt(); m_oldVersion |= valcurr job(lsFiles(directory, args, verbosity)); if (job->exec() && job->status() == KDevelop::VcsJob::JobSucceeded) return job->output().split('\n', QString::SkipEmptyParts); return QStringList(); } DVcsJob* GitPlugin::gitRevParse(const QString &repository, const QStringList &args, KDevelop::OutputJob::OutputJobVerbosity verbosity) { DVcsJob* job = new DVcsJob(QDir(repository), this, verbosity); *job << "git" << "rev-parse" << args; return job; } DVcsJob* GitPlugin::gitRevList(const QString &repository, const QStringList &args) { DVcsJob* job = new DVcsJob(QDir(repository), this); { *job << "git" << "rev-list" << args; return job; } } VcsStatusInfo::State GitPlugin::messageToState(const QString& msg) { Q_ASSERT(msg.size()==1 || msg.size()==2); VcsStatusInfo::State ret = VcsStatusInfo::ItemUnknown; if(msg.contains('U') || msg == "AA" || msg == "DD") ret = VcsStatusInfo::ItemHasConflicts; else switch(msg[0].toAscii()) { case 'M': ret = VcsStatusInfo::ItemModified; break; case 'A': ret = VcsStatusInfo::ItemAdded; break; case 'R': case 'C': ret = VcsStatusInfo::ItemModified; break; case ' ': ret = msg[1] == 'M' ? VcsStatusInfo::ItemModified : VcsStatusInfo::ItemDeleted; break; case 'D': ret = VcsStatusInfo::ItemDeleted; break; case '?': ret = VcsStatusInfo::ItemUnknown; break; default: kDebug() << "Git status not identified:" << msg; break; } return ret; } StandardJob::StandardJob(IPlugin* parent, KJob* job, OutputJob::OutputJobVerbosity verbosity) : VcsJob(parent, verbosity) , m_job(job) , m_plugin(parent) , m_status(JobNotStarted) {} void StandardJob::start() { connect(m_job, SIGNAL(result(KJob*)), SLOT(result(KJob*))); m_job->start(); m_status=JobRunning; } void StandardJob::result(KJob* job) { m_status=job->error() == 0? JobSucceeded : JobFailed; emitResult(); } VcsJob* GitPlugin::copy(const KUrl& localLocationSrc, const KUrl& localLocationDstn) { //TODO: Probably we should "git add" after return new StandardJob(this, KIO::copy(localLocationSrc, localLocationDstn), KDevelop::OutputJob::Silent); } VcsJob* GitPlugin::move(const KUrl& source, const KUrl& destination) { QDir dir = urlDir(source); QStringList otherStr = getLsFiles(dir, QStringList() << "--others" << "--" << source.toLocalFile(), KDevelop::OutputJob::Silent); if(otherStr.isEmpty()) { DVcsJob* job = new DVcsJob(dir, this, KDevelop::OutputJob::Verbose); *job << "git" << "mv" << source.toLocalFile() << destination.toLocalFile(); return job; } else { return new StandardJob(this, KIO::move(source, destination), KDevelop::OutputJob::Silent); } } void GitPlugin::parseGitRepoLocationOutput(DVcsJob* job) { job->setResults(qVariantFromValue(KUrl(job->output()))); } VcsJob* GitPlugin::repositoryLocation(const KUrl& localLocation) { DVcsJob* job = new DVcsJob(urlDir(localLocation), this); //Probably we should check first if origin is the proper remote we have to use but as a first attempt it works *job << "git" << "config" << "remote.origin.url"; connect(job, SIGNAL(readyForParsing(KDevelop::DVcsJob*)), SLOT(parseGitRepoLocationOutput(KDevelop::DVcsJob*))); return job; } VcsJob* GitPlugin::pull(const KDevelop::VcsLocation& localOrRepoLocationSrc, const KUrl& localRepositoryLocation) { DVcsJob* job = new DVcsJob(urlDir(localRepositoryLocation), this); job->setCommunicationMode(KProcess::MergedChannels); *job << "git" << "pull"; if(!localOrRepoLocationSrc.localUrl().isEmpty()) *job << localOrRepoLocationSrc.localUrl().url(); return job; } VcsJob* GitPlugin::push(const KUrl& localRepositoryLocation, const KDevelop::VcsLocation& localOrRepoLocationDst) { DVcsJob* job = new DVcsJob(urlDir(localRepositoryLocation), this); job->setCommunicationMode(KProcess::MergedChannels); *job << "git" << "push"; if(!localOrRepoLocationDst.localUrl().isEmpty()) *job << localOrRepoLocationDst.localUrl().url(); return job; } VcsJob* GitPlugin::resolve(const KUrl::List& localLocations, IBasicVersionControl::RecursionMode recursion) { return add(localLocations, recursion); } VcsJob* GitPlugin::update(const KUrl::List& localLocations, const KDevelop::VcsRevision& rev, IBasicVersionControl::RecursionMode recursion) { if(rev.revisionType()==VcsRevision::Special && rev.revisionValue().value()==VcsRevision::Head) { return pull(VcsLocation(), localLocations.first()); } else { DVcsJob* job = new DVcsJob(urlDir(localLocations.first().toLocalFile()), this); { //Probably we should check first if origin is the proper remote we have to use but as a first attempt it works *job << "git" << "checkout" << rev.revisionValue().toString() << "--"; *job << (recursion == IBasicVersionControl::Recursive ? localLocations : preventRecursion(localLocations)); return job; } } } class GitVcsLocationWidget : public KDevelop::StandardVcsLocationWidget { public: GitVcsLocationWidget(QWidget* parent = 0, Qt::WindowFlags f = 0) : StandardVcsLocationWidget(parent, f) {} virtual bool isCorrect() const { return true; } }; KDevelop::VcsLocationWidget* GitPlugin::vcsLocation(QWidget* parent) const { return new GitVcsLocationWidget(parent); } diff --git a/plugins/git/tests/initTest.cpp b/plugins/git/tests/initTest.cpp index bf89e33354..617ef0ad47 100644 --- a/plugins/git/tests/initTest.cpp +++ b/plugins/git/tests/initTest.cpp @@ -1,338 +1,367 @@ /*************************************************************************** * This file was partly taken from KDevelop's cvs plugin * * Copyright 2007 Robert Gruber * * * * Adapted for Git * * Copyright 2008 Evgeniy Ivanov * * * * 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) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * 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, see . * ***************************************************************************/ #include "initTest.h" #include #include #include #include #include #include #include #include +#include #include "../gitplugin.h" #define VERIFYJOB(j) \ QVERIFY(j); QVERIFY(j->exec()); QVERIFY((j)->status() == KDevelop::VcsJob::JobSucceeded) const QString tempDir = QDir::tempPath(); const QString gitTest_BaseDir(tempDir + "/kdevGit_testdir/"); const QString gitTest_BaseDir2(tempDir + "/kdevGit_testdir2/"); const QString gitRepo(gitTest_BaseDir + ".git"); const QString gitSrcDir(gitTest_BaseDir + "src/"); const QString gitTest_FileName("testfile"); const QString gitTest_FileName2("foo"); const QString gitTest_FileName3("bar"); using namespace KDevelop; void GitInitTest::initTestCase() { AutoTestShell::init(); m_testCore = new KDevelop::TestCore(); m_testCore->initialize(KDevelop::Core::NoUi); m_plugin = new GitPlugin(m_testCore); removeTempDirs(); // Now create the basic directory structure QDir tmpdir(tempDir); tmpdir.mkdir(gitTest_BaseDir); tmpdir.mkdir(gitSrcDir); tmpdir.mkdir(gitTest_BaseDir2); } void GitInitTest::cleanupTestCase() { delete m_plugin; m_testCore->cleanup(); delete m_testCore; if (QFileInfo(gitTest_BaseDir).exists()) KIO::NetAccess::del(KUrl(gitTest_BaseDir), 0); if (QFileInfo(gitTest_BaseDir2).exists()) KIO::NetAccess::del(KUrl(gitTest_BaseDir2), 0); } void GitInitTest::repoInit() { kDebug() << "Trying to init repo"; // make job that creates the local repository VcsJob* j = m_plugin->init(KUrl(gitTest_BaseDir)); VERIFYJOB(j); //check if the CVSROOT directory in the new local repository exists now QVERIFY(QFileInfo(gitRepo).exists()); //check if isValidDirectory works QVERIFY(m_plugin->isValidDirectory(KUrl(gitTest_BaseDir))); //and for non-git dir, I hope nobody has /tmp under git QVERIFY(!m_plugin->isValidDirectory(KUrl("/tmp"))); //we have nothing, so ouput should be empty DVcsJob * j2 = m_plugin->gitRevParse(gitRepo, QStringList(QString("--branches"))); QVERIFY(j2); QVERIFY(j2->exec()); QString out = j2->output(); QVERIFY(j2->output().isEmpty()); } void GitInitTest::addFiles() { kDebug() << "Adding files to the repo"; //we start it after repoInit, so we still have empty git repo QFile f(gitTest_BaseDir + gitTest_FileName); if (f.open(QIODevice::WriteOnly)) { QTextStream input(&f); input << "HELLO WORLD"; } f.close(); f.setFileName(gitTest_BaseDir + gitTest_FileName2); if (f.open(QIODevice::WriteOnly)) { QTextStream input(&f); input << "No, bar()!"; } f.close(); //test git-status exitCode (see DVcsJob::setExitCode). VcsJob* j = m_plugin->status(KUrl::List(gitTest_BaseDir)); VERIFYJOB(j); // /tmp/kdevGit_testdir/ and testfile j = m_plugin->add(KUrl::List(gitTest_BaseDir + gitTest_FileName)); VERIFYJOB(j); f.setFileName(gitSrcDir + gitTest_FileName3); if (f.open(QIODevice::WriteOnly)) { QTextStream input(&f); input << "No, foo()! It's bar()!"; } f.close(); //test git-status exitCode again j = m_plugin->status(KUrl::List(gitTest_BaseDir)); VERIFYJOB(j); //repository path without trailing slash and a file in a parent directory // /tmp/repo and /tmp/repo/src/bar j = m_plugin->add(KUrl::List(QStringList(gitSrcDir + gitTest_FileName3))); VERIFYJOB(j); //let's use absolute path, because it's used in ContextMenus j = m_plugin->add(KUrl::List(QStringList(gitTest_BaseDir + gitTest_FileName2))); VERIFYJOB(j); //Now let's create several files and try "git add file1 file2 file3" f.setFileName(gitTest_BaseDir + "file1"); if (f.open(QIODevice::WriteOnly)) { QTextStream input(&f); input << "file1"; } f.close(); f.setFileName(gitTest_BaseDir + "file2"); if (f.open(QIODevice::WriteOnly)) { QTextStream input(&f); input << "file2"; } f.close(); KUrl::List multipleFiles; multipleFiles << (gitTest_BaseDir + "file1"); multipleFiles << (gitTest_BaseDir + "file2"); j = m_plugin->add(multipleFiles); VERIFYJOB(j); } void GitInitTest::commitFiles() { kDebug() << "Committing..."; //we start it after addFiles, so we just have to commit VcsJob* j = m_plugin->commit(QString("Test commit"), KUrl::List(gitTest_BaseDir)); VERIFYJOB(j); //test git-status exitCode one more time. j = m_plugin->status(KUrl::List(gitTest_BaseDir)); VERIFYJOB(j); //since we commited the file to the "pure" repository, .git/refs/heads/master should exist //TODO: maybe other method should be used QString headRefName(gitRepo + "/refs/heads/master"); QVERIFY(QFileInfo(headRefName).exists()); //Test the results of the "git add" DVcsJob* jobLs = new DVcsJob(gitTest_BaseDir, m_plugin); *jobLs << "git" << "ls-tree" << "--name-only" << "-r" << "HEAD"; if (jobLs->exec() && jobLs->status() == KDevelop::VcsJob::JobSucceeded) { QStringList files = jobLs->output().split("\n"); QVERIFY(files.contains(gitTest_FileName)); QVERIFY(files.contains(gitTest_FileName2)); QVERIFY(files.contains("src/" + gitTest_FileName3)); } QString firstCommit; QFile headRef(headRefName); if (headRef.open(QIODevice::ReadOnly)) { QTextStream output(&headRef); output >> firstCommit; } headRef.close(); QVERIFY(!firstCommit.isEmpty()); kDebug() << "Committing one more time"; //let's try to change the file and test "git commit -a" QFile f(gitTest_BaseDir + gitTest_FileName); if (f.open(QIODevice::WriteOnly)) { QTextStream input(&f); - input << "Just another HELLO WORLD"; + input << "Just another HELLO WORLD\n"; } f.close(); //add changes j = m_plugin->add(KUrl::List(QStringList(gitTest_BaseDir + gitTest_FileName))); VERIFYJOB(j); j = m_plugin->commit(QString("KDevelop's Test commit2"), KUrl::List(gitTest_BaseDir)); VERIFYJOB(j); QString secondCommit; if (headRef.open(QIODevice::ReadOnly)) { QTextStream output(&headRef); output >> secondCommit; } headRef.close(); QVERIFY(!secondCommit.isEmpty()); QVERIFY(firstCommit != secondCommit); } // void GitInitTest::cloneRepository() // { // kDebug() << "Do not clone people, clone Git repos!"; // // make job that clones the local repository, created in the previous test // DVcsJob* j = m_proxy->createWorkingCopy(KUrl(gitTest_BaseDir), KUrl(gitTest_BaseDir2)); // QVERIFY( j ); // // // try to start the job // QVERIFY( j->exec() ); // // //check if the .git directory in the new local repository exists now // QVERIFY( QFileInfo(QString(gitTest_BaseDir2"kdevGit_testdir/.git/")).exists() ); // } void GitInitTest::testInit() { repoInit(); } void GitInitTest::testAdd() { addFiles(); } void GitInitTest::testCommit() { commitFiles(); } void GitInitTest::testBranching() { DVcsJob* j = m_plugin->branch(gitTest_BaseDir); VERIFYJOB(j); QString curBranch = m_plugin->curBranch(gitTest_BaseDir); QCOMPARE(curBranch, QString("master")); QString newBranch("new"); j = m_plugin->branch(gitTest_BaseDir, QString("master"), newBranch); VERIFYJOB(j); QVERIFY(m_plugin->branches(gitTest_BaseDir).contains(newBranch)); j = m_plugin->switchBranch(gitTest_BaseDir, newBranch); VERIFYJOB(j); QCOMPARE(m_plugin->curBranch(gitTest_BaseDir), newBranch); j = m_plugin->branch(gitTest_BaseDir, QString("master"), QString(), QStringList("-D")); VERIFYJOB(j); QVERIFY(!m_plugin->branches(gitTest_BaseDir).contains(QString("master"))); } void GitInitTest::revHistory() { QList commits = m_plugin->getAllCommits(gitTest_BaseDir); QVERIFY(!commits.isEmpty()); QStringList logMessages; for (int i = 0; i < commits.count(); ++i) logMessages << commits[i].getLog(); QCOMPARE(commits.count(), 2); QCOMPARE(logMessages[0], QString("KDevelop's Test commit2")); //0 is later than 1! QCOMPARE(logMessages[1], QString("Test commit")); QVERIFY(commits[1].getParents().isEmpty()); //0 is later than 1! QVERIFY(!commits[0].getParents().isEmpty()); //initial commit is on the top QVERIFY(commits[1].getCommit().contains(QRegExp("^\\w{,40}$"))); QVERIFY(commits[0].getCommit().contains(QRegExp("^\\w{,40}$"))); QVERIFY(commits[0].getParents()[0].contains(QRegExp("^\\w{,40}$"))); } +void GitInitTest::testAnnotation() +{ + // called after commitFiles + QFile f(gitTest_BaseDir + gitTest_FileName); + QVERIFY(f.open(QIODevice::Append)); + QTextStream input(&f); + input << "An appended line"; + f.close(); + + VcsJob* j = m_plugin->commit(QString("KDevelop's Test commit3"), KUrl::List(gitTest_BaseDir)); + VERIFYJOB(j); + + j = m_plugin->annotate(KUrl(gitTest_BaseDir + gitTest_FileName), VcsRevision::createSpecialRevision(VcsRevision::Head)); + VERIFYJOB(j); + + QList results = j->fetchResults().toList(); + QCOMPARE(results.size(), 2); + QVERIFY(results.at(0).canConvert()); + VcsAnnotationLine annotation = results.at(0).value(); + QCOMPARE(annotation.lineNumber(), 0); + QCOMPARE(annotation.commitMessage(), QString("KDevelop's Test commit2")); + + QVERIFY(results.at(1).canConvert()); + annotation = results.at(1).value(); + QCOMPARE(annotation.lineNumber(), 1); + QCOMPARE(annotation.commitMessage(), QString("KDevelop's Test commit3")); +} + void GitInitTest::removeTempDirs() { if (QFileInfo(gitTest_BaseDir).exists()) if (!KIO::NetAccess::del(KUrl(gitTest_BaseDir), 0)) qDebug() << "KIO::NetAccess::del(" << gitTest_BaseDir << ") returned false"; if (QFileInfo(gitTest_BaseDir2).exists()) if (!KIO::NetAccess::del(KUrl(gitTest_BaseDir2), 0)) qDebug() << "KIO::NetAccess::del(" << gitTest_BaseDir2 << ") returned false"; } QTEST_KDEMAIN(GitInitTest, GUI) // #include "gittest.moc" diff --git a/plugins/git/tests/initTest.h b/plugins/git/tests/initTest.h index 25e0ef13f8..3258cc88d4 100644 --- a/plugins/git/tests/initTest.h +++ b/plugins/git/tests/initTest.h @@ -1,64 +1,65 @@ /*************************************************************************** * This file was partly taken from KDevelop's cvs plugin * * Copyright 2007 Robert Gruber * * * * Adapted for Git * * Copyright 2008 Evgeniy Ivanov * * * * 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) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * 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, see . * ***************************************************************************/ #ifndef GIT_INIT_H #define GIT_INIT_H #include class GitPlugin; namespace KDevelop { class TestCore; } class GitInitTest: public QObject { Q_OBJECT private: void repoInit(); void addFiles(); void commitFiles(); // void cloneRepository(); // void importTestData(); // void checkoutTestData(); private slots: void initTestCase(); void testInit(); void testAdd(); void testCommit(); void testBranching(); void revHistory(); + void testAnnotation(); void cleanupTestCase(); private: KDevelop::TestCore* m_testCore; GitPlugin* m_plugin; void removeTempDirs(); }; #endif diff --git a/plugins/pastebin/kdevpastebin.desktop b/plugins/pastebin/kdevpastebin.desktop index b59e909d06..b7046aaa0b 100644 --- a/plugins/pastebin/kdevpastebin.desktop +++ b/plugins/pastebin/kdevpastebin.desktop @@ -1,68 +1,68 @@ [Desktop Entry] Type=Service Icon=edit-paste Exec=blubb Comment=This plugin helps you upload your patches to Pastebin service -Comment[de]=Dieses Modul hilft Ihnen Ihren Patch auf einen Pastebin-Service hochzuladen. +Comment[de]=Dieses Modul hilft Ihnen, Ihre Patches zu einen Pastebin-Dienst hochzuladen. Comment[en_GB]=This plugin helps you upload your patches to Pastebin service Comment[es]=Este complemento le ayuda a enviar sus parches al servicio Pastebin Comment[et]=See plugin aitab üles laadida paiku Pastebini teenusesse Comment[it]=Questa estensione consente di caricare le tue patch su Pastebin Comment[nds]=Mit dit Moduul laat sik Kodeplasters na den Pastebin-Deenst hoochladen. Comment[nl]=Deze plugin helpt u om uw patches naar de Pastebin-service te uploaden Comment[pt]=Este 'plugin' ajuda-o a enviar as suas modificações para o serviço Pastebin Comment[pt_BR]=Este plugin o auxilia a enviar suas modificações para o serviço Pastebin Comment[sv]=Det här insticksprogrammet hjälper till att ladda upp programfixar till tjänsten Pastebin Comment[uk]=За допомогою цього додатка ви зможете вивантажувати ваші латки до служби Pastebin Comment[x-test]=xxThis plugin helps you upload your patches to Pastebin servicexx Comment[zh_CN]=此插件帮助您将补丁上传到 Pastebin 服务 Comment[zh_TW]=此外掛程式讓您上傳您的修補到 Pastebin 服務內 Name=Pastebin Name[ca]=Pastebin Name[da]=Pastebin Name[de]=Pastebin Name[en_GB]=Pastebin Name[es]=Pastebin Name[et]=Pastebin Name[fr]=Pastebin Name[it]=Pastebin Name[ja]=Pastebin Name[nds]=Pastebin Name[nl]=Pastebin Name[pt]=Colagem Name[pt_BR]=Colagem Name[ru]=Pastebin Name[sv]=Pastebin Name[uk]=Pastebin Name[x-test]=xxPastebinxx Name[zh_CN]=Pastebin Name[zh_TW]=Pastebin GenericName=Pastebin GenericName[ca]=Pastebin GenericName[da]=Pastebin GenericName[de]=Pastebin GenericName[en_GB]=Pastebin GenericName[es]=Pastebin GenericName[et]=Pastebin GenericName[fr]=Pastebin GenericName[it]=Pastebin GenericName[ja]=Pastebin GenericName[nds]=Pastebin GenericName[nl]=Pastebin GenericName[pt]=Colagem GenericName[pt_BR]=Colagem GenericName[ru]=Pastebin GenericName[sv]=Pastebin GenericName[uk]=Pastebin GenericName[x-test]=xxPastebinxx GenericName[zh_CN]=Pastebin GenericName[zh_TW]=Pastebin ServiceTypes=KDevelop/Plugin X-KDE-Library=kdevpastebin X-KDevelop-Version=11 X-KDevelop-Category=Global X-KDE-PluginInfo-Name=kdevpastebin X-KDE-PluginInfo-Author=Aleix Pol X-KDE-PluginInfo-License=GPL X-KDevelop-Mode=GUI X-KDevelop-Interfaces=org.kdevelop.IPatchExporter diff --git a/plugins/patchreview/kdevpatchreview.desktop b/plugins/patchreview/kdevpatchreview.desktop index d9636ac1ae..c13577f6d8 100644 --- a/plugins/patchreview/kdevpatchreview.desktop +++ b/plugins/patchreview/kdevpatchreview.desktop @@ -1,89 +1,89 @@ [Desktop Entry] Type=Service Icon=applications-engineering Exec=blubb Comment=This plugin allows reviewing patches directly in the editor. Comment[bg]=Тази приставка позволява преглеждане на кръпки направо в редактора. Comment[ca]=Aquest connector permet revisar pedaços directament en l'editor. Comment[ca@valencia]=Este connector permet revisar pedaços directament en l'editor. Comment[da]=Dette plugin tillader at gennemgå rettelser direkte i editoren. -Comment[de]=Dieses Modul ermöglich es, Patches direkt im Editor zu betrachten. +Comment[de]=Dieses Modul ermöglich es, Patches direkt im Editor durchzusehen. Comment[en_GB]=This plugin allows reviewing patches directly in the editor. Comment[es]=Este complemento permite revisar parches directamente en el editor. Comment[et]=See plugin võimaldab paiku üle vaadata otse redaktoris. Comment[fr]=Ce module externe permet de réviser des correctifs directement dans l'éditeur. Comment[gl]=Este engadido permite revisar parches directamente no editor. Comment[it]=Questa estensione permette di rivedere le patch direttamente nell'editor. Comment[nb]=Dette programtillegget kan gjennomgå programlapper direkte i redigeringen. Comment[nds]=Mit dit Moduul laat sik Kodeplasters direktemang binnen den Editor nakieken. Comment[nl]=Deze plugin laat patches herzien direct in de editor. Comment[pl]=Ta wtyczka pozwala na przeglądanie poprawek bezpośrednio w edytorze. Comment[pt]=Este 'plugin' permite obter as modificações directamente no editor. Comment[pt_BR]=Este plugin permite obter as modificações diretamente no editor. Comment[sl]=Vstavek omogoča pregledovanje popravkov neposredno iz urejevalnika. Comment[sv]=Insticksprogrammet gör det möjligt att direkt granska programfixar i editorn. Comment[uk]=За допомогою цього додатка ви зможете рецензувати латки безпосередньо у редакторі. Comment[x-test]=xxThis plugin allows reviewing patches directly in the editor.xx Comment[zh_CN]=此插件允许在编辑器中直接审阅补丁。 Comment[zh_TW]=此外掛程式任您可以直接在編輯器裡檢視修補。 Name=Patch Review Name[bg]=Преглед на кръпки Name[ca]=Revisa pedaç Name[ca@valencia]=Revisa pedaç Name[da]=Gennemgang af rettelser Name[de]=Patch-Durchsicht Name[en_GB]=Patch Review Name[es]=Revisión de parches Name[et]=Paikade ülevaatamine Name[fr]=Révision de correctifs Name[gl]=Revisor de parches Name[it]=Revisione patch Name[ja]=パッチレビュー Name[nb]=Lappegjennomgang Name[nds]=Plasternakiek Name[nl]=Patchoverzicht Name[pl]=Przeglądanie poprawek Name[pt]=Revisão da Modificação Name[pt_BR]=Revisão da Modificação Name[sl]=Pregledovanje popravkov Name[sv]=Granska programfixar Name[tr]=Yama Gözden Geçirmesi Name[uk]=Рецензування латки Name[x-test]=xxPatch Reviewxx Name[zh_CN]=补丁审阅 Name[zh_TW]=修補檢視 GenericName=Patch Review GenericName[bg]=Преглед на кръпки GenericName[ca]=Revisa pedaç GenericName[ca@valencia]=Revisa pedaç GenericName[da]=Gennemgang af rettelser GenericName[de]=Patch-Durchsicht GenericName[en_GB]=Patch Review GenericName[es]=Revisión de parches GenericName[et]=Paikade ülevaatamine GenericName[fr]=Révision de correctifs GenericName[gl]=Revisor de parches GenericName[it]=Revisione patch GenericName[ja]=パッチレビュー GenericName[nb]=Lappegjennomgang GenericName[nds]=Plasternakiek GenericName[nl]=Patchoverzicht GenericName[pl]=Przeglądanie poprawek GenericName[pt]=Revisão da Modificação GenericName[pt_BR]=Revisão da Modificação GenericName[sl]=Pregledovanje popravkov GenericName[sv]=Granska programfixar GenericName[tr]=Yama Gözden Geçirmesi GenericName[uk]=Рецензування латки GenericName[x-test]=xxPatch Reviewxx GenericName[zh_CN]=补丁审阅 GenericName[zh_TW]=修補檢視 ServiceTypes=KDevelop/Plugin X-KDE-Library=kdevpatchreview X-KDevelop-Version=11 X-KDevelop-Category=Global X-KDevelop-Interfaces=org.kdevelop.IPatchReview X-KDE-PluginInfo-Name=kdevpatchreview X-KDE-PluginInfo-Author=David Nolden X-KDE-PluginInfo-License=GPL X-KDevelop-Mode=GUI diff --git a/plugins/snippet/kdevsnippet.desktop b/plugins/snippet/kdevsnippet.desktop index 4d8a94d7fd..395f5bf322 100644 --- a/plugins/snippet/kdevsnippet.desktop +++ b/plugins/snippet/kdevsnippet.desktop @@ -1,93 +1,93 @@ [Desktop Entry] Type=Service Exec=blubb Icon=edit-copy Comment=This plugin allows to store code snippets and insert them into open files Comment[bg]=Тази приставка позволява съхраняването на временни фрагменти от програмни низове и вмъкването им в отворени файлове Comment[ca]=Aquest connector permet desar retalls de codi i inserir-los a fitxers oberts Comment[ca@valencia]=Este connector permet alçar retalls de codi i inserir-los a fitxers oberts Comment[da]=Dette plugin tillader at gemme kodestumper og indsætte dem i åbne filer -Comment[de]=Dieses Modul bietet die Möglichkeit, Quelltextbausteine zu speichern und in geöffnete Dateien einzufügen. +Comment[de]=Dieses Modul bietet die Möglichkeit, Textbausteine zu speichern und in geöffnete Dateien einzufügen. Comment[el]=Αυτό το πρόσθετο σας επιτρέπει να αποθηκεύσετε δείγματα κώδικα και να τα εισάγετε σε ανοιχτά αρχεία Comment[en_GB]=This plugin allows to store code snippets and insert them into open files Comment[es]=Este complemento le permite almacenar fragmentos de código e insertarlos en los archivos abiertos Comment[et]=See plugin võimaldab salvestada koodijuppe ja lisada neid avatud failidesse Comment[fr]=Ce module externe permet d'enregistrer des fragments de code et de les insérer dans les fichiers ouverts Comment[gl]=Este engadido permite gardar fragmentos de código e inserilos en ficheiros abertos Comment[it]=Questa estensione permette di memorizzare i frammenti di codice e inserirli in file aperti Comment[nb]=Dette programtillegget kan lagre kodebiter og sette dem inn i åpne filer Comment[nds]=Dit Moduul wohrt Kodesnippels un föögt se na opmaakt Dateien in Comment[nl]=Deze plugin biedt het opslaan van codeknipsels en voegt ze in in open bestanden Comment[pl]=Ta wtyczka umożliwia zapisanie fragmentów kodu i wstawianie ich później otwartych plików Comment[pt]=Este 'plugin' permite-lhe guardar excertos de código e inseri-los nos ficheiros abertos Comment[pt_BR]=Esta extensão permite guardar trechos de código e inserí-los em arquivos abertos Comment[ru]=Этот модуль позволяет хранить фрагменты кода и вставлять их в открытые файлы. Comment[sl]=Vstavek omogoča shranjevanje delčkov kode in vstavljanje delčkov v odprte datoteke Comment[sv]=Insticksprogrammet gör det möjligt att lagra kodsnuttar och infoga dem i öppna filer Comment[uk]=За допомогою цього додатка можна зберігати фрагменти коду і вставляти їх до відкритих файлів Comment[x-test]=xxThis plugin allows to store code snippets and insert them into open filesxx Comment[zh_CN]=此插件允许存储代码片断并插入到打开的文件中去 Comment[zh_TW]=此外掛程式允許儲存程式碼片段,並將它們插入開啟的檔案中 Name=Code Snippets Support Name[bg]=Поддръжка на отрязъци от код Name[ca]=Implementació de retalls de codi Name[ca@valencia]=Implementació de retalls de codi Name[da]=Understøttelse af kodestumper -Name[de]=Unterstützung für Quelltextbausteine +Name[de]=Unterstützung für Textbausteine Name[el]=Υποστήριξη δειγμάτων κώδικα Name[en_GB]=Code Snippets Support Name[es]=Implementación de fragmentos de código Name[et]=Koodijuppide toetus Name[fr]=Prise en charge des fragments de code Name[gl]=Soporte de fragmentos de código Name[it]=Supporto frammenti di codice Name[ja]=コードスニペットのサポート Name[nb]=Støtte for kodebiter Name[nds]=Kodesnippels-Ünnerstütten Name[nl]=Ondersteuning voor codeknipsels Name[pa]=ਕੋਡ ਸਨਿੱਪਟ ਸਹਿਯੋਗ Name[pl]=Obsługa fragmentów kodu Name[pt]=Suporte para Excertos de Código Name[pt_BR]=Suporte a trechos de código Name[sl]=Podpora za delčke kode Name[sv]=Stöd för kodsnuttar Name[tr]=Kod Parçacıkları Desteği Name[uk]=Підтримка фрагментів коду Name[x-test]=xxCode Snippets Supportxx Name[zh_CN]=代码片断支持 Name[zh_TW]=程式碼片段支援 GenericName=Code Snippets Support GenericName[bg]=Поддръжка на отрязъци от код GenericName[ca]=Implementació de retalls de codi GenericName[ca@valencia]=Implementació de retalls de codi GenericName[da]=Understøttelse af kodestumper -GenericName[de]=Unterstützung für Quelltextbausteine +GenericName[de]=Unterstützung für Textbausteine GenericName[en_GB]=Code Snippets Support GenericName[es]=Implementación de fragmentos de código GenericName[et]=Koodijuppide toetus GenericName[fr]=Prise en charge des fragments de code GenericName[gl]=Soporte de fragmentos de código GenericName[it]=Supporto frammenti di codice GenericName[ja]=コードスニペットのサポート GenericName[nb]=Støtte for kodebiter GenericName[nds]=Kodesnippels-Ünnerstütten GenericName[nl]=Ondersteuning voor codeknipsels GenericName[pl]=Obsługa fragmentów kodu GenericName[pt]=Suporte para Excertos de Código GenericName[pt_BR]=Suporte a trechos de código GenericName[sl]=Podpora za delčke kode GenericName[sv]=Stöd för kodsnuttar GenericName[tr]=Kod Parçacıkları Desteği GenericName[uk]=Підтримка фрагментів коду GenericName[x-test]=xxCode Snippets Supportxx GenericName[zh_CN]=代码片断支持 GenericName[zh_TW]=程式碼片段支援 ServiceTypes=KDevelop/Plugin X-KDE-Library=kdevsnippet X-KDE-PluginInfo-Name=kdevsnippet X-KDE-PluginInfo-Author=Robert Gruber, Milian Wolff X-KDE-PluginInfo-Version=0.75 X-KDE-PluginInfo-License=GPL X-KDevelop-Version=11 X-KDevelop-Category=Global X-KDevelop-Mode=GUI diff --git a/project/abstractfilemanagerplugin.cpp b/project/abstractfilemanagerplugin.cpp index 76f631b60a..45579a90e5 100644 --- a/project/abstractfilemanagerplugin.cpp +++ b/project/abstractfilemanagerplugin.cpp @@ -1,509 +1,507 @@ /*************************************************************************** * This file is part of KDevelop * * Copyright 2010 Milian Wolff * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library 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 Library 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 "abstractfilemanagerplugin.h" #include "filemanagerlistjob.h" #include "projectmodel.h" #include "helper.h" #include #include #include #include #include #include #include #include #include #include #include #define ifDebug(x) using namespace KDevelop; //BEGIN Helper namespace { /** * Returns the parent folder item for a given item or the project root item if there is no parent. */ ProjectFolderItem* getParentFolder(ProjectBaseItem* item) { if ( item->parent() ) { return static_cast(item->parent()); } else { return item->project()->projectItem(); } } } //END Helper //BEGIN Private struct AbstractFileManagerPlugin::Private { explicit Private(AbstractFileManagerPlugin* qq) : q(qq) { } AbstractFileManagerPlugin* q; /// @p forceRecursion if true, existing folders will be re-read no matter what KJob* eventuallyReadFolder( ProjectFolderItem* item, const bool forceRecursion = false ); void addJobItems(FileManagerListJob* job, ProjectFolderItem* baseItem, const KIO::UDSEntryList& entries, const bool forceRecursion); void deleted(const QString &path); void created(const QString &path); void projectClosing(IProject* project); void jobFinished(KJob* job); /// Stops watching the given folder for changes, only useful for local files. void stopWatcher(ProjectFolderItem* folder); /// Continues watching the given folder for changes. void continueWatcher(ProjectFolderItem* folder); /// Common renaming function. bool rename( ProjectBaseItem* item, const KUrl& destination); QMap m_watchers; QMap > m_projectJobs; }; void AbstractFileManagerPlugin::Private::projectClosing(IProject* project) { if ( m_projectJobs.contains(project) ) { // make sure the import job does not live longer than the project // see also addLotsOfFiles test foreach( KJob* job, m_projectJobs[project] ) { - kDebug() << "killing project job:" << job; + kDebug(9517) << "killing project job:" << job; job->kill(); } m_projectJobs.remove(project); } delete m_watchers.take(project); } KJob* AbstractFileManagerPlugin::Private::eventuallyReadFolder( ProjectFolderItem* item, const bool forceRecursion ) { FileManagerListJob* listJob = new FileManagerListJob( item, forceRecursion ); m_projectJobs[ item->project() ] << listJob; - kDebug() << "adding job" << listJob << item->url() << "for project" << item->project(); + kDebug(9517) << "adding job" << listJob << item->url() << "for project" << item->project(); ICore::self()->runController()->registerJob( listJob ); q->connect( listJob, SIGNAL(finished(KJob*)), q, SLOT(jobFinished(KJob*)) ); q->connect( listJob, SIGNAL(entries(FileManagerListJob*, ProjectFolderItem*, KIO::UDSEntryList, bool)), q, SLOT(addJobItems(FileManagerListJob*, ProjectFolderItem*, KIO::UDSEntryList, bool)) ); return listJob; } void AbstractFileManagerPlugin::Private::jobFinished(KJob* job) { FileManagerListJob* gmlJob = qobject_cast(job); Q_ASSERT(gmlJob); - kDebug() << gmlJob; + kDebug(9517) << gmlJob; m_projectJobs[ gmlJob->item()->project() ].removeOne( job ); } void AbstractFileManagerPlugin::Private::addJobItems(FileManagerListJob* job, ProjectFolderItem* baseItem, const KIO::UDSEntryList& entries, const bool forceRecursion) { if ( entries.empty() ) { return; } - kDebug() << "reading entries of" << baseItem->url(); + kDebug(9517) << "reading entries of" << baseItem->url(); // build lists of valid files and folders with relative urls to the project folder KUrl::List files; KUrl::List folders; foreach ( const KIO::UDSEntry& entry, entries ) { QString name = entry.stringValue( KIO::UDSEntry::UDS_NAME ); if (name == "." || name == "..") { continue; } KUrl url = baseItem->url(); url.addPath( name ); if ( !q->isValid( url, entry.isDir(), baseItem->project() ) ) { continue; } else { if ( entry.isDir() ) { if( entry.isLink() ) { KUrl linkedUrl = baseItem->url(); linkedUrl.cd(entry.stringValue( KIO::UDSEntry::UDS_LINK_DEST )); // make sure we don't end in an infinite loop if( linkedUrl.isParentOf( baseItem->project()->folder() ) || baseItem->project()->folder().isParentOf( linkedUrl ) || linkedUrl == baseItem->project()->folder() ) { continue; } } folders << url; } else { files << url; } } } - ifDebug(kDebug() << "valid folders:" << folders;) - ifDebug(kDebug() << "valid files:" << files;) + ifDebug(kDebug(9517) << "valid folders:" << folders;) + ifDebug(kDebug(9517) << "valid files:" << files;) // remove obsolete rows for ( int j = 0; j < baseItem->rowCount(); ++j ) { - if ( baseItem->child(j)->type() == ProjectBaseItem::Folder ) { - ProjectFolderItem* f = static_cast( baseItem->child(j) ); + if ( ProjectFolderItem* f = baseItem->child(j)->folder() ) { // check if this is still a valid folder int index = folders.indexOf( f->url() ); if ( index == -1 ) { // folder got removed or is now invalid - ifDebug(kDebug() << "removing folder:" << f->url();) + ifDebug(kDebug(9517) << "removing folder:" << f->url();) baseItem->removeRow( j ); --j; } else { // this folder already exists in the view folders.removeAt( index ); if ( forceRecursion ) { //no need to add this item, but we still want to recurse into it job->addSubDir( f ); } } - } else if ( baseItem->child(j)->type() == ProjectBaseItem::File ) { - ProjectFileItem* f = static_cast( baseItem->child(j) ); + } else if ( ProjectFileItem* f = baseItem->child(j)->file() ) { // check if this is still a valid file int index = files.indexOf( f->url() ); if ( index == -1 ) { // file got removed or is now invalid - ifDebug(kDebug() << "removing file:" << f->url();) + ifDebug(kDebug(9517) << "removing file:" << f->url();) baseItem->removeRow( j ); --j; } else { // this file already exists in the view files.removeAt( index ); } } } // add new rows foreach ( const KUrl& url, files ) { ProjectFileItem* file = q->createFileItem( baseItem->project(), url, baseItem ); emit q->fileAdded( file ); } foreach ( const KUrl& url, folders ) { ProjectFolderItem* folder = q->createFolderItem( baseItem->project(), url, baseItem ); emit q->folderAdded( folder ); job->addSubDir( folder ); } } void AbstractFileManagerPlugin::Private::created(const QString &path) { - kDebug() << "created:" << path; + kDebug(9517) << "created:" << path; QFileInfo info(path); KUrl url = KUrl(path); KUrl parent = url.upUrl(); foreach ( IProject* p, m_watchers.keys() ) { if ( !q->isValid(url, info.isDir(), p) ) { continue; } if ( !p->foldersForUrl(url).isEmpty() || !p->filesForUrl(url).isEmpty() ) { // exists already in this project, happens e.g. when we restart the dirwatcher // for files it also gets triggered for kate's backup files continue; } foreach ( ProjectFolderItem* parentItem, p->foldersForUrl(parent) ) { if ( info.isDir() ) { ProjectFolderItem* folder = q->createFolderItem( p, url, parentItem ); emit q->folderAdded( folder ); eventuallyReadFolder( folder ); } else { ProjectFileItem* file = q->createFileItem( p, url, parentItem ); emit q->fileAdded( file ); } } } } void AbstractFileManagerPlugin::Private::deleted(const QString &path) { if ( QFile::exists(path) ) { // stopDirScan... return; } - kDebug() << "deleted:" << path; + kDebug(9517) << "deleted:" << path; KUrl url = KUrl(path); foreach ( IProject* p, m_watchers.keys() ) { if ( url.equals(p->folder(), KUrl::CompareWithoutTrailingSlash) ) { KMessageBox::error(qApp->activeWindow(), i18n("The base folder of project %1" " got deleted or moved outside of KDevelop.\n" "The project has to be closed.", p->name()), i18n("Project Folder Deleted") ); ICore::self()->projectController()->closeProject(p); continue; } foreach ( ProjectFolderItem* item, p->foldersForUrl(url) ) { emit q->folderRemoved(item); item->parent()->removeRow(item->row()); } foreach ( ProjectFileItem* item, p->filesForUrl(url) ) { emit q->fileRemoved(item); item->parent()->removeRow(item->row()); } } } bool AbstractFileManagerPlugin::Private::rename(ProjectBaseItem* item, const KUrl& destination) { if ( !q->isValid(destination, true, item->project()) ) { int cancel = KMessageBox::warningContinueCancel( qApp->activeWindow(), i18n("You tried to rename '%1' to '%2', but the latter is filtered and will be hidden.\n" "Do you want to continue?", item->text(), destination.fileName()), QString(), KStandardGuiItem::cont(), KStandardGuiItem::cancel(), "GenericManagerRenameToFiltered" ); if ( cancel == KMessageBox::Cancel ) { return false; } } foreach ( ProjectFolderItem* parent, item->project()->foldersForUrl(destination.upUrl()) ) { - if ( parent->type() == ProjectBaseItem::Folder ) { + if ( parent->folder() ) { stopWatcher(parent); const KUrl source = item->url(); bool success = renameUrl( item->project(), source, destination ); if ( success ) { item->setUrl( destination ); item->parent()->takeRow( item->row() ); parent->appendRow( item ); if (item->file()) { emit q->fileRenamed(source, item->file()); } else { Q_ASSERT(item->folder()); emit q->folderRenamed(source, item->folder()); } } continueWatcher(parent); return success; } } return false; } void AbstractFileManagerPlugin::Private::stopWatcher(ProjectFolderItem* folder) { if ( !folder->url().isLocalFile() ) { return; } Q_ASSERT(m_watchers.contains(folder->project())); m_watchers[folder->project()]->stopDirScan(folder->url().toLocalFile()); } void AbstractFileManagerPlugin::Private::continueWatcher(ProjectFolderItem* folder) { if ( !folder->url().isLocalFile() ) { return; } Q_ASSERT(m_watchers.contains(folder->project())); m_watchers[folder->project()]->restartDirScan(folder->url().toLocalFile()); } //END Private //BEGIN Plugin AbstractFileManagerPlugin::AbstractFileManagerPlugin( const KComponentData& instance, QObject *parent, const QVariantList & /*args*/ ) : IProjectFileManager(), IPlugin( instance, parent ), d(new Private(this)) { KDEV_USE_EXTENSION_INTERFACE( IProjectFileManager ) connect(core()->projectController(), SIGNAL(projectClosing(KDevelop::IProject*)), this, SLOT(projectClosing(KDevelop::IProject*))); } AbstractFileManagerPlugin::~AbstractFileManagerPlugin() { } IProjectFileManager::Features AbstractFileManagerPlugin::features() const { return Features( Folders | Files ); } QList AbstractFileManagerPlugin::parse( ProjectFolderItem *item ) { // we are async, can't return anything here - kDebug() << "note: parse will always return an empty list"; + kDebug(9517) << "note: parse will always return an empty list"; Q_UNUSED(item); return QList(); } ProjectFolderItem *AbstractFileManagerPlugin::import( IProject *project ) { ProjectFolderItem *projectRoot = createFolderItem( project, project->folder(), 0 ); emit folderAdded( projectRoot ); - kDebug() << "imported new project" << project->name() << "at" << projectRoot->url(); + kDebug(9517) << "imported new project" << project->name() << "at" << projectRoot->url(); ///TODO: check if this works for remote files when something gets changed through another KDE app if ( project->folder().isLocalFile() ) { d->m_watchers[project] = new KDirWatch( project ); connect(d->m_watchers[project], SIGNAL(created(QString)), this, SLOT(created(QString))); connect(d->m_watchers[project], SIGNAL(deleted(QString)), this, SLOT(deleted(QString))); d->m_watchers[project]->addDir(project->folder().toLocalFile(), KDirWatch::WatchSubDirs | KDirWatch:: WatchFiles ); } return projectRoot; } KJob* AbstractFileManagerPlugin::createImportJob(ProjectFolderItem* item) { return d->eventuallyReadFolder(item); } bool AbstractFileManagerPlugin::reload( ProjectFolderItem* item ) { - kDebug() << "reloading item" << item->url(); + kDebug(9517) << "reloading item" << item->url(); d->eventuallyReadFolder( item->folder(), true ); return true; } ProjectFolderItem* AbstractFileManagerPlugin::addFolder( const KUrl& url, ProjectFolderItem * parent ) { - kDebug() << "adding folder" << url << "to" << parent->url(); + kDebug(9517) << "adding folder" << url << "to" << parent->url(); ProjectFolderItem* created = 0; d->stopWatcher(parent); if ( createFolder(url) ) { created = createFolderItem( parent->project(), url, parent ); emit folderAdded(created); } d->continueWatcher(parent); return created; } ProjectFileItem* AbstractFileManagerPlugin::addFile( const KUrl& url, ProjectFolderItem * parent ) { - kDebug() << "adding file" << url << "to" << parent->url(); + kDebug(9517) << "adding file" << url << "to" << parent->url(); ProjectFileItem* created = 0; d->stopWatcher(parent); if ( createFile(url) ) { created = createFileItem( parent->project(), url, parent ); emit fileAdded(created); } d->continueWatcher(parent); return created; } bool AbstractFileManagerPlugin::renameFolder( ProjectFolderItem * folder, const KUrl& url ) { - kDebug() << "trying to rename a folder:" << folder->url() << url; + kDebug(9517) << "trying to rename a folder:" << folder->url() << url; return d->rename(folder, url); } bool AbstractFileManagerPlugin::renameFile( ProjectFileItem * file, const KUrl& url ) { - kDebug() << "trying to rename a file:" << file->url() << url; + kDebug(9517) << "trying to rename a file:" << file->url() << url; return d->rename(file, url); } bool AbstractFileManagerPlugin::removeFilesAndFolders(QList items) { bool success = true; foreach(ProjectBaseItem* item, items) { Q_ASSERT(item->folder() || item->file()); ProjectFolderItem* parent = getParentFolder(item); d->stopWatcher(parent); success &= removeUrl(parent->project(), item->url(), true); if ( success ) { if (item->file()) { emit fileRemoved(item->file()); } else { Q_ASSERT(item->folder()); emit folderRemoved(item->folder()); } item->parent()->removeRow( item->row() ); } d->continueWatcher(parent); if ( !success ) break; } return success; } bool AbstractFileManagerPlugin::isValid( const KUrl& /*url*/, const bool /*isFolder*/, IProject* /*project*/ ) const { return true; } ProjectFileItem* AbstractFileManagerPlugin::createFileItem( IProject* project, const KUrl& url, ProjectBaseItem* parent ) { return new ProjectFileItem( project, url, parent ); } ProjectFolderItem* AbstractFileManagerPlugin::createFolderItem( IProject* project, const KUrl& url, ProjectBaseItem* parent ) { return new ProjectFolderItem( project, url, parent ); } KDirWatch* AbstractFileManagerPlugin::projectWatcher( IProject* project ) const { return d->m_watchers.value( project, 0 ); } //END Plugin #include "abstractfilemanagerplugin.moc" diff --git a/project/filemanagerlistjob.cpp b/project/filemanagerlistjob.cpp index 38912cd8a0..ce82c3e892 100644 --- a/project/filemanagerlistjob.cpp +++ b/project/filemanagerlistjob.cpp @@ -1,89 +1,89 @@ /* This file is part of KDevelop Copyright 2009 Radu Benea This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "filemanagerlistjob.h" #include #include #include using namespace KDevelop; FileManagerListJob::FileManagerListJob(ProjectFolderItem* item, const bool forceRecursion) : KIO::Job(), m_item(0), m_forceRecursion(forceRecursion) { /* the following line is not an error in judgement, apparently starting a * listJob while the previous one hasn't self-destructed takes a lot of time, * so we give the job a chance to selfdestruct first */ connect( this, SIGNAL(nextJob()), SLOT(startNextJob()), Qt::QueuedConnection ); addSubDir(item); startNextJob(); } ProjectFolderItem* FileManagerListJob::item() const { return m_item; } void FileManagerListJob::addSubDir( ProjectFolderItem* item ) { Q_ASSERT(!m_item || item->url().upUrl() == m_item->url()); m_listQueue.enqueue(item); } void FileManagerListJob::slotEntries(KIO::Job* job, const KIO::UDSEntryList& entriesIn) { Q_UNUSED(job); entryList.append(entriesIn); } void FileManagerListJob::startNextJob() { if ( m_listQueue.isEmpty() ) { return; } m_item = m_listQueue.dequeue(); KIO::ListJob* job = KIO::listDir( m_item->url(), KIO::HideProgressInfo ); job->setParentJob( this ); connect( job, SIGNAL(entries(KIO::Job*, KIO::UDSEntryList)), this, SLOT(slotEntries(KIO::Job*, KIO::UDSEntryList)) ); connect( job, SIGNAL(result(KJob*)), SLOT(slotResult(KJob*)) ); } void FileManagerListJob::slotResult(KJob* job) { emit entries(this, m_item, entryList, m_forceRecursion); entryList.clear(); if( job->error() ) { - kDebug() << "error in list job:" << job->error() << job->errorString(); + kDebug(9517) << "error in list job:" << job->error() << job->errorString(); } if( m_listQueue.isEmpty() ) { emitResult(); } else { emit nextJob(); } } #include "filemanagerlistjob.moc" diff --git a/project/projectmodel.cpp b/project/projectmodel.cpp index d609406b13..1bc51892c7 100644 --- a/project/projectmodel.cpp +++ b/project/projectmodel.cpp @@ -1,979 +1,982 @@ /* This file is part of KDevelop Copyright 2005 Roberto Raggi Copyright 2007 Andreas Pakulat Copyright 2007 Aleix Pol This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "projectmodel.h" #include #include #include #include #include #include #include #include #include #include #include "interfaces/iprojectfilemanager.h" #include #include #include #include #include #include #include // Utility function to determine between direct connection and blocking-queued-connection // for emitting of signals for data changes/row addition/removal // BlockingQueuedConnection is necessary here as slots connected to the "aboutToBe" signals // expect the actual model content to not have changed yet. So we need to make sure the // signal is delivered before we really do something. static Qt::ConnectionType getConnectionTypeForSignalDelivery( KDevelop::ProjectModel* model ) { if( QThread::currentThread() == model->thread() ) { return Qt::DirectConnection; } else { return Qt::BlockingQueuedConnection; } } namespace KDevelop { QStringList removeProjectBasePath( const QStringList& fullpath, KDevelop::ProjectBaseItem* item ) { QStringList result = fullpath; if( item ) { KDevelop::ProjectModel* model = KDevelop::ICore::self()->projectController()->projectModel(); QStringList basePath = model->pathFromIndex( model->indexFromItem( item ) ); if( basePath.count() >= fullpath.count() ) { return QStringList(); } for( int i = 0; i < basePath.count(); i++ ) { result.takeFirst(); } } return result; } QStringList joinProjectBasePath( const QStringList& partialpath, KDevelop::ProjectBaseItem* item ) { QStringList basePath; if( item ) { KDevelop::ProjectModel* model = KDevelop::ICore::self()->projectController()->projectModel(); basePath = model->pathFromIndex( model->indexFromItem( item ) ); } return basePath + partialpath; } class ProjectModelPrivate { public: ProjectModelPrivate( ProjectModel* model ): model( model ) { } ProjectBaseItem* rootItem; ProjectModel* model; ProjectBaseItem* itemFromIndex( const QModelIndex& idx ) { if( !idx.isValid() ) { return rootItem; } if( idx.model() != model ) { return 0; } return model->itemFromIndex( idx ); } }; class ProjectBaseItemPrivate { public: ProjectBaseItemPrivate() : project(0), parent(0), row(-1), model(0) {} IProject* project; ProjectBaseItem* parent; int row; QList children; QString text; ProjectBaseItem::ProjectItemType type; Qt::ItemFlags flags; ProjectModel* model; KUrl m_url; }; ProjectBaseItem::ProjectBaseItem( IProject* project, const QString &name, ProjectBaseItem *parent ) : d_ptr(new ProjectBaseItemPrivate) { Q_D(ProjectBaseItem); // only the root item has no project (and can be identified by having no name or parent) Q_ASSERT(project || (name.isEmpty() && !parent)); d->project = project; d->text = name; d->flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled; if( parent ) { parent->appendRow( this ); } } ProjectBaseItem::~ProjectBaseItem() { Q_D(ProjectBaseItem); if( parent() ) { parent()->takeRow( d->row ); } else if( model() ) { model()->takeRow( d->row ); } removeRows(0, d->children.size()); delete d; } ProjectBaseItem* ProjectBaseItem::child( int row ) const { Q_D(const ProjectBaseItem); if( row < 0 || row >= d->children.length() ) { return 0; } return d->children.at( row ); } QList< ProjectBaseItem* > ProjectBaseItem::children() const { Q_D(const ProjectBaseItem); return d->children; } ProjectBaseItem* ProjectBaseItem::takeRow(int row) { Q_D(ProjectBaseItem); Q_ASSERT(row >= 0 && row < d->children.size()); if( model() ) { QMetaObject::invokeMethod( model(), "rowsAboutToBeRemoved", getConnectionTypeForSignalDelivery( model() ), Q_ARG(QModelIndex, index()), Q_ARG(int, row), Q_ARG(int, row) ); } ProjectBaseItem* olditem = d->children.takeAt( row ); olditem->d_func()->parent = 0; olditem->d_func()->row = -1; olditem->setModel( 0 ); for(int i=row; id_func()->row--; Q_ASSERT(child(i)->d_func()->row==i); } if( model() ) { QMetaObject::invokeMethod( model(), "rowsRemoved", getConnectionTypeForSignalDelivery( model() ), Q_ARG(QModelIndex, index()), Q_ARG(int, row), Q_ARG(int, row) ); } return olditem; } void ProjectBaseItem::removeRow( int row ) { delete takeRow( row ); } void ProjectBaseItem::removeRows(int row, int count) { for( ; count > 0; count-- ) { removeRow( row ); } } QModelIndex ProjectBaseItem::index() const { if( model() ) { return model()->indexFromItem( this ); } return QModelIndex(); } int ProjectBaseItem::rowCount() const { Q_D(const ProjectBaseItem); return d->children.count(); } int ProjectBaseItem::type() const { return ProjectBaseItem::BaseItem; } ProjectModel* ProjectBaseItem::model() const { Q_D(const ProjectBaseItem); return d->model; } ProjectBaseItem* ProjectBaseItem::parent() const { Q_D(const ProjectBaseItem); if( model() && model()->d->rootItem == d->parent ) { return 0; } return d->parent; } int ProjectBaseItem::row() const { Q_D(const ProjectBaseItem); return d->row; } QString ProjectBaseItem::text() const { Q_D(const ProjectBaseItem); if( project() && !parent() ) { return project()->name(); } else { Q_ASSERT(!d->text.isEmpty()); return d->text; } } void ProjectBaseItem::setModel( ProjectModel* model ) { Q_D(ProjectBaseItem); d->model = model; foreach( ProjectBaseItem* item, d->children ) { item->setModel( model ); } } void ProjectBaseItem::setRow( int row ) { Q_D(ProjectBaseItem); d->row = row; } void ProjectBaseItem::setText( const QString& text ) { Q_D(ProjectBaseItem); d->text = text; if( model() ) { QMetaObject::invokeMethod( model(), "dataChanged", getConnectionTypeForSignalDelivery( model() ), Q_ARG(QModelIndex, index()), Q_ARG(QModelIndex, index()) ); } } ProjectBaseItem::RenameStatus ProjectBaseItem::rename(const QString& newname) { setText( newname ); return RenameOk; } KDevelop::ProjectBaseItem::ProjectItemType baseType( int type ) { if( type == KDevelop::ProjectBaseItem::Folder || type == KDevelop::ProjectBaseItem::BuildFolder ) return KDevelop::ProjectBaseItem::Folder; if( type == KDevelop::ProjectBaseItem::Target || type == KDevelop::ProjectBaseItem::ExecutableTarget || type == KDevelop::ProjectBaseItem::LibraryTarget) return KDevelop::ProjectBaseItem::Target; return static_cast( type ); } bool ProjectBaseItem::lessThan( const KDevelop::ProjectBaseItem* item ) const { if(item->type() >= KDevelop::ProjectBaseItem::CustomProjectItemType ) { // For custom types we want to make sure that if they override lessThan, then we // prefer their lessThan implementation return !item->lessThan( this ); } KDevelop::ProjectBaseItem::ProjectItemType leftType=baseType(type()), rightType=baseType(item->type()); if(leftType==rightType) { if(leftType==KDevelop::ProjectBaseItem::File) { return file()->fileName().compare(item->file()->fileName(), Qt::CaseInsensitive) < 0; } return this->text()text(); } else { return leftTypeproject; } void ProjectBaseItem::appendRow( ProjectBaseItem* item ) { Q_D(ProjectBaseItem); if( !item ) { return; } if( item->parent() ) { // Proper way is to first removeRow() on the original parent, then appendRow on this one kWarning() << "Ignoring double insertion of item" << item; return; } int startrow,endrow; if( model() ) { startrow = endrow = d->children.count(); QMetaObject::invokeMethod( model(), "rowsAboutToBeInserted", getConnectionTypeForSignalDelivery( model() ), Q_ARG(QModelIndex, index()), Q_ARG(int, startrow), Q_ARG(int, endrow) ); } d->children.append( item ); item->setRow( d->children.count() - 1 ); item->d_func()->parent = this; item->setModel( model() ); if( model() ) { QMetaObject::invokeMethod( model(), "rowsInserted", getConnectionTypeForSignalDelivery( model() ), Q_ARG( QModelIndex, index() ), Q_ARG( int, startrow ), Q_ARG( int, endrow ) ); } } KUrl ProjectBaseItem::url( ) const { Q_D(const ProjectBaseItem); return d->m_url; } void ProjectBaseItem::setUrl( const KUrl& url ) { Q_D(ProjectBaseItem); d->m_url = url; setText( d->m_url.fileName() ); } Qt::ItemFlags ProjectBaseItem::flags() { Q_D(ProjectBaseItem); return d->flags; } void ProjectBaseItem::setFlags(Qt::ItemFlags flags) { Q_D(ProjectBaseItem); d->flags = flags; if(d->model) d->model->dataChanged(index(), index()); } QString ProjectBaseItem::iconName() const { return ""; } ProjectFolderItem *ProjectBaseItem::folder() const { return 0; } ProjectTargetItem *ProjectBaseItem::target() const { return 0; } ProjectExecutableTargetItem *ProjectBaseItem::executable() const { return 0; } ProjectFileItem *ProjectBaseItem::file() const { return 0; } QList ProjectBaseItem::folderList() const { QList lst; for ( int i = 0; i < rowCount(); ++i ) { ProjectBaseItem* item = child( i ); if ( item->type() == Folder || item->type() == BuildFolder ) { ProjectFolderItem *kdevitem = dynamic_cast( item ); if ( kdevitem ) lst.append( kdevitem ); } } return lst; } QList ProjectBaseItem::targetList() const { QList lst; for ( int i = 0; i < rowCount(); ++i ) { ProjectBaseItem* item = child( i ); if ( item->type() == Target || item->type() == LibraryTarget || item->type() == ExecutableTarget ) { ProjectTargetItem *kdevitem = dynamic_cast( item ); if ( kdevitem ) lst.append( kdevitem ); } } return lst; } QList ProjectBaseItem::fileList() const { QList lst; for ( int i = 0; i < rowCount(); ++i ) { ProjectBaseItem* item = child( i ); Q_ASSERT(item); if ( item && item->type() == File ) { ProjectFileItem *kdevitem = dynamic_cast( item ); if ( kdevitem ) lst.append( kdevitem ); } } return lst; } void ProjectModel::resetModel() { reset(); } void ProjectModel::clear() { d->rootItem->removeRows(0, d->rootItem->rowCount()); } ProjectFolderItem::ProjectFolderItem( IProject* project, const KUrl & dir, ProjectBaseItem * parent ) : ProjectBaseItem( project, dir.fileName(), parent ) { setUrl( dir ); } ProjectFolderItem::~ProjectFolderItem() { } void ProjectFolderItem::setUrl( const KUrl& url ) { KUrl copy(url); copy.adjustPath(KUrl::AddTrailingSlash); ProjectBaseItem::setUrl(copy); + + propagateRename(url); } ProjectFolderItem *ProjectFolderItem::folder() const { return const_cast(this); } int ProjectFolderItem::type() const { return ProjectBaseItem::Folder; } QString ProjectFolderItem::folderName() const { return url().fileName(); } -void propagateRename( const KDevelop::ProjectFolderItem* item, const KUrl& newBase) +void ProjectFolderItem::propagateRename(const KUrl& newBase) const { KUrl url = newBase; url.addPath("dummy"); - foreach( KDevelop::ProjectBaseItem* child, item->children() ) + foreach( KDevelop::ProjectBaseItem* child, children() ) { url.setFileName( child->text() ); child->setUrl( url ); - if ( child->folder() ) { - propagateRename( child->folder(), url ); + + const ProjectFolderItem* folder = child->folder(); + if ( folder ) { + folder->propagateRename( url ); } } } ProjectBaseItem::RenameStatus ProjectFolderItem::rename(const QString& newname) { //TODO: Same as ProjectFileItem, so should be shared somehow KUrl dest = url().upUrl(); dest.addPath(newname); if( !newname.contains('/') ) { KIO::UDSEntry entry; //There exists a file with that name? if( !KIO::NetAccess::stat(dest, entry, 0) ) { if( !project() || project()->projectFileManager()->renameFolder(this, dest) ) { - setUrl( dest ); - propagateRename(this, dest); return ProjectBaseItem::RenameOk; - } else + } + else { return ProjectBaseItem::ProjectManagerRenameFailed; } } else { return ProjectBaseItem::ExistingItemSameName; } } else { return ProjectBaseItem::InvalidNewName; } } bool ProjectFolderItem::hasFileOrFolder(const QString& name) const { for ( int i = 0; i < rowCount(); ++i ) { ProjectBaseItem* item = child( i ); if ( ProjectFileItem* file = dynamic_cast(item)) if (file->fileName() == name) return true; if ( ProjectFolderItem* folder = dynamic_cast(item)) if (folder->folderName() == name) return true; } return false; } ProjectBuildFolderItem::ProjectBuildFolderItem( IProject* project, const KUrl &dir, ProjectBaseItem *parent) : ProjectFolderItem( project, dir, parent ) { } QString ProjectFolderItem::iconName() const { return "folder"; } int ProjectBuildFolderItem::type() const { return ProjectBaseItem::BuildFolder; } QString ProjectBuildFolderItem::iconName() const { return "folder-development"; } ProjectFileItem::ProjectFileItem( IProject* project, const KUrl & file, ProjectBaseItem * parent ) : ProjectBaseItem( project, file.fileName(), parent ) { setUrl( file ); // Need to this manually here as setUrl() is virtual and hence the above // only calls the version in ProjectBaseItem and not ours if( project ) { project->addToFileSet( KDevelop::IndexedString(file) ); } } ProjectFileItem::~ProjectFileItem() { if( project() ) { project()->removeFromFileSet(KDevelop::IndexedString(url())); } } QString ProjectFileItem::iconName() const { return KMimeType::findByUrl(url(), 0, false, true)->iconName(url()); } ProjectBaseItem::RenameStatus ProjectFileItem::rename(const QString& newname) { KUrl dest = url().upUrl(); dest.addPath(newname); if( !newname.contains('/') ) { KIO::UDSEntry entry; //There exists a file with that name? if( !KIO::NetAccess::stat(dest, entry, 0) ) { if( !project() || project()->projectFileManager()->renameFile(this, dest) ) { - setUrl( dest ); return ProjectBaseItem::RenameOk; - } else + } + else { return ProjectBaseItem::ProjectManagerRenameFailed; } } else { return ProjectBaseItem::ExistingItemSameName; } } else { return ProjectBaseItem::InvalidNewName; } } QString ProjectFileItem::fileName() const { return url().fileName(); } void ProjectFileItem::setUrl( const KUrl& url ) { if( project() ) { if(!this->url().isEmpty()) project()->removeFromFileSet( KDevelop::IndexedString(this->url()) ); project()->addToFileSet( KDevelop::IndexedString(url) ); } ProjectBaseItem::setUrl( url ); } int ProjectFileItem::type() const { return ProjectBaseItem::File; } ProjectFileItem *ProjectFileItem::file() const { return const_cast( this ); } ProjectTargetItem::ProjectTargetItem( IProject* project, const QString &name, ProjectBaseItem *parent ) : ProjectBaseItem( project, name, parent ) { } QString ProjectTargetItem::iconName() const { return "system-run"; } int ProjectTargetItem::type() const { return ProjectBaseItem::Target; } ProjectTargetItem *ProjectTargetItem::target() const { return const_cast( this ); } ProjectExecutableTargetItem::ProjectExecutableTargetItem( IProject* project, const QString &name, ProjectBaseItem *parent ) : ProjectTargetItem(project, name, parent) { } ProjectExecutableTargetItem *ProjectExecutableTargetItem::executable() const { return const_cast( this ); } int ProjectExecutableTargetItem::type() const { return ProjectBaseItem::ExecutableTarget; } ProjectLibraryTargetItem::ProjectLibraryTargetItem( IProject* project, const QString &name, ProjectBaseItem *parent ) : ProjectTargetItem(project, name, parent) {} int ProjectLibraryTargetItem::type() const { return ProjectBaseItem::LibraryTarget; } QModelIndex ProjectModel::pathToIndex(const QStringList& tofetch_) const { if(tofetch_.isEmpty()) return QModelIndex(); QStringList tofetch(tofetch_); if(tofetch.last().isEmpty()) tofetch.takeLast(); QModelIndex current=index(0,0, QModelIndex()); QModelIndex ret; for(int a = 0; a < tofetch.size(); ++a) { const QString& currentName = tofetch[a]; bool matched = false; QModelIndexList l = match(current, Qt::DisplayRole, currentName, -1, Qt::MatchExactly); foreach(const QModelIndex& idx, l) { //If this is not the last item, only match folders, as there may be targets and folders with the same name if(a == tofetch.size()-1 || itemFromIndex(idx)->folder()) { ret = idx; current = index(0,0, ret); matched = true; break; } } if(!matched) { ret = QModelIndex(); break; } } Q_ASSERT(!ret.isValid() || data(ret).toString()==tofetch.last()); return ret; } QStringList ProjectModel::pathFromIndex(const QModelIndex& index) const { if (!index.isValid()) return QStringList(); QModelIndex idx = index; QStringList list; do { QString t = data(idx, Qt::DisplayRole).toString(); list.prepend(t); QModelIndex parent = idx.parent(); idx = parent.sibling(parent.row(), index.column()); } while (idx.isValid()); return list; } int ProjectModel::columnCount( const QModelIndex& ) const { return 1; } int ProjectModel::rowCount( const QModelIndex& parent ) const { ProjectBaseItem* item = d->itemFromIndex( parent ); return item ? item->rowCount() : 0; } QModelIndex ProjectModel::parent( const QModelIndex& child ) const { if( child.isValid() ) { ProjectBaseItem* item = static_cast( child.internalPointer() ); return indexFromItem( item ); } return QModelIndex(); } QModelIndex ProjectModel::indexFromItem( const ProjectBaseItem* item ) const { if( item && item->d_func()->parent ) { return createIndex( item->row(), 0, item->d_func()->parent ); } return QModelIndex(); } ProjectBaseItem* ProjectModel::itemFromIndex( const QModelIndex& index ) const { if( index.row() >= 0 && index.column() == 0 && index.model() == this ) { ProjectBaseItem* parent = static_cast( index.internalPointer() ); if( parent ) { return parent->child( index.row() ); } } return 0; } QVariant ProjectModel::data( const QModelIndex& index, int role ) const { if( ( role == Qt::DisplayRole || role == Qt::ToolTipRole || role == Qt::DecorationRole ) && index.isValid() ) { ProjectBaseItem* item = itemFromIndex( index ); if( item ) { switch(role) { case Qt::DecorationRole: return item->iconName(); case Qt::ToolTipRole: return item->url().prettyUrl(); default: return item->text(); } } } return QVariant(); } ProjectModel::ProjectModel( QObject *parent ) : QAbstractItemModel( parent ), d( new ProjectModelPrivate( this ) ) { d->rootItem = new ProjectBaseItem( 0, "", 0 ); d->rootItem->setModel( this ); } ProjectModel::~ProjectModel() { } ProjectVisitor::ProjectVisitor() { } QModelIndex ProjectModel::index( int row, int column, const QModelIndex& parent ) const { if( hasIndex( row, column, parent ) ) { ProjectBaseItem* parentItem = d->itemFromIndex( parent ); if( parentItem && row >= 0 && row < parentItem->rowCount() && column == 0 ) { return createIndex( row, column, parentItem ); } } return QModelIndex(); } void ProjectModel::appendRow( ProjectBaseItem* item ) { d->rootItem->appendRow( item ); } void ProjectModel::removeRow( int row ) { d->rootItem->removeRow( row ); } ProjectBaseItem* ProjectModel::takeRow( int row ) { return d->rootItem->takeRow( row ); } bool ProjectModel::hasChildren(const QModelIndex& parent) const { bool b = QAbstractItemModel::hasChildren(parent); return b; } Qt::ItemFlags ProjectModel::flags(const QModelIndex& index) const { ProjectBaseItem* item = itemFromIndex( index ); if(item) return item->flags(); else return 0; } bool ProjectModel::insertColumns(int, int, const QModelIndex&) { // Not supported return false; } bool ProjectModel::insertRows(int, int, const QModelIndex&) { // Not supported return false; } bool ProjectModel::setData(const QModelIndex&, const QVariant&, int) { // Not supported return false; } void ProjectVisitor::visit ( IProject* prj ) { visit( prj->projectItem() ); } void ProjectVisitor::visit ( ProjectBuildFolderItem* folder ) { foreach( ProjectFileItem* item, folder->fileList() ) { visit( item ); } foreach( ProjectTargetItem* item, folder->targetList() ) { if( item->type() == ProjectBaseItem::LibraryTarget ) { visit( dynamic_cast( item ) ); } else if( item->type() == ProjectBaseItem::ExecutableTarget ) { visit( dynamic_cast( item ) ); } } foreach( ProjectFolderItem* item, folder->folderList() ) { if( item->type() == ProjectBaseItem::BuildFolder ) { visit( dynamic_cast( item ) ); } else if( item->type() == ProjectBaseItem::Folder ) { visit( dynamic_cast( item ) ); } } } void ProjectVisitor::visit ( ProjectExecutableTargetItem* exec ) { foreach( ProjectFileItem* item, exec->fileList() ) { visit( item ); } } void ProjectVisitor::visit ( ProjectFolderItem* folder ) { foreach( ProjectFileItem* item, folder->fileList() ) { visit( item ); } foreach( ProjectTargetItem* item, folder->targetList() ) { if( item->type() == ProjectBaseItem::LibraryTarget ) { visit( dynamic_cast( item ) ); } else if( item->type() == ProjectBaseItem::ExecutableTarget ) { visit( dynamic_cast( item ) ); } } foreach( ProjectFolderItem* item, folder->folderList() ) { if( item->type() == ProjectBaseItem::BuildFolder ) { visit( dynamic_cast( item ) ); } else if( item->type() == ProjectBaseItem::Folder ) { visit( dynamic_cast( item ) ); } } } void ProjectVisitor::visit ( ProjectFileItem* ) { } void ProjectVisitor::visit ( ProjectLibraryTargetItem* lib ) { foreach( ProjectFileItem* item, lib->fileList() ) { visit( item ); } } ProjectVisitor::~ProjectVisitor() { } } #include "projectmodel.moc" diff --git a/project/projectmodel.h b/project/projectmodel.h index f247b1a12b..4ae32fbef6 100644 --- a/project/projectmodel.h +++ b/project/projectmodel.h @@ -1,369 +1,371 @@ /* This file is part of KDevelop Copyright 2005 Roberto Raggi Copyright 2007 Andreas Pakulat Copyright 2007 Aleix Pol This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KDEVPROJECTMODEL_H #define KDEVPROJECTMODEL_H #include #include "projectexport.h" #include #include template struct QPair; template class QList; namespace KDevelop { class IProject; class ProjectFolderItem; class ProjectBuildFolderItem; class ProjectFileItem; class ProjectTargetItem; class ProjectExecutableTargetItem; class ProjectLibraryTargetItem; class ProjectModel; class KDEVPLATFORMPROJECT_EXPORT ProjectVisitor { public: ProjectVisitor(); virtual ~ProjectVisitor(); virtual void visit( IProject* ); virtual void visit( ProjectBuildFolderItem* ); virtual void visit( ProjectExecutableTargetItem* ); virtual void visit( ProjectFolderItem* ); virtual void visit( ProjectFileItem* ); virtual void visit( ProjectLibraryTargetItem* ); }; /** * Interface that allows a developer to implement the three basic types of * items you would see in a multi-project * \li Folder * \li Project * \li Custom Target * \li Library Target * \li Executable Target * \li File */ class KDEVPLATFORMPROJECT_EXPORT ProjectBaseItem { public: ProjectBaseItem( IProject*, const QString &name, ProjectBaseItem *parent = 0 ); virtual ~ProjectBaseItem(); enum ProjectItemType { BaseItem = 0 /** item is a base item */, BuildFolder = 1 /** item is a buildable folder */, Folder = 2 /** item is a folder */, ExecutableTarget = 3 /** item is an executable target */, LibraryTarget = 4 /** item is a library target */, Target = 5 /** item is a target */, File = 6 /** item is a file */, CustomProjectItemType = 100 /** type which should be used as base for custom types */ }; enum RenameStatus { RenameOk = 0, ExistingItemSameName = 1, ProjectManagerRenameFailed = 2, InvalidNewName = 3 }; /** @returns Returns the project that the item belongs to. */ IProject* project() const; /** @returns If this item is a folder, it returns a pointer to the folder, otherwise returns a 0 pointer. */ virtual ProjectFolderItem *folder() const; /** @returns If this item is a target, it returns a pointer to the target, otherwise returns a 0 pointer. */ virtual ProjectTargetItem *target() const; /** @returns If this item is a file, it returns a pointer to the file, otherwise returns a 0 pointer. */ virtual ProjectFileItem *file() const; /** @returns If this item is a file, it returns a pointer to the file, otherwise returns a 0 pointer. */ virtual ProjectExecutableTargetItem *executable() const; /** @returns Returns a list of the folders that have this object as the parent. */ QList folderList() const; /** @returns Returns a list of the targets that have this object as the parent. */ QList targetList() const; /** @returns Returns a list of the files that have this object as the parent. */ QList fileList() const; virtual bool lessThan( const KDevelop::ProjectBaseItem* ) const; /** @returns the @p row item in the list of children of this item or 0 if there is no such child. */ ProjectBaseItem* child( int row ) const; /** @returns the list of children of this item. */ QList children() const; /** @returns a valid QModelIndex for usage with the model API for this item. */ QModelIndex index() const; /** @returns The parent item if this item has one, else it return 0. */ virtual ProjectBaseItem* parent() const; /** @returns the displayed text of this item. */ QString text() const; /** @returns the row in the list of children of this items parent, or -1. */ int row() const; /** * Allows to change the displayed text of this item. * @param text the new text */ void setText( const QString& text ); /** @returns the number of children of this item, or 0 if there are none. */ int rowCount() const; /** @returns the model to which this item belongs, or 0 if its not associated to a model. */ ProjectModel* model() const; /** * Adds a new child item to this item. */ void appendRow( ProjectBaseItem* item ); /** * Removes and deletes the item at the given @p row if there is one. */ void removeRow( int row ); /** * Removes and deletes the @p count items after the given @p row if there is one. */ void removeRows( int row, int count ); /** * Returns and removes the item at the given @p row if there is one. */ ProjectBaseItem* takeRow( int row ); /** @returns RTTI info, allows to know the type of item */ virtual int type() const; /** @returns a string to pass to KIcon as icon-name suitable to represent this item. */ virtual QString iconName() const; /** * Set the url of this item. * Note this function never renames the item in the project manager or on the filesystem, * it only changes the url and possibly the text nothing else. */ virtual void setUrl( const KUrl& ); /** Get the url of this item (if any) */ KUrl url() const; /** * Renames the item to the new name. * @returns status information wether the renaming succeeded. */ virtual RenameStatus rename( const QString& newname ); /** * Default flags: Qt::ItemIsEnabled | Qt::ItemIsSelectable * * @returns the flags supported by the item */ virtual Qt::ItemFlags flags(); /** * Sets what flags should be returned by ::flags() method. */ void setFlags(Qt::ItemFlags flags); protected: class ProjectBaseItemPrivate* const d_ptr; ProjectBaseItem( ProjectBaseItemPrivate& dd ); void setRow( int row ); void setModel( ProjectModel* model ); private: Q_DECLARE_PRIVATE(ProjectBaseItem) friend class ProjectModel; }; /** * Implementation of the ProjectBaseItem interface that is specific to a * folder */ class KDEVPLATFORMPROJECT_EXPORT ProjectFolderItem: public ProjectBaseItem { public: ProjectFolderItem( IProject*, const KUrl &dir, ProjectBaseItem *parent = 0 ); virtual ~ProjectFolderItem(); virtual void setUrl(const KUrl& ); virtual ProjectFolderItem *folder() const; ///Reimplemented from QStandardItem virtual int type() const; /** Get the folder name, equal to url().fileName() but faster (precomputed) */ QString folderName() const; /** @returns Returns whether this folder directly contains the specified file or folder. */ bool hasFileOrFolder(const QString& name) const; virtual QString iconName() const; virtual RenameStatus rename(const QString& newname); + + void propagateRename( const KUrl& newBase ) const; }; /** * Folder which contains buildable targets as part of a buildable project */ class KDEVPLATFORMPROJECT_EXPORT ProjectBuildFolderItem: public ProjectFolderItem { public: ProjectBuildFolderItem( IProject*, const KUrl &dir, ProjectBaseItem *parent = 0 ); ///Reimplemented from QStandardItem virtual int type() const; virtual QString iconName() const; }; /** * Object which represents a target in a build system. * * This object contains all properties specific to a target. */ class KDEVPLATFORMPROJECT_EXPORT ProjectTargetItem: public ProjectBaseItem { public: ProjectTargetItem( IProject*, const QString &name, ProjectBaseItem *parent = 0 ); ///Reimplemented from QStandardItem virtual int type() const; virtual ProjectTargetItem *target() const; virtual QString iconName() const; }; /** * Object which represents an executable target in a build system. * * This object contains all properties specific to an executable. */ class KDEVPLATFORMPROJECT_EXPORT ProjectExecutableTargetItem: public ProjectTargetItem { public: ProjectExecutableTargetItem( IProject*, const QString &name, ProjectBaseItem *parent = 0 ); virtual ProjectExecutableTargetItem *executable() const; virtual int type() const; virtual KUrl builtUrl() const=0; virtual KUrl installedUrl() const=0; }; /** * Object which represents a library target in a build system. * * This object contains all properties specific to a library. */ class KDEVPLATFORMPROJECT_EXPORT ProjectLibraryTargetItem: public ProjectTargetItem { public: ProjectLibraryTargetItem(IProject* project, const QString &name, ProjectBaseItem *parent = 0 ); virtual int type() const; }; /** * Object which represents a file. */ class KDEVPLATFORMPROJECT_EXPORT ProjectFileItem: public ProjectBaseItem { public: ProjectFileItem( IProject*, const KUrl& file, ProjectBaseItem *parent = 0 ); ~ProjectFileItem(); ///Reimplemented from QStandardItem virtual int type() const; virtual ProjectFileItem *file() const; /** Get the file name, equal to url().fileName() but faster (precomputed) */ QString fileName() const; virtual void setUrl( const KUrl& ); virtual QString iconName() const; virtual RenameStatus rename(const QString& newname); }; /** * Class providing some convenience methods for accessing the project model * @todo: maybe switch to QAbstractItemModel, would make the implementation * for at least the checkbox-behaviour easier */ class KDEVPLATFORMPROJECT_EXPORT ProjectModel: public QAbstractItemModel { Q_OBJECT public: ProjectModel( QObject *parent = 0 ); virtual ~ProjectModel(); void resetModel(); void clear(); void appendRow( ProjectBaseItem* item ); void removeRow( int row ); ProjectBaseItem* takeRow( int row ); QModelIndex pathToIndex(const QStringList& tofetch) const; QStringList pathFromIndex(const QModelIndex& index) const; QModelIndex indexFromItem( const ProjectBaseItem* item ) const; ProjectBaseItem* itemFromIndex( const QModelIndex& ) const; virtual int columnCount( const QModelIndex& parent = QModelIndex() ) const; virtual QVariant data( const QModelIndex& index, int role = Qt::DisplayRole ) const; virtual QModelIndex parent( const QModelIndex& child ) const; virtual int rowCount( const QModelIndex& parent = QModelIndex() ) const; virtual QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const; virtual bool hasChildren(const QModelIndex& parent = QModelIndex()) const; virtual bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole); virtual bool insertColumns(int column, int count, const QModelIndex& parent = QModelIndex()); virtual bool insertRows(int row, int count, const QModelIndex& parent = QModelIndex()); virtual Qt::ItemFlags flags(const QModelIndex& index) const; private: class ProjectModelPrivate* const d; friend class ProjectBaseItem; }; KDEVPLATFORMPROJECT_EXPORT QStringList joinProjectBasePath( const QStringList& partialpath, KDevelop::ProjectBaseItem* item ); KDEVPLATFORMPROJECT_EXPORT QStringList removeProjectBasePath( const QStringList& fullpath, KDevelop::ProjectBaseItem* item ); } #endif // KDEVPROJECTMODEL_H diff --git a/shell/languagecontroller.cpp b/shell/languagecontroller.cpp index 166236181f..4fd04d9b51 100644 --- a/shell/languagecontroller.cpp +++ b/shell/languagecontroller.cpp @@ -1,271 +1,271 @@ /*************************************************************************** * Copyright 2006 Adam Treat * * Copyright 2007 Alexander Dymo * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library 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 Library 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 "languagecontroller.h" #include #include #include #include #include #include #include #include #include #include #include "core.h" #include "language.h" #include "settings/ccpreferences.h" #include "completionsettings.h" #include namespace KDevelop { typedef QHash LanguageHash; typedef QHash > LanguageCache; struct LanguageControllerPrivate { LanguageControllerPrivate(LanguageController *controller) : dataMutex(QMutex::Recursive), backgroundParser(new BackgroundParser(controller)), m_cleanedUp(false), m_controller(controller) {} void documentActivated(KDevelop::IDocument *document) { KUrl url = document->url(); if (!url.isValid()) { return; } foreach (ILanguage *lang, activeLanguages) { lang->deactivate(); } activeLanguages.clear(); QList languages = m_controller->languagesForUrl(url); foreach (ILanguage *lang, languages) { lang->activate(); activeLanguages << lang; } } QList activeLanguages; mutable QMutex dataMutex; LanguageHash languages; //Maps language-names to languages LanguageCache languageCache; //Maps mimetype-names to languages typedef QMultiMap MimeTypeCache; MimeTypeCache mimeTypeCache; //Maps mimetypes to languages BackgroundParser *backgroundParser; bool m_cleanedUp; ILanguage* addLanguageForSupport(ILanguageSupport* support); private: LanguageController *m_controller; }; ILanguage* LanguageControllerPrivate::addLanguageForSupport(KDevelop::ILanguageSupport* languageSupport) { if(languages.contains(languageSupport->name())) return languages[languageSupport->name()]; Q_ASSERT(dynamic_cast(languageSupport)); ILanguage* ret = new Language(languageSupport, m_controller); languages.insert(languageSupport->name(), ret); QVariant mimetypes = Core::self()->pluginController()->pluginInfo(dynamic_cast(languageSupport)).property("X-KDevelop-SupportedMimeTypes"); foreach(const QString& mimeTypeName, mimetypes.toStringList()) { kDebug(9505) << "adding supported mimetype:" << mimeTypeName << "language:" << languageSupport->name(); languageCache[mimeTypeName] << ret; KMimeType::Ptr mime = KMimeType::mimeType(mimeTypeName); if(mime) { mimeTypeCache.insert(mime, ret); } else { kWarning() << "could not create mime-type" << mimeTypeName; } } return ret; } LanguageController::LanguageController(QObject *parent) : ILanguageController(parent) { setObjectName("LanguageController"); d = new LanguageControllerPrivate(this); } LanguageController::~LanguageController() { delete d; } void LanguageController::initialize() { d->backgroundParser->loadSettings(); // make sure the DUChain is setup before we try to access it from different threads at the same time DUChain::self(); connect(Core::self()->documentController(), SIGNAL(documentActivated(KDevelop::IDocument*)), SLOT(documentActivated(KDevelop::IDocument*))); } void LanguageController::cleanup() { QMutexLocker lock(&d->dataMutex); d->m_cleanedUp = true; } QList LanguageController::activeLanguages() { QMutexLocker lock(&d->dataMutex); return d->activeLanguages; } ICompletionSettings *LanguageController::completionSettings() const { return &CompletionSettings::self(); } QList LanguageController::loadedLanguages() const { QMutexLocker lock(&d->dataMutex); QList ret; if(d->m_cleanedUp) return ret; foreach(ILanguage* lang, d->languages) ret << lang; return ret; } ILanguage *LanguageController::language(const QString &name) const { QMutexLocker lock(&d->dataMutex); if(d->m_cleanedUp) return 0; if(d->languages.contains(name)) return d->languages[name]; else{ ILanguage* ret = 0; QStringList constraints; constraints << QString("'%1' == [X-KDevelop-Language]").arg(name); QList supports = Core::self()->pluginController()-> allPluginsForExtension("ILanguageSupport", constraints); if(!supports.isEmpty()) { ILanguageSupport *languageSupport = supports[0]->extension(); if(supports[0]) ret = d->addLanguageForSupport(languageSupport); } return ret; } } QList LanguageController::languagesForUrl(const KUrl &url) { QMutexLocker lock(&d->dataMutex); QList languages; if(d->m_cleanedUp) return languages; QString fileName = url.fileName(); ///TODO: cache regexp or simple string pattern for endsWith matching QRegExp exp("", Qt::CaseInsensitive, QRegExp::Wildcard); ///non-crashy part: Use the mime-types of known languages for(LanguageControllerPrivate::MimeTypeCache::const_iterator it = d->mimeTypeCache.constBegin(); it != d->mimeTypeCache.constEnd(); ++it) { foreach(QString pattern, it.key()->patterns()) { if(pattern.startsWith('*')) { pattern = pattern.mid(1); if (!pattern.contains('*')) { //optimize: we can skip the expensive QRegExp in this case //and do a simple string compare (much faster) if (fileName.endsWith(pattern)) { languages << *it; } continue; } } exp.setPattern(pattern); if(int position = exp.indexIn(fileName)) { if(position != -1 && exp.matchedLength() + position == fileName.length()) languages << *it; } } } //Never use findByUrl from within a background thread, and never load a language support //from within the backgruond thread. Both is unsafe, and can lead to crashes if(!languages.isEmpty() || QThread::currentThread() != thread()) return languages; ///Crashy and unsafe part: Load missing language-supports - KMimeType::Ptr mimeType = KMimeType::findByUrl(url); + KMimeType::Ptr mimeType = KMimeType::findByUrl(url, 0, false, true); LanguageCache::ConstIterator it = d->languageCache.constFind(mimeType->name()); if (it != d->languageCache.constEnd()) { languages = it.value(); } else { QStringList constraints; constraints << QString("'%1' in [X-KDevelop-SupportedMimeTypes]").arg(mimeType->name()); QList supports = Core::self()->pluginController()-> allPluginsForExtension("ILanguageSupport", constraints); if (supports.isEmpty()) { kDebug(9505) << "no languages for mimetype:" << mimeType->name(); d->languageCache.insert(mimeType->name(), QList()); } else { foreach (IPlugin *support, supports) { ILanguageSupport* languageSupport = support->extension(); kDebug(9505) << "language-support:" << languageSupport; if(languageSupport) languages << d->addLanguageForSupport(languageSupport); } } } return languages; } BackgroundParser *LanguageController::backgroundParser() const { return d->backgroundParser; } } #include "languagecontroller.moc"