diff --git a/language/codegen/createclass.cpp b/language/codegen/createclass.cpp index 524149c321..d66a949f30 100644 --- a/language/codegen/createclass.cpp +++ b/language/codegen/createclass.cpp @@ -1,713 +1,718 @@ /* This file is part of KDevelop Copyright 2008 Hamish Rodda This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. 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 "createclass.h" #include #include #include #include #include #include #include "ui_newclass.h" #include "ui_licensechooser.h" #include "ui_outputlocation.h" #include "duchain/persistentsymboltable.h" #include "duchain/duchainlock.h" #include "duchain/duchain.h" #include "duchain/types/structuretype.h" #include "codegen/documentchangeset.h" #include "overridespage.h" #include #include #include #include namespace KDevelop { class CreateClassWizardPrivate { public: KUrl baseUrl; OutputPage* output; ClassGenerator * generator; }; CreateClassWizard::CreateClassWizard(QWidget* parent, ClassGenerator * generator, KUrl baseUrl) : QWizard(parent) , d(new CreateClassWizardPrivate) { d->baseUrl = baseUrl; Q_ASSERT(generator); d->generator = generator; setDefaultProperty("KUrlRequester", "url", SIGNAL(textChanged(QString))); setDefaultProperty("KTextEdit", "plainText", SIGNAL(textChanged())); } CreateClassWizard::~CreateClassWizard() { delete d; } void CreateClassWizard::setup() { setWindowTitle(i18n("Create New Class in %1", d->baseUrl.prettyUrl())); if (QWizardPage* page = newIdentifierPage()) addPage(page); if (QWizardPage* page = newOverridesPage()) addPage(page); addPage(new LicensePage(this)); addPage(d->output = new OutputPage(this)); } void CreateClassWizard::accept() { QWizard::accept(); //Transmit all the final information to the generator d->generator->setLicense(field("license").toString()); kDebug() << "Header Url: " << field("headerUrl").toString(); /* d->generator->setHeaderUrl(field("headerUrl").toString()); d->generator->setImplementationUrl(field("implementationUrl").toString()); d->generator->setHeaderPosition(SimpleCursor(field("headerLine").toInt(), field("headerColumn").toInt())); d->generator->setHeaderPosition(SimpleCursor(field("implementationLine").toInt(), field("implementationColumn").toInt())); */ DocumentChangeSet changes = d->generator->generate(); changes.setReplacementPolicy(DocumentChangeSet::WarnOnFailedChange); changes.setActivationPolicy(KDevelop::DocumentChangeSet::Activate); changes.applyAllChanges(); } ClassGenerator * CreateClassWizard::generator() { return d->generator; } ClassIdentifierPage* CreateClassWizard::newIdentifierPage() { return new ClassIdentifierPage(this); } OverridesPage* CreateClassWizard::newOverridesPage() { return new OverridesPage(d->generator, this); } struct ClassGeneratorPrivate { QString name; ///< The name for the class to be generated (does not include namespace if relevant) QString license; QList inheritedClasses; ///< Represent *ALL* of the inherited classes SimpleCursor headerPosition; SimpleCursor implementationPosition; KUrl headerUrl; KUrl implementationUrl; }; ClassGenerator::ClassGenerator() : d(new ClassGeneratorPrivate) { } ClassGenerator::~ClassGenerator() { delete d; } const QString & ClassGenerator::name() const { return d->name; } void ClassGenerator::setName(const QString & newName) { d->name = newName; } QString ClassGenerator::identifier() const { return name(); } void ClassGenerator::setIdentifier(const QString & identifier) { setName(identifier); } void ClassGenerator::addDeclaration(DeclarationPointer newDeclaration) { m_declarations << newDeclaration; } const QList ClassGenerator::declarations() const { return m_declarations; } const QList & ClassGenerator::addBaseClass(const QString & newBaseClass) { DUChainReadLocker lock(DUChain::lock()); bool added = false; PersistentSymbolTable::Declarations decl = PersistentSymbolTable::self().getDeclarations(IndexedQualifiedIdentifier(QualifiedIdentifier(newBaseClass))); //Search for all super classes for(PersistentSymbolTable::Declarations::Iterator it = decl.iterator(); it; ++it) { DeclarationPointer declaration = DeclarationPointer(it->declaration()); if(declaration->isForwardDeclaration()) continue; // Check if it's a class/struct/etc if(declaration->type()) { fetchSuperClasses(declaration); m_baseClasses << declaration; added = true; break; } } if(!added) m_baseClasses << DeclarationPointer(); //Some entities expect that there is always an item added to the list, so just add zero return m_baseClasses; } const QList & ClassGenerator::inheritanceList() const { return d->inheritedClasses; } void ClassGenerator::clearInheritance() { m_baseClasses.clear(); d->inheritedClasses.clear(); } void ClassGenerator::clearDeclarations() { m_declarations.clear(); } KUrl ClassGenerator::headerUrlFromBase(KUrl baseUrl, bool toLower) { Q_UNUSED(baseUrl); Q_UNUSED(toLower); KUrl url; url.addPath(d->name); return url; } KUrl ClassGenerator::implementationUrlFromBase(KUrl baseUrl, bool toLower) { Q_UNUSED(baseUrl); Q_UNUSED(toLower); return KUrl(); } void ClassGenerator::setHeaderPosition ( SimpleCursor position ) { d->headerPosition = position; } void ClassGenerator::setImplementationPosition ( SimpleCursor position ) { d->implementationPosition = position; } void ClassGenerator::setHeaderUrl ( KUrl header ) { d->headerUrl = header; kDebug() << "Header for the generated class: " << header; } void ClassGenerator::setImplementationUrl ( KUrl implementation ) { d->implementationUrl = implementation; kDebug() << "Implementation for the generated class: " << implementation; } SimpleCursor ClassGenerator::headerPosition() { return d->headerPosition; } SimpleCursor ClassGenerator::implementationPosition() { return d->implementationPosition; } KUrl & ClassGenerator::headerUrl() { return d->headerUrl; } KUrl & ClassGenerator::implementationUrl() { return d->implementationUrl; } /// Specify license for this class void ClassGenerator::setLicense(const QString & license) { kDebug() << "New Class: " << d->name << "Set license: " << d->license; d->license = license; } /// Get the license specified for this classes const QString & ClassGenerator::license() const { return d->license; } void ClassGenerator::fetchSuperClasses(DeclarationPointer derivedClass) { DUChainReadLocker lock(DUChain::lock()); //Prevent duplicity if(d->inheritedClasses.contains(derivedClass)) return; d->inheritedClasses.append(derivedClass); DUContext* context = derivedClass->internalContext(); foreach (const DUContext::Import& import, context->importedParentContexts()) if (DUContext * parentContext = import.context(context->topContext())) if (parentContext->type() == DUContext::Class) fetchSuperClasses( DeclarationPointer(parentContext->owner()) ); } class ClassIdentifierPagePrivate { public: ClassIdentifierPagePrivate() : classid(0) { } Ui::NewClassDialog* classid; }; ClassIdentifierPage::ClassIdentifierPage(QWizard* parent) : QWizardPage(parent) , d(new ClassIdentifierPagePrivate) { setTitle(i18n("Class Basics")); setSubTitle( i18n("Identify the class and any classes from which it is to inherit.") ); d->classid = new Ui::NewClassDialog; d->classid->setupUi(this); connect(d->classid->addInheritancePushButton, SIGNAL(pressed()), this, SLOT(addInheritance())); connect(d->classid->removeInheritancePushButton, SIGNAL(pressed()), this, SLOT(removeInheritance())); connect(d->classid->moveUpPushButton, SIGNAL(pressed()), this, SLOT(moveUpInheritance())); connect(d->classid->moveDownPushButton, SIGNAL(pressed()), this, SLOT(moveDownInheritance())); + connect(d->classid->inheritanceList, SIGNAL(currentRowChanged(int)), this, SLOT(checkMoveButtonState())); registerField("classIdentifier*", d->classid->identifierLineEdit); registerField("classInheritance", this, "inheritance", SIGNAL(inheritanceChanged())); } ClassIdentifierPage::~ClassIdentifierPage() { delete d; } KLineEdit* ClassIdentifierPage::identifierLineEdit() const { return d->classid->identifierLineEdit; } KLineEdit* ClassIdentifierPage::inheritanceLineEdit() const { return d->classid->inheritanceLineEdit; } void ClassIdentifierPage::addInheritance() { d->classid->inheritanceList->addItem(d->classid->inheritanceLineEdit->text()); d->classid->inheritanceLineEdit->clear(); d->classid->removeInheritancePushButton->setEnabled(true); if (d->classid->inheritanceList->count() > 1) checkMoveButtonState(); emit inheritanceChanged(); } void ClassIdentifierPage::removeInheritance() { delete d->classid->inheritanceList->takeItem(d->classid->inheritanceList->currentRow()); if (d->classid->inheritanceList->count() == 0) d->classid->removeInheritancePushButton->setEnabled(false); + checkMoveButtonState(); + emit inheritanceChanged(); } void ClassIdentifierPage::moveUpInheritance() { int currentRow = d->classid->inheritanceList->currentRow(); Q_ASSERT(currentRow > 0); if (currentRow <= 0) return; QListWidgetItem* item = d->classid->inheritanceList->takeItem(currentRow); d->classid->inheritanceList->insertItem(currentRow - 1, item); + d->classid->inheritanceList->setCurrentItem(item); emit inheritanceChanged(); } void ClassIdentifierPage::moveDownInheritance() { int currentRow = d->classid->inheritanceList->currentRow(); Q_ASSERT(currentRow != -1 && currentRow < d->classid->inheritanceList->count() - 1); if (currentRow == -1 || currentRow >= d->classid->inheritanceList->count() - 1) return; QListWidgetItem* item = d->classid->inheritanceList->takeItem(currentRow); d->classid->inheritanceList->insertItem(currentRow + 1, item); + d->classid->inheritanceList->setCurrentItem(item); emit inheritanceChanged(); } QualifiedIdentifier ClassIdentifierPage::parseParentClassId(const QString& inheritedObject) { return QualifiedIdentifier(inheritedObject); } void ClassIdentifierPage::checkMoveButtonState() { int currentRow = d->classid->inheritanceList->currentRow(); d->classid->moveUpPushButton->setEnabled(currentRow > 0); d->classid->moveDownPushButton->setEnabled(currentRow >= 0 && currentRow < d->classid->inheritanceList->count() - 1); } QStringList ClassIdentifierPage::inheritanceList() const { QStringList ret; for (int i = 0; i < d->classid->inheritanceList->count(); ++i) ret << d->classid->inheritanceList->item(i)->text(); return ret; } bool ClassIdentifierPage::validatePage ( void ) { //save the information in the generator ClassGenerator * generator = dynamic_cast(wizard())->generator(); generator->setIdentifier(field("classIdentifier").toString()); //Remove old base classes, and add the new ones generator->clearInheritance(); foreach (const QString & inherited, field("classInheritance").toStringList()) generator->addBaseClass(inherited); return true; } class LicensePagePrivate { public: struct LicenseInfo { QString name; QString path; QString contents; }; typedef QList LicenseList; LicensePagePrivate() : license(0) { } Ui::LicenseChooserDialog* license; LicenseList availableLicenses; }; LicensePage::LicensePage(QWizard* parent) : QWizardPage(parent) , d(new LicensePagePrivate) { setTitle(i18n("License")); setSubTitle( i18n("Choose the license under which to place the new class.") ); d->license = new Ui::LicenseChooserDialog; d->license->setupUi(this); connect(d->license->licenseComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(licenseComboChanged(int))); connect(d->license->saveLicense, SIGNAL(clicked(bool)), d->license->licenseName, SLOT(setEnabled(bool))); // Read all the available licenses from the standard dirs initializeLicenses(); //Set the license selection to the previous one KConfigGroup config(KGlobal::config()->group("CodeGeneration")); d->license->licenseComboBox->setCurrentIndex(config.readEntry( "LastSelectedLicense", 0 )); //Needed to avoid a bug where licenseComboChanged doesn't get called by QComboBox if the past selection was 0 licenseComboChanged(d->license->licenseComboBox->currentIndex()); registerField("license", d->license->licenseTextEdit); } LicensePage::~LicensePage() { KConfigGroup config(KGlobal::config()->group("CodeGeneration")); //Do not save invalid license numbers' int index = d->license->licenseComboBox->currentIndex(); if( index >= 0 || index < d->availableLicenses.size() ) { config.writeEntry("LastSelectedLicense", index); config.config()->sync(); } else kWarning() << "Attempted to save an invalid license number: " << index << ". Number of licenses:" << d->availableLicenses.size(); delete d; } // If the user entered a custom license that they want to save, save it bool LicensePage::validatePage() { if(d->license->licenseComboBox->currentIndex() == (d->availableLicenses.size() - 1) && d->license->saveLicense->isChecked()) return saveLicense(); else return true; } //! Read all the license files in the global and local config dirs void LicensePage::initializeLicenses() { kDebug() << "Searching for available licenses"; KStandardDirs * dirs = KGlobal::dirs(); QStringList licenseDirs = dirs->findDirs("data", "kdevcodegen/licenses"); //Iterate through the possible directories that contain licenses, and load their names foreach(const QString& currentDir, licenseDirs) { QDirIterator it(currentDir, QDir::Files | QDir::Readable); while(it.hasNext()) { LicensePagePrivate::LicenseInfo newLicense; newLicense.path = it.next(); newLicense.name = it.fileName(); kDebug() << "Found License: " << newLicense.name; d->availableLicenses.push_back(newLicense); d->license->licenseComboBox->addItem(newLicense.name); } } //Finally add the option other for user specified licenses LicensePagePrivate::LicenseInfo license; d->availableLicenses.push_back(license); d->license->licenseComboBox->addItem("Other"); } // Read a license index, if it is not loaded, open it from the file QString & LicensePage::readLicense(int licenseIndex) { //If the license is not loaded into memory, read it in if(d->availableLicenses[licenseIndex].contents.isEmpty()) { QString licenseText(""); //If we are dealing with the last option "other" just return a new empty string if(licenseIndex != (d->availableLicenses.size() - 1)) { kDebug() << "Reading license: " << d->availableLicenses[licenseIndex].name ; QFile newLicense(d->availableLicenses[licenseIndex].path); if(newLicense.open(QIODevice::ReadOnly | QIODevice::Text)) { QTextStream newLicenseText(&newLicense); newLicenseText.setAutoDetectUnicode(true); licenseText = newLicenseText.readAll(); newLicense.close(); } else licenseText = "Error, could not open license file.\n Was it deleted?"; } d->availableLicenses[licenseIndex].contents = licenseText; } return d->availableLicenses[licenseIndex].contents; } // ---Slots--- void LicensePage::licenseComboChanged(int selectedLicense) { //If the last slot is selected enable the save license combobox if(selectedLicense == (d->availableLicenses.size() - 1)) { d->license->licenseTextEdit->clear(); d->license->licenseTextEdit->setReadOnly(false); d->license->saveLicense->setEnabled(true); } else { d->license->saveLicense->setEnabled(false); d->license->licenseTextEdit->setReadOnly(true); } if(selectedLicense < 0 || selectedLicense >= d->availableLicenses.size()) d->license->licenseTextEdit->setText(i18n("Could not load previous license")); else d->license->licenseTextEdit->setText(readLicense(selectedLicense)); } bool LicensePage::saveLicense() { kDebug() << "Attempting to save custom license: " << d->license->licenseName->text(); QString localDataDir = KStandardDirs::locateLocal("data", "kdevcodegen/licenses/", KGlobal::activeComponent()); QFile newFile(localDataDir + d->license->licenseName->text()); if(newFile.exists()) { KMessageBox::sorry(this, i18n("The specified license already exists. Please provide a different name.")); return false; } newFile.open(QIODevice::WriteOnly); qint64 result = newFile.write(d->license->licenseTextEdit->toPlainText().toUtf8()); newFile.close(); if(result == -1) { KMessageBox::sorry(this, i18n("There was an error writing the file.")); return false; } return true; } class OutputPagePrivate { public: OutputPagePrivate() : output(0) { } Ui::OutputLocationDialog* output; CreateClassWizard* parent; void updateRanges(KIntNumInput * line, KIntNumInput * column, bool enable); }; void OutputPagePrivate::updateRanges(KIntNumInput * line, KIntNumInput * column, bool enable) { kDebug() << "Updating Ranges, file exists: " << enable; line->setEnabled(enable); column->setEnabled(enable); } OutputPage::OutputPage(CreateClassWizard* parent) : QWizardPage(parent) , d(new OutputPagePrivate) { d->parent = parent; setTitle(i18n("Output")); setSubTitle( i18n("Choose where to save the new class.") ); d->output = new Ui::OutputLocationDialog; d->output->setupUi(this); d->output->headerUrl->setMode( KFile::File | KFile::LocalOnly ); d->output->headerUrl->fileDialog()->setOperationMode( KFileDialog::Saving ); d->output->implementationUrl->setMode( KFile::File | KFile::LocalOnly ); d->output->implementationUrl->fileDialog()->setOperationMode( KFileDialog::Saving ); connect(d->output->lowerFilenameCheckBox, SIGNAL(stateChanged(int)), this, SLOT(updateFileNames())); connect(d->output->headerUrl, SIGNAL(textChanged(const QString &)), this, SLOT(updateHeaderRanges(const QString &))); connect(d->output->implementationUrl, SIGNAL(textChanged(const QString &)), this, SLOT(updateImplementationRanges(const QString &))); registerField("headerUrl", d->output->headerUrl); registerField("implementationUrl", d->output->implementationUrl); } void OutputPage::initializePage() { updateFileNames(); QWizardPage::initializePage(); } void OutputPage::updateFileNames() { d->output->headerUrl->setUrl(d->parent->generator()->headerUrlFromBase(d->parent->d->baseUrl, d->output->lowerFilenameCheckBox->isChecked())); d->output->implementationUrl->setUrl(d->parent->generator()->implementationUrlFromBase(d->parent->d->baseUrl, d->output->lowerFilenameCheckBox->isChecked())); } void OutputPage::updateHeaderRanges(const QString & url) { QFileInfo info(url); d->updateRanges(d->output->headerLineNumber, d->output->headerColumnNumber, info.exists() && !info.isDir()); } void OutputPage::updateImplementationRanges(const QString & url) { QFileInfo info(url); d->updateRanges(d->output->implementationLineNumber, d->output->implementationColumnNumber, info.exists() && !info.isDir()); } bool OutputPage::isComplete() const { return !d->output->headerUrl->url().url().isEmpty() && !d->output->implementationUrl->url().url().isEmpty(); } bool OutputPage::validatePage() { d->parent->generator()->setHeaderUrl(d->output->headerUrl->text()); d->parent->generator()->setImplementationUrl(d->output->implementationUrl->text()); d->parent->generator()->setHeaderPosition(SimpleCursor(d->output->headerLineNumber->value(), d->output->headerColumnNumber->value())); d->parent->generator()->setImplementationPosition(SimpleCursor(d->output->implementationLineNumber->value(), d->output->implementationColumnNumber->value())); return true; } OutputPage::~OutputPage() { delete d; } } #include "createclass.moc" diff --git a/language/codegen/createclass.h b/language/codegen/createclass.h index 6cd73431be..d97b844aa7 100644 --- a/language/codegen/createclass.h +++ b/language/codegen/createclass.h @@ -1,321 +1,323 @@ /* This file is part of KDevelop Copyright 2008 Hamish Rodda This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. 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 KDEV_CREATECLASS_H #define KDEV_CREATECLASS_H #include "language/duchain/identifier.h" #include "language/duchain/duchainpointer.h" #include #include #include "../languageexport.h" #include class KLineEdit; class KUrl; namespace KDevelop { class OverridesPage; class IndexedDeclaration; class Context; class Declaration; class DocumentChangeSet; class KDEVPLATFORMLANGUAGE_EXPORT ClassIdentifierPage : public QWizardPage { Q_OBJECT Q_PROPERTY(QStringList inheritance READ inheritanceList()) public: ClassIdentifierPage(QWizard* parent); virtual ~ClassIdentifierPage(); /// Returns the line edit which contains the new class identifier. KLineEdit* identifierLineEdit() const; /// Returns the line edit which contains a base class identifier. KLineEdit* inheritanceLineEdit() const; /// Returns a list of inheritances for the new class QStringList inheritanceList() const; virtual bool validatePage(); Q_SIGNALS: void inheritanceChanged(); public Q_SLOTS: /// Called when an inheritance is to be added. To override in subclasses, /// (eg. if there is a problem with the base class proposed), /// don't call this implementation. virtual void addInheritance(); /** * Called when an inheritance is to be removed. * * To override in subclasses, don't call this implementation. */ virtual void removeInheritance(); /** * Called when an inheritance is to be moved up. * * To override in subclasses, don't call this implementation. */ virtual void moveUpInheritance(); /** * Called when an inheritance is to be moved up. * * To override in subclasses, don't call this implementation. */ virtual void moveDownInheritance(); /** * Parses a parent class into a QualifiedIdentifier, the default implementation * Just returns the string converted to a QualifiedIdentifier */ virtual QualifiedIdentifier parseParentClassId(const QString& inheritedObject); -private: +private Q_SLOTS: void checkMoveButtonState(); +private: + class ClassIdentifierPagePrivate* const d; }; //!@todo Add the name of the Author at the top of the license class KDEVPLATFORMLANGUAGE_EXPORT LicensePage : public QWizardPage { Q_OBJECT public: LicensePage(QWizard* parent); virtual ~LicensePage(); bool validatePage(); public Q_SLOTS: virtual void licenseComboChanged(int license); private: // data class LicensePagePrivate* const d; // methods void initializeLicenses(); QString & readLicense(int licenseIndex); bool saveLicense(); }; /** * A Class generator defines the logic to create a language-specific class * and optionally declare some members */ class KDEVPLATFORMLANGUAGE_EXPORT ClassGenerator { public: ClassGenerator(); virtual ~ClassGenerator(); /** * Generate the actual DocumentChangeSet */ virtual DocumentChangeSet generate() = 0; /** * Remove all previous base classes */ virtual void clearInheritance(); /** * Clear all in class declarations */ void clearDeclarations(); /** * Add another base class, must be the pure identifier * * \return the current list of base classes */ virtual const QList & addBaseClass(const QString &); /** * Add a declaration to insert to the new Class */ void addDeclaration(DeclarationPointer newDeclaration); /** * @return All the current declarations for this class */ const QList declarations() const; /// \return The list of all of the inherited classes const QList & inheritanceList() const; /** *Should return the suggested url of the header file for the given class-name */ virtual KUrl headerUrlFromBase(KUrl baseUrl, bool toLower=true); /** *Should return the suggested url of the implementation file for the given class-name, *if header and implementation are separate for this language. */ virtual KUrl implementationUrlFromBase(KUrl baseUrl, bool toLower=true); /** * Set the URL where the header will be implemented */ void setHeaderUrl(KUrl header); /** * Set the URL where the implementation will be implemented */ void setImplementationUrl(KUrl implementation); /** * Set the position where the header is to be inserted */ void setHeaderPosition(SimpleCursor position); /** * Set the position where the implementation stubbs are to be inserted */ void setImplementationPosition(SimpleCursor position); /** * \return The name of the class to generate (excluding namespaces) */ const QString & name() const; /** * \param identifier The Qualified identifier that the class will have */ virtual void setIdentifier(const QString & identifier); /** * \return The Identifier of the class to generate (including all used namespaces) */ virtual QString identifier() const; const QString & license() const; void setLicense(const QString & license); /** * \return The class to be generated as a Type */ virtual StructureType::Ptr objectType() const = 0; protected: /** * Set the name (without namespace) for this class */ void setName(const QString &); SimpleCursor headerPosition(); SimpleCursor implementationPosition(); KUrl & headerUrl(); KUrl & implementationUrl(); /** * Look recursively for parent classes, and add them to the Inheritance list */ void fetchParentClasses(const Context * baseClass); QList m_baseClasses; //!< These are the base classes, that are directly inherited from QList m_declarations; //!< Declarations private: struct ClassGeneratorPrivate * const d; void fetchSuperClasses(DeclarationPointer derivedClass); }; /** * Provides a wizard for creating a new class using a ClassGenerator. */ class KDEVPLATFORMLANGUAGE_EXPORT CreateClassWizard : public QWizard { Q_OBJECT public: CreateClassWizard(QWidget* parent, ClassGenerator * generator, KUrl baseUrl = KUrl()); virtual ~CreateClassWizard(); /** * Creates the generic parts of the new class wizard. */ virtual void setup(); /** * Called when the wizard completes. */ virtual void accept(); /** * \return The generator that this wizard will use */ virtual ClassGenerator * generator(); virtual ClassIdentifierPage* newIdentifierPage(); virtual OverridesPage* newOverridesPage(); private: friend class OutputPage; class CreateClassWizardPrivate* const d; }; class KDEVPLATFORMLANGUAGE_EXPORT OutputPage : public QWizardPage { Q_OBJECT public: OutputPage(CreateClassWizard* parent); virtual ~OutputPage(); virtual void initializePage(); virtual bool validatePage(); virtual bool isComplete() const; private: class OutputPagePrivate* const d; private Q_SLOTS: virtual void updateFileNames(); /** * This implementation simply enables the position widgets on a file that exists. * Derived classes should overload to set the ranges where class generation should be allowed */ virtual void updateHeaderRanges(const QString &); /** * This implementation simply enables the position widgets on a file that exists. * Derived classes should overload to set the ranges where class generation should be allowed */ virtual void updateImplementationRanges(const QString &); }; } #endif // KDEV_CREATECLASS_H diff --git a/language/duchain/stringhelpers.cpp b/language/duchain/stringhelpers.cpp index 0993ae3d04..aedfad2c55 100644 --- a/language/duchain/stringhelpers.cpp +++ b/language/duchain/stringhelpers.cpp @@ -1,610 +1,615 @@ /* Copyright 2007 David Nolden This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. 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 "stringhelpers.h" #include "safetycounter.h" #include #include namespace KDevelop { class ParamIteratorPrivate { public: QString m_prefix; QString m_source; QString m_parens; int m_cur; int m_curEnd; int m_end; int next() const { return findCommaOrEnd( m_source, m_cur, m_parens[ 1 ] ); } }; bool parenFits( QChar c1, QChar c2 ) { if( c1 == '<' && c2 == '>' ) return true; else if( c1 == '(' && c2 == ')' ) return true; else if( c1 == '[' && c2 == ']' ) return true; else if( c1 == '{' && c2 == '}' ) return true; else return false; } int findClose( const QString& str , int pos ) { int depth = 0; QList st; QChar last = ' '; for( int a = pos; a < (int)str.length(); a++) { switch(str[a].unicode()) { case '<': case '(': case '[': case '{': st.push_front( str[a] ); depth++; break; case '>': if( last == '-' ) break; case ')': case ']': case '}': if( !st.isEmpty() && parenFits(st.front(), str[a]) ) { depth--; st.pop_front(); } break; case '"': last = str[a]; a++; while( a < (int)str.length() && (str[a] != '"' || last == '\\')) { last = str[a]; a++; } continue; break; case '\'': last = str[a]; a++; while( a < (int)str.length() && (str[a] != '\'' || last == '\\')) { last = str[a]; a++; } continue; break; } last = str[a]; if( depth == 0 ) { return a; } } return -1; } int findCommaOrEnd( const QString& str , int pos, QChar validEnd) { for( int a = pos; a < (int)str.length(); a++) { switch(str[a].unicode()) { case '"': case '(': case '[': case '{': case '<': a = findClose( str, a ); if( a == -1 ) return str.length(); break; case ')': case ']': case '}': case '>': if( validEnd != ' ' && validEnd != str[a] ) continue; case ',': return a; } } return str.length(); } QString reverse( const QString& str ) { QString ret; int len = str.length(); for( int a = len-1; a >= 0; --a ) { switch(str[a].unicode()) { case '(': ret += ')'; continue; case '[': ret += ']'; continue; case '{': ret += '}'; continue; case '<': ret += '>'; continue; case ')': ret += '('; continue; case ']': ret += '['; continue; case '}': ret += '{'; continue; case '>': ret += '<'; continue; default: ret += str[a]; continue; } } return ret; } ///@todo this hackery sucks QString escapeForBracketMatching(QString str) { str.replace("<<", "$&"); str.replace(">>", "$$"); str.replace("\\\"", "$!"); str.replace("->", "$?"); return str; } QString escapeFromBracketMatching(QString str) { str.replace("$&", "<<"); str.replace("$$", ">>"); str.replace("$!", "\\\""); str.replace("$?", "->"); return str; } void skipFunctionArguments(QString str, QStringList& skippedArguments, int& argumentsStart ) { QString withStrings = escapeForBracketMatching(str); str = escapeForBracketMatching(clearStrings(str)); //Blank out everything that can confuse the bracket-matching algorithm QString reversed = reverse( str.left(argumentsStart) ); QString withStringsReversed = reverse( withStrings.left(argumentsStart) ); //Now we should decrease argumentStart at the end by the count of steps we go right until we find the beginning of the function SafetyCounter s( 1000 ); int pos = 0; int len = reversed.length(); //we are searching for an opening-brace, but the reversion has also reversed the brace while( pos < len && s ) { int lastPos = pos; pos = KDevelop::findCommaOrEnd( reversed, pos ) ; if( pos > lastPos ) { QString arg = reverse( withStringsReversed.mid(lastPos, pos-lastPos) ).trimmed(); if( !arg.isEmpty() ) skippedArguments.push_front( escapeFromBracketMatching(arg) ); //We are processing the reversed reverseding, so push to front } if( reversed[pos] == ')' || reversed[pos] == '>' ) break; else ++pos; } if( !s ) { kDebug() << "skipFunctionArguments: Safety-counter triggered"; } argumentsStart -= pos; } QString reduceWhiteSpace(QString str) { str = str.trimmed(); QString ret; QChar spaceChar = ' '; bool hadSpace = false; for( int a = 0; a < str.length(); a++ ) { if( str[a].isSpace() ) { hadSpace = true; } else { if( hadSpace ) { hadSpace = false; ret += spaceChar; } ret += str[a]; } } return ret; } void fillString( QString& str, int start, int end, QChar replacement ) { for( int a = start; a < end; a++) str[a] = replacement; } QString stripFinalWhitespace(QString str) { for( int a = str.length() - 1; a >= 0; --a ) { if( !str[a].isSpace() ) return str.left( a+1 ); } return QString(); } QString clearComments( QString str, QChar replacement ) { QString withoutStrings = clearStrings(str, '$'); SafetyCounter s( 1000 ); int lastPos = 0; int pos; int len = str.length(); while( (pos = withoutStrings.indexOf( "/*", lastPos )) != -1 ) { if( !s ) return str; int i = withoutStrings.indexOf( "*/", pos ); int iNewline = withoutStrings.indexOf( '\n', pos ); while(iNewline != -1 && iNewline < i && pos < len) { //Preserve newlines iNewline = withoutStrings.indexOf( '\n', pos ); fillString( str, pos, iNewline, replacement ); pos = iNewline+1; } if( i != -1 && i <= len - 2 ) { fillString( str, pos, i+2, replacement ); lastPos = i+2; if( lastPos == len ) break; } else { + if ( i == -1 ) { + // unterminated comment, might happen during code completion + // see also: https://bugs.kde.org/show_bug.cgi?id=231351 + fillString( str, pos, len, replacement ); + } break; } } lastPos = 0; while( (pos = withoutStrings.indexOf( "//", lastPos )) != -1 ) { if( !s ) return str; int i = withoutStrings.indexOf( '\n', pos ); if( i != -1 && i <= len - 1 ) { fillString( str, pos, i, replacement ); lastPos = i+1; } else { fillString( str, pos, len, replacement ); break; } } return str; } QString clearStrings( QString str, QChar replacement ) { bool inString = false; for(int pos = 0; pos < str.length(); ++pos) { //Skip a character a la 'b' if(!inString && str[pos] == '\'' && pos + 3 <= str.length()) { //skip the opening ' str[pos] = replacement; ++pos; if(str[pos] == '\\') { //Skip an escape character str[pos] = replacement; ++pos; } //Skip the actual character str[pos] = replacement; ++pos; //Skip the closing ' if(pos < str.length() && str[pos] == '\'') { str[pos] = replacement; } continue; } bool intoString = false; if(str[pos] == '"' && !inString) intoString = true; if(inString || intoString) { if(inString) { if(str[pos] == '"') inString = false; }else{ inString = true; } bool skip = false; if(str[pos] == '\\') skip = true; str[pos] = replacement; if(skip) { ++pos; if(pos < str.length()) str[pos] = replacement; } } } return str; } static inline bool isWhite( QChar c ) { return c.isSpace(); } static inline bool isWhite( char c ) { return QChar(c).isSpace(); } void rStrip( const QString& str, QString& from ) { if( str.isEmpty() ) return; int i = 0; int ip = from.length(); int s = from.length(); for( int a = s-1; a >= 0; a-- ) { if( isWhite( from[a] ) ) { continue; } else { if( from[a] == str[i] ) { i++; ip = a; if( i == (int)str.length() ) break; } else { break; } } } if( ip != (int)from.length() ) from = from.left( ip ); } void strip( const QString& str, QString& from ) { if( str.isEmpty() ) return; int i = 0; int ip = 0; int s = from.length(); for( int a = 0; a < s; a++ ) { if( isWhite( from[a] ) ) { continue; } else { if( from[a] == str[i] ) { i++; ip = a+1; if( i == (int)str.length() ) break; } else { break; } } } if( ip ) from = from.mid( ip ); } void rStrip( const QByteArray& str, QByteArray& from ) { if( str.isEmpty() ) return; int i = 0; int ip = from.length(); int s = from.length(); for( int a = s-1; a >= 0; a-- ) { if( isWhite( from[a] ) ) { ///@todo Check whether this can cause problems in utf-8, as only one real character is treated! continue; } else { if( from[a] == str[i] ) { i++; ip = a; if( i == (int)str.length() ) break; } else { break; } } } if( ip != (int)from.length() ) from = from.left( ip ); } void strip( const QByteArray& str, QByteArray& from ) { if( str.isEmpty() ) return; int i = 0; int ip = 0; int s = from.length(); for( int a = 0; a < s; a++ ) { if( isWhite( from[a] ) ) { ///@todo Check whether this can cause problems in utf-8, as only one real character is treated! continue; } else { if( from[a] == str[i] ) { i++; ip = a+1; if( i == (int)str.length() ) break; } else { break; } } } if( ip ) from = from.mid( ip ); } QString formatComment( const QString& comment ) { QString ret; QStringList lines = comment.split( '\n', QString::KeepEmptyParts ); if ( !lines.isEmpty() ) { QStringList::iterator it = lines.begin(); QStringList::iterator eit = lines.end(); // remove common leading chars from the beginning of lines for( ; it != eit; ++it ) { strip( "///", *it ); strip( "//", *it ); strip( "**", *it ); rStrip( "/**", *it ); } ret = lines.join( "\n" ); } return ret.trimmed(); } QByteArray formatComment( const QByteArray& comment ) { QByteArray ret; QList lines = comment.split( '\n' ); if ( !lines.isEmpty() ) { QList::iterator it = lines.begin(); QList::iterator eit = lines.end(); // remove common leading chars from the beginning of lines for( ; it != eit; ++it ) { strip( "///", *it ); strip( "//", *it ); strip( "**", *it ); rStrip( "/**", *it ); } foreach(const QByteArray& line, lines) { if(!ret.isEmpty()) ret += "\n"; ret += line; } } return ret.trimmed(); } ParamIterator::~ParamIterator() { delete d; } ParamIterator::ParamIterator( QString parens, QString source, int offset ) : d(new ParamIteratorPrivate) { d->m_source = source; d->m_parens = parens; d->m_cur = offset; d->m_curEnd = offset; d->m_end = d->m_source.length(); ///The whole search should be stopped when: A) The end-sign is found on the top-level B) A closing-brace of parameters was found int parenBegin = d->m_source.indexOf( parens[ 0 ], offset ); //Search for an interrupting end-sign that comes before the found paren-begin int foundEnd = -1; if( parens.length() > 2 ) { foundEnd = d->m_source.indexOf( parens[2], offset ); if( foundEnd > parenBegin && parenBegin != -1 ) foundEnd = -1; } if( foundEnd != -1 ) { //We have to stop the search, because we found an interrupting end-sign before the opening-paren d->m_prefix = d->m_source.mid( offset, foundEnd - offset ); d->m_curEnd = d->m_end = d->m_cur = foundEnd; } else { if( parenBegin != -1 ) { //We have a valid prefix before an opening-paren. Take the prefix, and start iterating parameters. d->m_prefix = d->m_source.mid( offset, parenBegin - offset ); d->m_cur = parenBegin + 1; d->m_curEnd = d->next(); if( d->m_curEnd == d->m_source.length() ) { //The paren was not closed. It might be an identifier like "operator<", so count everything as prefix. d->m_prefix = d->m_source.mid(offset); d->m_curEnd = d->m_end = d->m_cur = d->m_source.length(); } } else { //We have neither found an ending-character, nor an opening-paren, so take the whole input and end d->m_prefix = d->m_source.mid(offset); d->m_curEnd = d->m_end = d->m_cur = d->m_source.length(); } } } ParamIterator& ParamIterator::operator ++() { if( d->m_source[d->m_curEnd] == d->m_parens[1] ) { //We have reached the end-paren. Stop iterating. d->m_cur = d->m_end = d->m_curEnd + 1; } else { //Iterate on through parameters d->m_cur = d->m_curEnd + 1; if ( d->m_cur < ( int ) d->m_source.length() ) { d->m_curEnd = d->next(); } } return *this; } QString ParamIterator::operator *() { return d->m_source.mid( d->m_cur, d->m_curEnd - d->m_cur ).trimmed(); } ParamIterator::operator bool() const { return d->m_cur < ( int ) d->m_end; } QString ParamIterator::prefix() const { return d->m_prefix; } uint ParamIterator::position() const { return (uint)d->m_cur; } } diff --git a/shell/runcontroller.cpp b/shell/runcontroller.cpp index 521ff354b1..0069f0327e 100644 --- a/shell/runcontroller.cpp +++ b/shell/runcontroller.cpp @@ -1,894 +1,894 @@ /* This file is part of KDevelop Copyright 2007-2008 Hamish Rodda Copyright 2008 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 "runcontroller.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "core.h" #include "plugincontroller.h" #include "uicontroller.h" #include "projectcontroller.h" #include "mainwindow.h" #include "launchconfiguration.h" #include "launchconfigurationdialog.h" #include #include #include #include using namespace KDevelop; QString RunController::LaunchConfigurationsGroup = "Launch"; QString RunController::LaunchConfigurationsListEntry = "Launch Configurations"; static QString CurrentLaunchConfigProjectEntry = "Current Launch Config Project"; static QString CurrentLaunchConfigNameEntry = "Current Launch Config GroupName"; static QString ConfiguredFromProjectItemEntry = "Configured from ProjectItem"; typedef QPair Target; Q_DECLARE_METATYPE(Target) //TODO: Doesn't handle add/remove of launch configs in the dialog or renaming of configs //TODO: Doesn't auto-select launch configs opened from projects class DebugMode : public ILaunchMode { public: DebugMode() {} virtual KIcon icon() const { return KIcon(); } virtual QString id() const { return "debug"; } virtual QString name() const { return i18n("Debug"); } }; class ProfileMode : public ILaunchMode { public: ProfileMode() {} virtual KIcon icon() const { return KIcon(); } virtual QString id() const { return "profile"; } virtual QString name() const { return i18n("Profile"); } }; class ExecuteMode : public ILaunchMode { public: ExecuteMode() {} virtual KIcon icon() const { return KIcon("system-run"); } virtual QString id() const { return "execute"; } virtual QString name() const { return i18n("Execute"); } }; class RunController::RunControllerPrivate { public: QItemDelegate* delegate; IRunController::State state; RunController* q; QHash jobs; KActionMenu* stopAction; KAction* profileAction; KAction* runAction; KAction* dbgAction; KSelectAction* currentTargetAction; QMap launchConfigurationTypes; QList launchConfigurations; QMap launchModes; QSignalMapper* launchChangeMapper; QSignalMapper* launchAsMapper; QMap > launchAsInfo; KDevelop::ProjectBaseItem* contextItem; bool hasLaunchConfigType( const QString& typeId ) { return launchConfigurationTypes.contains( typeId ); } void saveCurrentLaunchAction() { if (!currentTargetAction) return; if( currentTargetAction->currentAction() ) { KConfigGroup grp = Core::self()->activeSession()->config()->group( RunController::LaunchConfigurationsGroup ); LaunchConfiguration* l = static_cast( qVariantValue( currentTargetAction->currentAction()->data() ) ); grp.writeEntry( CurrentLaunchConfigProjectEntry, l->project() ? l->project()->name() : "" ); grp.writeEntry( CurrentLaunchConfigNameEntry, l->configGroupName() ); grp.sync(); } } void enableLaunchActions() { runAction->setEnabled( !launchConfigurations.isEmpty() ); profileAction->setEnabled( !launchConfigurations.isEmpty() ); dbgAction->setEnabled( !launchConfigurations.isEmpty() ); } void configureLaunches() { LaunchConfigurationDialog dlg; dlg.exec(); } QString launchActionText( LaunchConfiguration* l ) { QString label; if( l->project() ) { label = QString("%1 : %2").arg( l->project()->name()).arg(l->name()); } else { label = QString("%1" ).arg(l->name()); } return label; } void launchAs( int id ) { //kDebug() << "Launching id:" << id; QPair info = launchAsInfo[id]; //kDebug() << "fetching type and mode:" << info.first << info.second; LaunchConfigurationType* type = launchConfigurationTypeForId( info.first ); ILaunchMode* mode = q->launchModeForId( info.second ); //kDebug() << "got mode and type:" << type << type->id() << mode << mode->id(); if( type && mode ) { ILauncher* launcher = 0; foreach (ILauncher *l, type->launchers()) { //kDebug() << "avaliable launcher" << l << l->id() << l->supportedModes(); if (l->supportedModes().contains(mode->id())) { launcher = l; break; } } if (launcher) { QStringList itemPath = Core::self()->projectController()->projectModel()->pathFromIndex(contextItem->index()); ILaunchConfiguration* ilaunch = 0; foreach (LaunchConfiguration *l, launchConfigurations) { QStringList path = l->config().readEntry(ConfiguredFromProjectItemEntry, QStringList()); if (path == itemPath) { //kDebug() << "allready generated ilaunch" << ilaunch; ilaunch = l; break; } } if (!ilaunch) { ilaunch = q->createLaunchConfiguration( type, qMakePair( mode->id(), launcher->id() ), contextItem->project(), contextItem->text() ); LaunchConfiguration* launch = dynamic_cast( ilaunch ); type->configureLaunchFromItem( launch->config(), contextItem ); launch->config().writeEntry(ConfiguredFromProjectItemEntry, itemPath); //kDebug() << "created config, launching"; } else { //kDebug() << "reusing generated config, launching"; } q->setDefaultLaunch(ilaunch); q->execute( mode->id(), ilaunch ); } } } void updateCurrentLaunchAction() { if (!currentTargetAction) return; KConfigGroup launchGrp = Core::self()->activeSession()->config()->group( RunController::LaunchConfigurationsGroup ); QString currentLaunchProject = launchGrp.readEntry( CurrentLaunchConfigProjectEntry, "" ); QString currentLaunchName = launchGrp.readEntry( CurrentLaunchConfigNameEntry, "" ); LaunchConfiguration* l = 0; if( currentTargetAction->currentAction() ) { l = static_cast( qVariantValue( currentTargetAction->currentAction()->data() ) ); } else if( !launchConfigurations.isEmpty() ) { l = launchConfigurations.at( 0 ); } if( l && ( ( !currentLaunchProject.isEmpty() && ( !l->project() || l->project()->name() != currentLaunchProject ) ) || l->configGroupName() != currentLaunchName ) ) { foreach( QAction* a, currentTargetAction->actions() ) { LaunchConfiguration* l = static_cast( qvariant_cast( a->data() ) ); if( currentLaunchName == l->configGroupName() && ( ( currentLaunchProject.isEmpty() && !l->project() ) || ( l->project() && l->project()->name() == currentLaunchProject ) ) ) { a->setChecked( true ); break; } } } if( !currentTargetAction->currentAction() ) { kDebug() << "oops no current action, using first if list is non-empty"; if( !currentTargetAction->actions().isEmpty() ) { currentTargetAction->actions().first()->setChecked( true ); } } } void addLaunchAction( LaunchConfiguration* l ) { if (!currentTargetAction) return; KAction* action = currentTargetAction->addAction(launchActionText( l )); action->setData(qVariantFromValue(l)); } void readLaunchConfigs( KSharedConfigPtr cfg, IProject* prj ) { KConfigGroup group(cfg, RunController::LaunchConfigurationsGroup); QStringList configs = group.readEntry( RunController::LaunchConfigurationsListEntry, QStringList() ); foreach( const QString& cfg, configs ) { KConfigGroup grp = group.group( cfg ); if( launchConfigurationTypeForId( grp.readEntry( LaunchConfiguration::LaunchConfigurationTypeEntry, "" ) ) ) { q->addLaunchConfiguration( new LaunchConfiguration( grp, prj ) ); } } } LaunchConfigurationType* launchConfigurationTypeForId( const QString& id ) { QMap::iterator it = launchConfigurationTypes.find( id ); if( it != launchConfigurationTypes.end() ) { return it.value(); } else { kWarning() << "couldn't find type for id:" << id << ". Known types:" << launchConfigurationTypes.keys(); } return 0; } }; RunController::RunController(QObject *parent) : IRunController(parent) , d(new RunControllerPrivate) { setObjectName("RunController"); // TODO: need to implement compile only if needed before execute // TODO: need to implement abort all running programs when project closed d->currentTargetAction = 0; d->state = Idle; d->q = this; d->delegate = new RunDelegate(this); d->launchChangeMapper = new QSignalMapper( this ); d->launchAsMapper = 0; d->contextItem = 0; if(!(Core::self()->setupFlags() & Core::NoUi)) { // Note that things like registerJob() do not work without the actions, it'll simply crash. setupActions(); } } RunController::~RunController() { delete d; } void KDevelop::RunController::launchChanged( LaunchConfiguration* l ) { foreach( QAction* a, d->currentTargetAction->actions() ) { if( static_cast( qVariantValue( a->data() ) ) == l ) { a->setText( d->launchActionText( l ) ); break; } } } void RunController::cleanup() { stopAllProcesses(); d->saveCurrentLaunchAction(); } void RunController::initialize() { addLaunchMode( new ExecuteMode() ); addLaunchMode( new ProfileMode() ); addLaunchMode( new DebugMode() ); d->readLaunchConfigs( Core::self()->activeSession()->config(), 0 ); foreach (IProject* project, Core::self()->projectController()->projects()) { slotProjectOpened(project); } connect(Core::self()->projectController(), SIGNAL(projectOpened( KDevelop::IProject* )), this, SLOT(slotProjectOpened(KDevelop::IProject*))); connect(Core::self()->projectController(), SIGNAL(projectClosing( KDevelop::IProject* )), this, SLOT(slotProjectClosing(KDevelop::IProject*))); connect(Core::self()->projectController(), SIGNAL(projectConfigurationChanged(KDevelop::IProject*)), this, SLOT(slotRefreshProject(KDevelop::IProject*))); if( (Core::self()->setupFlags() & Core::NoUi) == 0 ) { // Only do this in GUI mode d->updateCurrentLaunchAction(); d->enableLaunchActions(); } } KJob* RunController::execute(const QString& runMode, ILaunchConfiguration* launch) { if( !launch ) { kDebug() << "execute called without launch config!"; return 0; } LaunchConfiguration *run = dynamic_cast(launch); //TODO: Port to launch framework, probably needs to be part of the launcher //if(!run.dependencies().isEmpty()) // ICore::self()->documentController()->saveAllDocuments(IDocument::Silent); //foreach(KJob* job, run.dependencies()) //{ // jobs.append(job); //} kDebug() << "mode:" << runMode; QString launcherId = run->launcherForMode( runMode ); kDebug() << "launcher id:" << launcherId; ILauncher* launcher = run->type()->launcherForId( launcherId ); if( !launcher ) { kWarning() << i18n("Launcher could not be found for the name '%1'. Check the launch configuration.", launcherId ); return 0; } KJob* launchJob = launcher->start(runMode, run); registerJob(launchJob); return launchJob; } void RunController::setupActions() { KAction *action; // TODO not multi-window friendly, FIXME KActionCollection* ac = Core::self()->uiControllerInternal()->defaultMainWindow()->actionCollection(); action = new KAction (i18n("Configure Launches..."), this); ac->addAction("configure_launches", action); action->setStatusTip(i18n("Open Launch Configuration Dialog")); action->setToolTip(i18n("Open Launch Configuration Dialog")); action->setWhatsThis(i18n("

Opens a dialog to setup new launch configurations, or to change the existing ones.

")); connect(action, SIGNAL(triggered(bool)), SLOT(configureLaunches())); d->runAction = new KAction( KIcon("system-run"), i18n("Execute Launch"), this); d->runAction->setIconText( i18nc("Short text for 'Execute Launch' used in the toolbar", "Execute") ); d->runAction->setShortcut(Qt::SHIFT + Qt::Key_F9); d->runAction->setToolTip(i18n("Execute current Launch")); d->runAction->setStatusTip(i18n("Execute current Launch")); d->runAction->setWhatsThis(i18n("Execute Launch

Executes the target or the program specified in currently active launch configuration.

")); ac->addAction("run_execute", d->runAction); connect(d->runAction, SIGNAL(triggered(bool)), this, SLOT(slotExecute())); d->dbgAction = new KAction( KIcon("dbgrun"), i18n("Debug Launch"), this); d->dbgAction->setShortcut(Qt::Key_F9); d->dbgAction->setIconText( i18nc("Short text for 'Debug Launch' used in the toolbar", "Debug") ); d->dbgAction->setToolTip(i18n("Debug current Launch")); d->dbgAction->setStatusTip(i18n("Debug current Launch")); d->dbgAction->setWhatsThis(i18n("Debug Launch

Executes the target or the program specified in currently active launch configuration inside a Debugger.

")); ac->addAction("run_debug", d->dbgAction); connect(d->dbgAction, SIGNAL(triggered(bool)), this, SLOT(slotDebug())); d->profileAction = new KAction( KIcon(""), i18n("Profile Launch"), this); d->profileAction->setToolTip(i18n("Profile current Launch")); d->profileAction->setStatusTip(i18n("Profile current Launch")); d->profileAction->setWhatsThis(i18n("Profile Launch

Executes the target or the program specified in currently active launch configuration inside a Profiler.

")); ac->addAction("run_profile", d->profileAction); connect(d->profileAction, SIGNAL(triggered(bool)), this, SLOT(slotProfile())); - action = d->stopAction = new KActionMenu( KIcon("dialog-close"), i18n("Stop Jobs"), this); + action = d->stopAction = new KActionMenu( KIcon("process-stop"), i18n("Stop Jobs"), this); action->setIconText(i18nc("Short text for 'Stop Jobs' used in the toolbar", "Stop")); action->setShortcut(Qt::Key_Escape); action->setToolTip(i18n("Stop all currently running jobs")); action->setWhatsThis(i18n("Stop Jobs

Requests that all running jobs are stopped.

")); action->setEnabled(false); ac->addAction("run_stop", action); connect(action, SIGNAL(triggered(bool)), this, SLOT(stopAllProcesses())); d->currentTargetAction = new KSelectAction( i18n("Current Launch Configuration"), this); d->currentTargetAction->setToolTip(i18n("Current Launch Configuration")); d->currentTargetAction->setStatusTip(i18n("Current Launch Configuration")); d->currentTargetAction->setWhatsThis(i18n("

Select which launch configuration to run when run is invoked.

")); ac->addAction("run_default_target", d->currentTargetAction); } LaunchConfigurationType* RunController::launchConfigurationTypeForId( const QString& id ) { return d->launchConfigurationTypeForId( id ); } void KDevelop::RunController::slotProjectOpened(KDevelop::IProject * project) { d->readLaunchConfigs( project->projectConfiguration(), project ); d->updateCurrentLaunchAction(); } void KDevelop::RunController::slotProjectClosing(KDevelop::IProject * project) { if (!d->currentTargetAction) return; foreach (QAction* action, d->currentTargetAction->actions()) { LaunchConfiguration* l = static_cast(qvariant_cast(action->data())); if ( project == l->project() ) { l->save(); d->launchConfigurations.removeAll(l); delete l; bool wasSelected = action->isChecked(); delete action; if (wasSelected && !d->currentTargetAction->actions().isEmpty()) d->currentTargetAction->actions().first()->setChecked(true); } } d->enableLaunchActions(); } void KDevelop::RunController::slotRefreshProject(KDevelop::IProject* project) { slotProjectClosing(project); slotProjectOpened(project); } void RunController::slotDebug() { executeDefaultLaunch( "debug" ); } void RunController::slotProfile() { executeDefaultLaunch( "profile" ); } void RunController::slotExecute() { executeDefaultLaunch( "execute" ); } LaunchConfiguration* KDevelop::RunController::defaultLaunch() const { QAction* projectAction = d->currentTargetAction->currentAction(); if( projectAction ) return static_cast(qvariant_cast(projectAction->data())); return 0; } void KDevelop::RunController::registerJob(KJob * job) { if (!job) return; if (!d->jobs.contains(job)) { KAction* stopJobAction = 0; if (Core::self()->setupFlags() != Core::NoUi) { stopJobAction = new KAction(job->objectName().isEmpty() ? i18n("Unnamed job") : job->objectName(), this); stopJobAction->setData(QVariant::fromValue(static_cast(job))); d->stopAction->addAction(stopJobAction); connect (stopJobAction, SIGNAL(triggered(bool)), SLOT(slotKillJob())); job->setUiDelegate( new KDialogJobUiDelegate() ); } d->jobs.insert(job, stopJobAction); connect( job, SIGNAL(finished(KJob*)), SLOT(finished(KJob*)) ); IRunController::registerJob(job); emit jobRegistered(job); } job->start(); checkState(); } void KDevelop::RunController::unregisterJob(KJob * job) { IRunController::unregisterJob(job); Q_ASSERT(d->jobs.contains(job)); // Delete the stop job action QAction *action = d->jobs.take(job); if (action) action->deleteLater(); checkState(); emit jobUnregistered(job); } void KDevelop::RunController::checkState() { bool running = false; foreach (KJob* job, d->jobs.keys()) { if (!job->isSuspended()) { running = true; break; } } if ( ( d->state != Running ? false : true ) == running ) { d->state = running ? Running : Idle; emit runStateChanged(d->state); } if (Core::self()->setupFlags() != Core::NoUi) d->stopAction->setEnabled(running); } void KDevelop::RunController::stopAllProcesses() { foreach (KJob* job, d->jobs.keys()) { if (job->capabilities() & KJob::Killable) job->kill(KJob::EmitResult); } } void KDevelop::RunController::slotKillJob() { KAction* action = dynamic_cast(sender()); Q_ASSERT(action); KJob* job = static_cast(qvariant_cast(action->data())); if (job->capabilities() & KJob::Killable) job->kill(); } void KDevelop::RunController::finished(KJob * job) { unregisterJob(job); switch (job->error()) { case KJob::NoError: case KJob::KilledJobError: case OutputJob::FailedShownError: break; default: KMessageBox::error(qApp->activeWindow(), job->errorString(), i18n("Process Error")); } } void KDevelop::RunController::suspended(KJob * job) { Q_UNUSED(job); checkState(); } void KDevelop::RunController::resumed(KJob * job) { Q_UNUSED(job); checkState(); } QList< KJob * > KDevelop::RunController::currentJobs() const { return d->jobs.keys(); } QList RunController::launchConfigurations() const { QList configs; foreach (LaunchConfiguration *config, launchConfigurationsInternal()) configs << config; return configs; } QList RunController::launchConfigurationsInternal() const { return d->launchConfigurations; } QList RunController::launchConfigurationTypes() const { return d->launchConfigurationTypes.values(); } void RunController::addConfigurationType( LaunchConfigurationType* type ) { if( !d->launchConfigurationTypes.contains( type->id() ) ) { d->launchConfigurationTypes.insert( type->id(), type ); } } void RunController::removeConfigurationType( LaunchConfigurationType* type ) { foreach( LaunchConfiguration* l, d->launchConfigurations ) { if( l->type() == type ) { d->launchConfigurations.removeAll( l ); delete l; } } d->launchConfigurationTypes.remove( type->id() ); } void KDevelop::RunController::addLaunchMode(KDevelop::ILaunchMode* mode) { if( !d->launchModes.contains( mode->id() ) ) { d->launchModes.insert( mode->id(), mode ); } } QList< KDevelop::ILaunchMode* > KDevelop::RunController::launchModes() const { return d->launchModes.values(); } void KDevelop::RunController::removeLaunchMode(KDevelop::ILaunchMode* mode) { d->launchModes.remove( mode->id() ); } KDevelop::ILaunchMode* KDevelop::RunController::launchModeForId(const QString& id) const { QMap::iterator it = d->launchModes.find( id ); if( it != d->launchModes.end() ) { return it.value(); } return 0; } void KDevelop::RunController::addLaunchConfiguration(KDevelop::LaunchConfiguration* l) { if( !d->launchConfigurations.contains( l ) ) { d->addLaunchAction( l ); d->launchConfigurations << l; d->enableLaunchActions(); if( !d->currentTargetAction->currentAction() ) { if( !d->currentTargetAction->actions().isEmpty() ) { d->currentTargetAction->actions().first()->setChecked( true ); } } connect( l, SIGNAL(nameChanged(LaunchConfiguration*)), SLOT(launchChanged(LaunchConfiguration*)) ); } } void KDevelop::RunController::removeLaunchConfiguration(KDevelop::LaunchConfiguration* l) { KConfigGroup launcherGroup; if( l->project() ) { launcherGroup = l->project()->projectConfiguration()->group( LaunchConfigurationsGroup ); } else { launcherGroup = Core::self()->activeSession()->config()->group( LaunchConfigurationsGroup ); } QStringList configs = launcherGroup.readEntry( RunController::LaunchConfigurationsListEntry, QStringList() ); configs.removeAll( l->configGroupName() ); launcherGroup.deleteGroup( l->configGroupName() ); launcherGroup.writeEntry( RunController::LaunchConfigurationsListEntry, configs ); launcherGroup.sync(); foreach( QAction* a, d->currentTargetAction->actions() ) { if( static_cast( qVariantValue( a->data() ) ) == l ) { bool wasSelected = a->isChecked(); d->currentTargetAction->removeAction( a ); if( wasSelected && !d->currentTargetAction->actions().isEmpty() ) { d->currentTargetAction->actions().first()->setChecked( true ); } break; } } d->launchConfigurations.removeAll( l ); d->enableLaunchActions(); delete l; } void KDevelop::RunController::executeDefaultLaunch(const QString& runMode) { if( !defaultLaunch() ) { kWarning() << "no default launch!"; return; } execute( runMode, defaultLaunch() ); } void RunController::setDefaultLaunch(ILaunchConfiguration* l) { foreach( QAction* a, d->currentTargetAction->actions() ) { if( static_cast( qVariantValue( a->data() ) ) == l ) { a->setChecked(true); break; } } } ILaunchConfiguration* RunController::createLaunchConfiguration ( LaunchConfigurationType* type, const QPair& launcher, IProject* project, const QString& name ) { KConfigGroup launchGroup; if( project ) { launchGroup = project->projectConfiguration()->group( RunController::LaunchConfigurationsGroup ); } else { launchGroup = Core::self()->activeSession()->config()->group( RunController::LaunchConfigurationsGroup ); } QStringList configs = launchGroup.readEntry( RunController::LaunchConfigurationsListEntry, QStringList() ); uint num = 0; QString baseName = "Launch Configuration"; while( configs.contains( QString( "%1 %2" ).arg( baseName ).arg( num ) ) ) { num++; } QString groupName = QString( "%1 %2" ).arg( baseName ).arg( num ); KConfigGroup launchConfigGroup = launchGroup.group( groupName ); QString cfgName = name; if( name.isEmpty() ) { cfgName = i18n("New %1 Configuration", type->name() ); } launchConfigGroup.writeEntry(LaunchConfiguration::LaunchConfigurationNameEntry, cfgName ); launchConfigGroup.writeEntry(LaunchConfiguration::LaunchConfigurationTypeEntry, type->id() ); launchConfigGroup.sync(); configs << groupName; launchGroup.writeEntry( RunController::LaunchConfigurationsListEntry, configs ); launchGroup.sync(); LaunchConfiguration* l = new LaunchConfiguration( launchConfigGroup, project ); l->setLauncherForMode( launcher.first, launcher.second ); Core::self()->runControllerInternal()->addLaunchConfiguration( l ); return l; } QItemDelegate * KDevelop::RunController::delegate() const { return d->delegate; } ContextMenuExtension RunController::contextMenuExtension ( Context* ctx ) { delete d->launchAsMapper; d->launchAsMapper = new QSignalMapper( this ); kDebug() << "connected launchmapper:" << connect( d->launchAsMapper, SIGNAL( mapped( int ) ), SLOT( launchAs( int ) ) ); d->launchAsInfo.clear(); d->contextItem = 0; ContextMenuExtension ext; if( ctx->type() == Context::ProjectItemContext ) { KDevelop::ProjectItemContext* prjctx = dynamic_cast( ctx ); if( prjctx->items().count() == 1 ) { ProjectBaseItem* itm = prjctx->items().at( 0 ); int i = 0; foreach( ILaunchMode* mode, d->launchModes.values() ) { KActionMenu* menu = new KActionMenu( i18n("%1 As...", mode->name() ), this ); foreach( LaunchConfigurationType* type, launchConfigurationTypes() ) { bool hasLauncher = false; foreach( ILauncher* launcher, type->launchers() ) { if( launcher->supportedModes().contains( mode->id() ) ) { hasLauncher = true; } } if( type->canLaunch(itm) && hasLauncher ) { d->launchAsInfo[i] = qMakePair( type->id(), mode->id() ); KAction* act = new KAction( d->launchAsMapper ); act->setText( type->name() ); kDebug() << "Setting up mapping for:" << i << "for action" << act->text() << "in mode" << mode->name(); d->launchAsMapper->setMapping( act, i ); kDebug() << "action connected:" << connect( act, SIGNAL( triggered() ), d->launchAsMapper, SLOT( map() ) ); menu->addAction(act); i++; } } if( menu->menu()->actions().count() > 0 ) { ext.addAction( ContextMenuExtension::RunGroup, menu); } } if( ext.actions( ContextMenuExtension::RunGroup ).count() > 0 ) { d->contextItem = itm; } } } return ext; } RunDelegate::RunDelegate( QObject* parent ) : QItemDelegate(parent), runProviderBrush( KColorScheme::View, KColorScheme::PositiveText ), errorBrush( KColorScheme::View, KColorScheme::NegativeText ) { } void RunDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const { QStyleOptionViewItem opt = option; QVariant status = index.data(Qt::UserRole+1); // if( status.isValid() && status.canConvert() ) // { // IRunProvider::OutputTypes type = status.value(); // if( type == IRunProvider::RunProvider ) // { // opt.palette.setBrush( QPalette::Text, runProviderBrush.brush( option.palette ) ); // } else if( type == IRunProvider::StandardError ) // { // opt.palette.setBrush( QPalette::Text, errorBrush.brush( option.palette ) ); // } // } QItemDelegate::paint(painter, opt, index); } #include "runcontroller.moc"