diff --git a/plugins/contextbrowser/contextbrowser.cpp b/plugins/contextbrowser/contextbrowser.cpp index fd242aa3c..a1cd00e2e 100644 --- a/plugins/contextbrowser/contextbrowser.cpp +++ b/plugins/contextbrowser/contextbrowser.cpp @@ -1,1043 +1,1045 @@ /* * This file is part of KDevelop * * Copyright 2007 David Nolden * * 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 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 "contextbrowser.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "contextbrowserview.h" #include #include #include #include #include #include #include const unsigned int highlightingTimeout = 150; using KDevelop::ILanguage; using KTextEditor::Attribute; using KTextEditor::SmartInterface; using KTextEditor::View; bool toolTipEnabled = true; static QWidget* masterWidget(QWidget* w) { while(w && w->parent() && qobject_cast(w->parent())) w = qobject_cast(w->parent()); return w; } class ContextBrowserViewFactory: public KDevelop::IToolViewFactory { public: ContextBrowserViewFactory(ContextBrowserPlugin *plugin): m_plugin(plugin) {} virtual QWidget* create(QWidget *parent = 0) { ContextBrowserView* ret = new ContextBrowserView(m_plugin, parent); QObject::connect(ret, SIGNAL(startDelayedBrowsing(KTextEditor::View*)), m_plugin, SLOT(startDelayedBrowsing(KTextEditor::View*))); QObject::connect(ret, SIGNAL(stopDelayedBrowsing()), m_plugin, SLOT(stopDelayedBrowsing())); return ret; } virtual Qt::DockWidgetArea defaultPosition() { return Qt::BottomDockWidgetArea; } virtual QString id() const { return "org.kdevelop.ContextBrowser"; } private: ContextBrowserPlugin *m_plugin; }; void ContextBrowserPlugin::createActionsForMainWindow(Sublime::MainWindow* window, QString& xmlFile, KActionCollection& actions) { xmlFile = "kdevcontextbrowser.rc" ; KAction* previousContext = actions.addAction("previous_context"); previousContext->setText( i18n("&Previous Visited Context") ); previousContext->setIcon( KIcon("go-previous-context" ) ); previousContext->setShortcut( Qt::META | Qt::Key_Left ); QObject::connect(previousContext, SIGNAL(triggered(bool)), this, SIGNAL(previousContextShortcut())); KAction* nextContext = actions.addAction("next_context"); nextContext->setText( i18n("&Next Visited Context") ); nextContext->setIcon( KIcon("go-next-context" ) ); nextContext->setShortcut( Qt::META | Qt::Key_Right ); QObject::connect(nextContext, SIGNAL(triggered(bool)), this, SIGNAL(nextContextShortcut())); KAction* previousUse = actions.addAction("previous_use"); previousUse->setText( i18n("&Previous Use") ); + previousUse->setIcon( KIcon("go-previous-use") ); previousUse->setShortcut( Qt::META | Qt::SHIFT | Qt::Key_Left ); QObject::connect(previousUse, SIGNAL(triggered(bool)), this, SLOT(previousUseShortcut())); KAction* nextUse = actions.addAction("next_use"); nextUse->setText( i18n("&Next Use") ); + nextUse->setIcon( KIcon("go-next-use") ); nextUse->setShortcut( Qt::META | Qt::SHIFT | Qt::Key_Right ); QObject::connect(nextUse, SIGNAL(triggered(bool)), this, SLOT(nextUseShortcut())); KAction* outline = actions.addAction("outline_line"); outline->setText(i18n("Context Browser")); QWidget* w = toolbarWidgetForMainWindow(window); w->setHidden(false); outline->setDefaultWidget(w); } K_PLUGIN_FACTORY(ContextBrowserFactory, registerPlugin(); ) K_EXPORT_PLUGIN(ContextBrowserFactory(KAboutData("kdevcontextbrowser","kdevcontextbrowser",ki18n("Context Browser"), "0.1", ki18n("Shows information for the current context"), KAboutData::License_GPL))) ContextBrowserPlugin::ContextBrowserPlugin(QObject *parent, const QVariantList&) : KDevelop::IPlugin(ContextBrowserFactory::componentData(), parent) , m_backupsMutex(QMutex::Recursive), m_viewFactory(new ContextBrowserViewFactory(this)) { core()->uiController()->addToolView(i18n("Code Browser"), m_viewFactory); connect( core()->documentController(), SIGNAL( textDocumentCreated( KDevelop::IDocument* ) ), this, SLOT( textDocumentCreated( KDevelop::IDocument* ) ) ); connect( core()->documentController(), SIGNAL( documentClosed( KDevelop::IDocument* ) ), this, SLOT( documentClosed( KDevelop::IDocument* ) ) ); connect( core()->languageController()->backgroundParser(), SIGNAL(parseJobFinished(KDevelop::ParseJob*)), this, SLOT(parseJobFinished(KDevelop::ParseJob*))); connect( DUChain::self(), SIGNAL( declarationSelected(DeclarationPointer) ), this, SLOT( declarationSelectedInUI(DeclarationPointer) ) ); m_updateTimer = new QTimer(this); m_updateTimer->setSingleShot(true); connect( m_updateTimer, SIGNAL( timeout() ), this, SLOT( updateViews() ) ); //Needed global action for the context-menu extensions m_findUses = new QAction(i18n("Find Uses"), this); connect(m_findUses, SIGNAL(triggered(bool)), this, SLOT(findUses())); } ContextBrowserPlugin::~ContextBrowserPlugin() { foreach (KTextEditor::SmartRange* range, m_watchedRanges) range->removeWatcher(this); } void ContextBrowserPlugin::watchRange(KTextEditor::SmartRange* range) { KTextEditor::SmartInterface* smart = dynamic_cast(range->document()); Q_ASSERT(smart); QMutexLocker lock(smart->smartMutex()); if (range->watchers().contains( this )) return; range->addWatcher(this); m_watchedRanges.insert(range); } void ContextBrowserPlugin::ignoreRange(KTextEditor::SmartRange* range) { if (!range->watchers().contains( this )) return; range->removeWatcher(this); m_watchedRanges.remove(range); } void ContextBrowserPlugin::unload() { core()->uiController()->removeToolView(m_viewFactory); } void ContextBrowserPlugin::rangeDeleted( KTextEditor::SmartRange *range ) { QMutexLocker lock(&m_backupsMutex); m_backups.remove( range ); m_watchedRanges.remove(range); for(QMap::iterator it = m_highlightedRange.begin(); it != m_highlightedRange.end(); ++it) if(*it == range) { m_highlightedRange.erase(it); return; } } ///@todo this doesn't work, we use TextHintInterface instead atm. void ContextBrowserPlugin::mouseEnteredRange( KTextEditor::SmartRange* range, View* view ) { m_mouseHoverCursor = SimpleCursor(range->start()); m_mouseHoverDocument = view->document()->url(); m_updateViews << view; m_updateTimer->start(1); } void ContextBrowserPlugin::mouseExitedRange( KTextEditor::SmartRange* /*range*/, View* view ) { clearMouseHover(); m_updateViews << view; m_updateTimer->start(1); // triggers updateViews() } KDevelop::ContextMenuExtension ContextBrowserPlugin::contextMenuExtension(KDevelop::Context* context) { KDevelop::ContextMenuExtension menuExt = KDevelop::IPlugin::contextMenuExtension( context ); KDevelop::DeclarationContext *codeContext = dynamic_cast(context); if (!codeContext) return menuExt; DUChainReadLocker lock(DUChain::lock()); if(!codeContext->declaration().data()) return menuExt; qRegisterMetaType("KDevelop::IndexedDeclaration"); m_findUses->setData(QVariant::fromValue(codeContext->declaration())); menuExt.addAction(KDevelop::ContextMenuExtension::ExtensionGroup, m_findUses); return menuExt; } void ContextBrowserPlugin::findUses() { QAction* action = qobject_cast(sender()); Q_ASSERT(action); DUChainReadLocker lock(DUChain::lock()); KDevelop::IndexedDeclaration decl = action->data().value(); if(!decl.data()) return; QWidget* widget = ICore::self()->uiController()->findToolView(i18n("Code Browser"), m_viewFactory, KDevelop::IUiController::CreateAndRaise); if(!widget) return; ContextBrowserView* view = dynamic_cast(widget); Q_ASSERT(view); view->allowLockedUpdate(); view->setDeclaration(decl.data(), decl.data()->topContext(), true); KDevelop::AbstractNavigationWidget* navigationWidget = dynamic_cast(view->navigationWidget()); if(navigationWidget) navigationWidget->executeContextAction("show_uses"); } void ContextBrowserPlugin::textHintRequested(const KTextEditor::Cursor& cursor, QString&) { m_mouseHoverCursor = SimpleCursor(cursor); View* view = dynamic_cast(sender()); if(!view) { kWarning() << "could not cast to view"; }else{ m_mouseHoverDocument = view->document()->url(); m_updateViews << view; } m_updateTimer->start(1); // triggers updateViews() if(toolTipEnabled) showToolTip(view, cursor); } void ContextBrowserPlugin::stopDelayedBrowsing() { if(m_currentToolTip) { m_currentToolTip->deleteLater(); m_currentToolTip = 0; } } void ContextBrowserPlugin::startDelayedBrowsing(KTextEditor::View* view) { if(!m_currentToolTip) { showToolTip(view, view->cursorPosition()); } } void ContextBrowserPlugin::hideTooTip() { if(m_currentToolTip) { m_currentToolTip->deleteLater(); m_currentToolTip = 0; } } void ContextBrowserPlugin::showToolTip(KTextEditor::View* view, KTextEditor::Cursor position) { KUrl viewUrl(view->document()->url()); QList languages = ICore::self()->languageController()->languagesForUrl(viewUrl); QWidget* navigationWidget = 0; { DUChainReadLocker lock(DUChain::lock()); foreach( ILanguage* language, languages) { navigationWidget = language->languageSupport()->specialLanguageObjectNavigationWidget(viewUrl, SimpleCursor(position)); if(navigationWidget) break; } if(!navigationWidget) { Declaration* decl = DUChainUtils::declarationForDefinition( DUChainUtils::itemUnderCursor(viewUrl, SimpleCursor(position)) ); if(decl) { if(m_currentToolTipDeclaration == IndexedDeclaration(decl) && m_currentToolTip) return; m_currentToolTipDeclaration = IndexedDeclaration(decl); navigationWidget = decl->context()->createNavigationWidget(decl, DUChainUtils::standardContextForUrl(viewUrl)); } } } if(navigationWidget) { foreach(ContextBrowserView* contextView, m_views) if(masterWidget(contextView) == masterWidget(view)) { if(contextView->isVisible()) { //There is a context-browser view visible, we don't need a tooltip delete navigationWidget; return; } contextView->setNavigationWidget(navigationWidget); } if(m_currentToolTip) { m_currentToolTip->deleteLater(); m_currentToolTip = 0; } KDevelop::NavigationToolTip* tooltip = new KDevelop::NavigationToolTip(view, view->mapToGlobal(view->cursorToCoordinate(position)) + QPoint(20, 40), navigationWidget); tooltip->resize( navigationWidget->sizeHint() + QSize(10, 10) ); kDebug() << "tooltip size" << tooltip->size(); m_currentToolTip = tooltip; ActiveToolTip::showToolTip(tooltip); //First disconnect to prevent multiple connections disconnect(view, SIGNAL(cursorPositionChanged(KTextEditor::View*,KTextEditor::Cursor)), this, SLOT(hideTooTip())); disconnect(view, SIGNAL(focusOut(KTextEditor::View*)), this, SLOT(hideTooTip())); connect(view, SIGNAL(cursorPositionChanged(KTextEditor::View*,KTextEditor::Cursor)), this, SLOT(hideTooTip())); connect(view, SIGNAL(focusOut(KTextEditor::View*)), this, SLOT(hideTooTip())); }else{ kDebug() << "not showing tooltip, no navigation-widget"; } } void ContextBrowserPlugin::clearMouseHover() { m_mouseHoverCursor = SimpleCursor::invalid(); m_mouseHoverDocument.clear(); } Attribute::Ptr highlightedUseAttribute(bool /*mouseHighlight*/, bool bold) { if(bold) { static Attribute::Ptr standardBoldAttribute = Attribute::Ptr(); if( !standardBoldAttribute ) { standardBoldAttribute= Attribute::Ptr( new Attribute() ); standardBoldAttribute->setBackgroundFillWhitespace(true); standardBoldAttribute->setBackground(QColor(255, 255, 0, 150));//QApplication::palette().toolTipBase()); standardBoldAttribute->setFontBold(true); } return standardBoldAttribute; }else{ static Attribute::Ptr standardAttribute = Attribute::Ptr(); if( !standardAttribute ) { standardAttribute = Attribute::Ptr( new Attribute() ); standardAttribute->setBackgroundFillWhitespace(true); standardAttribute->setBackground(QColor(255, 255, 0, 150));//QApplication::palette().toolTipBase()); } return standardAttribute; } } Attribute::Ptr highlightedDeclarationAttribute() { static Attribute::Ptr standardAttribute = Attribute::Ptr(); if( !standardAttribute ) { standardAttribute = Attribute::Ptr( new Attribute() ); standardAttribute->setBackgroundFillWhitespace(true); standardAttribute->setBackground(Qt::red); } return standardAttribute; } Attribute::Ptr highlightedSpecialObjectAttribute() { static Attribute::Ptr standardAttribute = Attribute::Ptr(); if( !standardAttribute ) { standardAttribute = Attribute::Ptr( new Attribute() ); standardAttribute->setBackgroundFillWhitespace(true); standardAttribute->setBackground(QColor(90, 255, 0, 150));//QApplication::palette().toolTipBase()); } return standardAttribute; } void ContextBrowserPlugin::changeHighlight( KTextEditor::SmartRange* range, bool highlight, bool /*declaration*/, bool mouseHighlight ) { if( !range ) return; QMutexLocker lock(&m_backupsMutex); if( highlight ) { /* if( declaration ) attrib = highlightedDeclarationAttribute(); else*/ Attribute::Ptr attrib = highlightedUseAttribute(mouseHighlight, range->attribute() ? range->attribute()->fontBold() : false); if( !m_backups.contains(range) ) { m_backups[range] = qMakePair(attrib, range->attribute()); watchRange(range); } range->setAttribute(attrib); }else{ //Reset old highlighting, if our set attribute is still on the range if( m_backups.contains(range) && m_backups[range].first == range->attribute() ) { range->setAttribute(m_backups[range].second); m_backups.remove(range); } ignoreRange(range); } } void ContextBrowserPlugin::changeHighlight( View* view, KDevelop::Declaration* decl, bool highlight, bool mouseHighlight ) { if( !view || !decl || !decl->context() ) { kDebug() << "invalid view/declaration"; return; } KTextEditor::SmartRange* range = decl->smartRange(); if( range ) changeHighlight( range, highlight, true, mouseHighlight ); QList uses; { KDevelop::DUChainReadLocker lock( DUChain::lock() ); uses = decl->smartUses(); } foreach(KTextEditor::SmartRange* range, uses) changeHighlight( range, highlight, false, mouseHighlight ); if( FunctionDefinition* def = FunctionDefinition::definition(decl) ) if( def->smartRange() ) changeHighlight( def->smartRange(), highlight, false, mouseHighlight ); } bool ContextBrowserPlugin::findSpecialObject(View* view, const SimpleCursor& position, ILanguage*& pickedLanguage) { SmartInterface* iface = dynamic_cast(view->document()); if (!iface) return false; if(m_highlightedRange.contains(view)) { // remove old highlighting QMutexLocker lock(iface->smartMutex()); //Q_ASSERT(m_highlightedRange[view]->document() == view->document()); if (m_highlightedRange[view]->document() == view->document()) { delete m_highlightedRange[view]; m_highlightedRange.remove(view); } else { kDebug() << "m_highlightedRange[view]->document() != view->document()"; } } KUrl viewUrl = view->document()->url(); QList languages = ICore::self()->languageController()->languagesForUrl(viewUrl); SimpleRange r = SimpleRange::invalid(); foreach( ILanguage* language, languages) { r = language->languageSupport()->specialLanguageObjectRange(viewUrl, position); if(r.isValid()) { pickedLanguage = language; break; } } if (r.isValid()) { m_highlightedRange[view] = iface->newSmartRange( r.textRange(), m_highlightedRange[view] );//iface->newSmartRange( view->document()->documentRange() ); iface->addHighlightToDocument(m_highlightedRange[view]); m_highlightedRange[view]->setAttribute( highlightedSpecialObjectAttribute() ); watchRange(m_highlightedRange[view]); return true; } else { pickedLanguage = 0; return false; } } Declaration* ContextBrowserPlugin::findDeclaration(View* view, const SimpleCursor& position, bool mouseHighlight) { Q_UNUSED(mouseHighlight); Declaration* foundDeclaration = 0; if(m_useDeclaration.data()) { foundDeclaration = m_useDeclaration.data(); }else{ //If we haven't found a special language object, search for a use/declaration and eventually highlight it foundDeclaration = DUChainUtils::declarationForDefinition( DUChainUtils::itemUnderCursor(view->document()->url(), position) ); } return foundDeclaration; } bool ContextBrowserPlugin::showDeclarationView(View* view, const SimpleCursor& position, Declaration* foundDeclaration, DUContext* ctx) { Q_UNUSED(position); Q_ASSERT(foundDeclaration); bool success = false; foreach(ContextBrowserView* contextView, m_views) { if(masterWidget(contextView) == masterWidget(view)) { contextView->setDeclaration(foundDeclaration, ctx->topContext()); success = true; } } return success; } bool ContextBrowserPlugin::showSpecialObjectView(View* view, const SimpleCursor& position, ILanguage* pickedLanguage, DUContext* ctx) { //Q_ASSERT(pickedLanguage != 0); // specialObject was found, pickedLanguage must have been set if (!pickedLanguage) { kDebug() << "Special's object language turned null."; return false; } bool success = false; foreach(ContextBrowserView* contextView, m_views) { if(masterWidget(contextView) == masterWidget(view)) { ILanguageSupport* ls = pickedLanguage->languageSupport(); QWidget* w = ls->specialLanguageObjectNavigationWidget(view->document()->url(), position); contextView->setSpecialNavigationWidget(w); success = true; } } return success; } void ContextBrowserPlugin::showContextView(View* view, const SimpleCursor& position, DUContext* ctx) { Q_UNUSED(position); Q_ASSERT(ctx); foreach(ContextBrowserView* contextView, m_views) { if(masterWidget(contextView) == masterWidget(view)) { contextView->setContext(SpecializationStore::self().applySpecialization(ctx, ctx->topContext())); } } } DUContext* contextAt(const SimpleCursor& position, TopDUContext* topContext) { DUContext* ctx = topContext->findContextAt(position); while(ctx && (ctx->type() == DUContext::Template || ctx->type() == DUContext::Helper || ctx->localScopeIdentifier().isEmpty()) && ctx->parentContext()) ctx = ctx->parentContext(); return ctx; } void ContextBrowserPlugin::unHighlightAll(KTextEditor::View* selectView) { foreach(KTextEditor::View* view, m_highlightedDeclarations.keys()) { if(selectView && selectView != view) continue; KTextEditor::SmartInterface* smart = dynamic_cast(view->document()); QMutexLocker smartLock(smart->smartMutex()); QMutexLocker lock(&m_backupsMutex); QMap< KTextEditor::SmartRange*, QPair< Attribute::Ptr, Attribute::Ptr > >::iterator it = m_backups.begin(); while(it != m_backups.end()) { if(it.key()->document() == view->document()) { if(it.key()->attribute() == it->first) it.key()->setAttribute(it->second); //Set the backed up attribute, if it wasn't changed yet ignoreRange(it.key()); it = m_backups.erase(it); } else { ++it; } } changeHighlight( view, m_highlightedDeclarations[view].data(), false, false ); } m_highlightedDeclarations.clear(); } void ContextBrowserPlugin::updateBrowserWidgetFor(View* view) { bool keptHighlightedDeclaration = m_keepHighlightedDeclaration.contains(view) && m_highlightedDeclarations[view].data(); m_keepHighlightedDeclaration.remove(view); bool mouseHighlight = (view->document()->url() == m_mouseHoverDocument) && (m_mouseHoverCursor.isValid()); SimpleCursor position; if (mouseHighlight) { position = m_mouseHoverCursor; } else { position = SimpleCursor(view->cursorPosition()); } ///First: Check whether there is a special language object ///@todo Maybe make this optional, because it can be slow ILanguage* pickedLanguage = 0; bool foundSpecialObject = 0; if(!keptHighlightedDeclaration) foundSpecialObject = findSpecialObject(view,position, pickedLanguage); KDevelop::DUChainReadLocker lock( DUChain::lock(), 100 ); if(!lock.locked()) { kDebug() << "Failed to lock du-chain in time"; return; } if(!keptHighlightedDeclaration) unHighlightAll(); TopDUContext* topContext = DUChainUtils::standardContextForUrl(view->document()->url()); if (!topContext) return; DUContext* ctx = contextAt(position, topContext); if (!ctx) return; Declaration* foundDeclaration = 0; //Only update the history if this context is around the text cursor if(core()->documentController()->activeDocument() && position == SimpleCursor(view->cursorPosition()) && view->document() == core()->documentController()->activeDocument()->textDocument()) { foreach(ContextBrowserView* contextView, m_views) if(masterWidget(contextView) == masterWidget(view)) contextView->updateHistory(ctx, position); } if(keptHighlightedDeclaration) { foundDeclaration = m_highlightedDeclarations[view].data(); kDebug() << "keeping"; } if(!foundSpecialObject && !foundDeclaration) foundDeclaration = findDeclaration(view, position, mouseHighlight); if( foundDeclaration ) { m_lastHighlightedDeclaration = IndexedDeclaration(foundDeclaration); m_highlightedDeclarations[view] = foundDeclaration; changeHighlight( view, foundDeclaration, true, mouseHighlight ); }else{ m_highlightedDeclarations.remove(view); } bool addedWidget = false; // try to add a declaration navigation widget if(foundDeclaration || foundSpecialObject) { IDocument* doc = core()->documentController()->activeDocument(); if(mouseHighlight || (view->isActiveView() && doc && doc->textDocument() == view->document())) { addedWidget = foundSpecialObject ? showSpecialObjectView(view, position, pickedLanguage, ctx) : showDeclarationView(view, position, foundDeclaration, ctx); } } // if that failed, try to add a context navigation widget if(!addedWidget && !mouseHighlight) { showContextView(view, position, ctx); } } void ContextBrowserPlugin::updateViews() { foreach( View* view, m_updateViews ) { updateBrowserWidgetFor(view); } m_updateViews.clear(); m_useDeclaration = IndexedDeclaration(); } void ContextBrowserPlugin::declarationSelectedInUI(DeclarationPointer decl) { m_useDeclaration = IndexedDeclaration(decl.data()); if(core()->documentController()->activeDocument() && core()->documentController()->activeDocument()->textDocument() && core()->documentController()->activeDocument()->textDocument()->activeView()) m_updateViews << core()->documentController()->activeDocument()->textDocument()->activeView(); m_updateTimer->start(highlightingTimeout); // triggers updateViews() } void ContextBrowserPlugin::parseJobFinished(KDevelop::ParseJob* job) { if(job->duChain() && job->duChain()->smartRange()) { KDevelop::DUChainReadLocker lock( DUChain::lock() ); registerAsRangeWatcher(job->duChain()); } for(QMap< View*, DeclarationPointer >::const_iterator it = m_highlightedDeclarations.begin(); it != m_highlightedDeclarations.end(); ++it) { if(it.key()->document()->url() == job->document().toUrl()) { if(m_updateViews.isEmpty()) m_updateTimer->start(highlightingTimeout); if(!m_updateViews.contains(it.key())) { kDebug() << "adding view for update"; m_updateViews << it.key(); m_keepHighlightedDeclaration.insert(it.key()); } } } } void ContextBrowserPlugin::registerAsRangeWatcher(KDevelop::DUChainBase* base) { if(base->smartRange()) { watchRange(base->smartRange()); } } void ContextBrowserPlugin::registerAsRangeWatcher(KDevelop::DUContext* ctx) { if(!ctx || !ctx->smartRange()) return; QMutex* lockSmartMutex = 0; if(!ctx->parentContext()) { KTextEditor::SmartInterface* smart = dynamic_cast(ctx->smartRange()->document()); Q_ASSERT(smart); lockSmartMutex = smart->smartMutex(); } QMutexLocker lock(lockSmartMutex); if(dynamic_cast(ctx) && static_cast(ctx)->parsingEnvironmentFile() && static_cast(ctx)->parsingEnvironmentFile()->isProxyContext() && !ctx->importedParentContexts().isEmpty()) return registerAsRangeWatcher(ctx->importedParentContexts()[0].context(0)); foreach(Declaration* decl, ctx->localDeclarations()) registerAsRangeWatcher(decl); for(int a = 0; a < ctx->usesCount(); ++a) { KTextEditor::SmartRange* range = ctx->useSmartRange(a); if(range) watchRange(range); } foreach(DUContext* child, ctx->childContexts()) registerAsRangeWatcher(child); } void ContextBrowserPlugin::textDocumentCreated( KDevelop::IDocument* document ) { Q_ASSERT(document->textDocument()); connect( document->textDocument(), SIGNAL(destroyed( QObject* )), this, SLOT( documentDestroyed( QObject* ) ) ); connect( document->textDocument(), SIGNAL( viewCreated( KTextEditor::Document* , KTextEditor::View* ) ), this, SLOT( viewCreated( KTextEditor::Document*, KTextEditor::View* ) ) ); foreach( View* view, document->textDocument()->views() ) viewCreated( document->textDocument(), view ); KDevelop::DUChainReadLocker lock( DUChain::lock() ); QList chains = DUChain::self()->chainsForDocument( document->url() ); foreach( TopDUContext* chain, chains ) registerAsRangeWatcher( chain ); } void ContextBrowserPlugin::documentClosed( KDevelop::IDocument* /*document*/ ) { } void ContextBrowserPlugin::documentDestroyed( QObject* /*obj*/ ) { } void ContextBrowserPlugin::viewDestroyed( QObject* obj ) { m_highlightedDeclarations.remove(static_cast(obj)); m_updateViews.remove(static_cast(obj)); } void ContextBrowserPlugin::cursorPositionChanged( View* view, const KTextEditor::Cursor& newPosition ) { if(view->document() == m_lastInsertionDocument && newPosition == m_lastInsertionPos) { //Do not update the highlighting while typing m_lastInsertionDocument = 0; m_lastInsertionPos = KTextEditor::Cursor(); m_keepHighlightedDeclaration.insert(view); }else{ m_keepHighlightedDeclaration.remove(view); } clearMouseHover(); m_updateViews.insert(view); m_updateTimer->start(highlightingTimeout/2); // triggers updateViews() } void ContextBrowserPlugin::textInserted(KTextEditor::Document* doc, KTextEditor::Range range) { m_lastInsertionDocument = doc; m_lastInsertionPos = range.end(); } void ContextBrowserPlugin::viewCreated( KTextEditor::Document* , View* v ) { disconnect( v, SIGNAL( cursorPositionChanged( KTextEditor::View*, const KTextEditor::Cursor& ) ), this, SLOT( cursorPositionChanged( KTextEditor::View*, const KTextEditor::Cursor& ) ) ); ///Just to make sure that multiple connections don't happen connect( v, SIGNAL( cursorPositionChanged( KTextEditor::View*, const KTextEditor::Cursor& ) ), this, SLOT( cursorPositionChanged( KTextEditor::View*, const KTextEditor::Cursor& ) ) ); connect( v, SIGNAL(destroyed( QObject* )), this, SLOT( viewDestroyed( QObject* ) ) ); disconnect( v->document(), SIGNAL(textInserted(KTextEditor::Document*,KTextEditor::Range)), this, SLOT(textInserted(KTextEditor::Document*,KTextEditor::Range))); connect( v->document(), SIGNAL(textInserted(KTextEditor::Document*,KTextEditor::Range)), this, SLOT(textInserted(KTextEditor::Document*,KTextEditor::Range))); KTextEditor::TextHintInterface *iface = dynamic_cast(v); if( !iface ) return; iface->enableTextHints(highlightingTimeout); connect(v, SIGNAL(needTextHint(const KTextEditor::Cursor&, QString&)), this, SLOT(textHintRequested(const KTextEditor::Cursor&, QString&))); } void ContextBrowserPlugin::registerToolView(ContextBrowserView* view) { m_views << view; } void ContextBrowserPlugin::previousUseShortcut() { switchUse(false); } void ContextBrowserPlugin::nextUseShortcut() { switchUse(true); } KTextEditor::Range cursorToRange(SimpleCursor cursor) { return KTextEditor::Range(cursor.textCursor(), cursor.textCursor()); } void ContextBrowserPlugin::switchUse(bool forward) { if(core()->documentController()->activeDocument() && core()->documentController()->activeDocument()->textDocument() && core()->documentController()->activeDocument()->textDocument()->activeView()) { KTextEditor::Document* doc = core()->documentController()->activeDocument()->textDocument(); KDevelop::SimpleCursor c(doc->activeView()->cursorPosition()); KDevelop::DUChainReadLocker lock( DUChain::lock() ); KDevelop::TopDUContext* chosen = DUChainUtils::standardContextForUrl(doc->url()); if( chosen ) { Declaration* decl = 0; //If we have a locked declaration, use that for jumping foreach(ContextBrowserView* view, m_views) { decl = view->lockedDeclaration().data(); ///@todo Somehow match the correct context-browser view if there is multiple if(decl) break; } if(!decl) //Try finding a declaration under the cursor decl = DUChainUtils::itemUnderCursor(doc->url(), c); if(decl) { Declaration* target = 0; if(forward) //Try jumping from definition to declaration target = DUChainUtils::declarationForDefinition(decl, chosen); else if(decl->url().toUrl() == doc->url() && decl->range().contains(c)) //Try jumping from declaration to definition target = FunctionDefinition::definition(decl); if(target && target != decl) { SimpleCursor jumpTo = target->range().start; KUrl document = target->url().toUrl(); lock.unlock(); core()->documentController()->openDocument( document, cursorToRange(jumpTo) ); return; }else{ //Always work with the declaration instead of the definition decl = DUChainUtils::declarationForDefinition(decl, chosen); } } if(!decl) { //Pick the last use we have highlighted decl = m_lastHighlightedDeclaration.data(); } if(decl) { KDevVarLengthArray usingFiles = DUChain::uses()->uses(decl->id()); if(DUChainUtils::contextHasUse(decl->topContext(), decl) && usingFiles.indexOf(decl->topContext()) == -1) usingFiles.insert(decl->topContext(), 0); if(decl->range().contains(c) && decl->url() == chosen->url()) { //The cursor is directly on the declaration. Jump to the first or last use. if(!usingFiles.isEmpty()) { TopDUContext* top = (forward ? usingFiles[0] : usingFiles.back()).data(); if(top) { QList useRanges = allUses(top, decl, true); qSort(useRanges); if(!useRanges.isEmpty()) { KUrl url = top->url().toUrl(); SimpleRange selectUse = forward ? useRanges.first() : useRanges.back(); lock.unlock(); core()->documentController()->openDocument(url, cursorToRange(selectUse.start)); } } } return; } //Check whether we are within a use QList localUses = allUses(chosen, decl, true); qSort(localUses); for(int a = 0; a < localUses.size(); ++a) { int nextUse = (forward ? a+1 : a-1); bool pick = localUses[a].contains(c); if(!pick && forward && a+1 < localUses.size() && localUses[a].end <= c && localUses[a+1].start > c) { //Special case: We aren't on a use, but we are jumping forward, and are behind this and the next use pick = true; } if(!pick && !forward && a-1 >= 0 && c < localUses[a].start && c >= localUses[a-1].end) { //Special case: We aren't on a use, but we are jumping backward, and are in front of this use, but behind the previous one pick = true; } if(!pick && a == 0 && c < localUses[a].start) { if(!forward) { //Will automatically jump to previous file }else{ nextUse = 0; //We are before the first use, so jump to it. } pick = true; } if(!pick && a == localUses.size()-1 && c >= localUses[a].end) { if(forward) { //Will automatically jump to next file }else{ //We are behind the last use, but moving backward. So pick the last use. nextUse = a; } pick = true; } if(pick) { //Make sure we end up behind the use if(nextUse != a) while(forward && nextUse < localUses.size() && (localUses[nextUse].start <= localUses[a].end || localUses[nextUse].isEmpty())) ++nextUse; //Make sure we end up before the use if(nextUse != a) while(!forward && nextUse >= 0 && (localUses[nextUse].start >= localUses[a].start || localUses[nextUse].isEmpty())) --nextUse; //Jump to the next use kDebug() << "count of uses:" << localUses.size() << "nextUse" << nextUse; if(nextUse < 0 || nextUse == localUses.size()) { kDebug() << "jumping to next file"; //Jump to the first use in the next using top-context int indexInFiles = usingFiles.indexOf(chosen); if(indexInFiles != -1) { int nextFile = (forward ? indexInFiles+1 : indexInFiles-1); kDebug() << "current file" << indexInFiles << "nextFile" << nextFile; if(nextFile < 0 || nextFile >= usingFiles.size()) { //Open the declaration, or the definition if(nextFile >= usingFiles.size()) { Declaration* definition = FunctionDefinition::definition(decl); if(definition) decl = definition; } KUrl u(decl->url().str()); SimpleRange range = decl->range(); range.end = range.start; lock.unlock(); core()->documentController()->openDocument(u, range.textRange()); return; }else{ TopDUContext* nextTop = usingFiles[nextFile].data(); KUrl u(nextTop->url().str()); QList nextTopUses = allUses(nextTop, decl, true); qSort(nextTopUses); if(!nextTopUses.isEmpty()) { SimpleRange range = forward ? nextTopUses.front() : nextTopUses.back(); range.end = range.start; lock.unlock(); core()->documentController()->openDocument(u, range.textRange()); } return; } }else{ kDebug() << "not found own file in use list"; } }else{ KUrl url(chosen->url().str()); SimpleRange range = localUses[nextUse]; range.end = range.start; lock.unlock(); core()->documentController()->openDocument(url, range.textRange()); return; } } } } } } } void ContextBrowserPlugin::unRegisterToolView(ContextBrowserView* view) { m_views.removeAll(view); } QWidget* ContextBrowserPlugin::toolbarWidgetForMainWindow(QWidget* widgetInWindow) { QWidget* master = masterWidget(widgetInWindow); for(QList< QPointer< QWidget > >::iterator it = m_toolbarWidgets.begin(); it != m_toolbarWidgets.end(); ++it) { if((*it) && masterWidget(*it) == master) { return *it; } } m_toolbarWidgets.append(new QWidget(master)); m_toolbarWidgets.back()->setHidden(true); return m_toolbarWidgets.back(); } #include "contextbrowser.moc" // kate: space-indent on; indent-width 2; tab-width 4; replace-tabs on; auto-insert-doxygen on diff --git a/plugins/documentswitcher/documentswitcherplugin.cpp b/plugins/documentswitcher/documentswitcherplugin.cpp index 54d9ca6cb..8659881d0 100644 --- a/plugins/documentswitcher/documentswitcherplugin.cpp +++ b/plugins/documentswitcher/documentswitcherplugin.cpp @@ -1,333 +1,335 @@ /*************************************************************************** * Copyright 2009 Andreas Pakulat * * * * 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 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 "documentswitcherplugin.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "documentswitchertreeview.h" #include K_PLUGIN_FACTORY(DocumentSwitcherFactory, registerPlugin(); ) K_EXPORT_PLUGIN(DocumentSwitcherFactory(KAboutData("kdevdocumentswitcher","kdevdocumentswitcher",ki18n("Document Switcher"), "0.1", ki18n("Switch between open documents using most-recently-used list"), KAboutData::License_GPL))) //TODO: Show frame around view's widget while walking through //TODO: Make the widget transparent //TODO: Better placement, at cursor position might not be ideal, maybe on the right side of the central widget DocumentSwitcherPlugin::DocumentSwitcherPlugin(QObject *parent, const QVariantList &/*args*/) :KDevelop::IPlugin(DocumentSwitcherFactory::componentData(), parent), view(0) { setXMLFile("kdevdocumentswitcher.rc"); kDebug() << "Adding active mainwindow from constructor" << KDevelop::ICore::self()->uiController()->activeMainWindow(); addMainWindow( qobject_cast( KDevelop::ICore::self()->uiController()->activeMainWindow() ) ); connect( KDevelop::ICore::self()->uiController()->controller(), SIGNAL( mainWindowAdded( Sublime::MainWindow* ) ), SLOT( addMainWindow( Sublime::MainWindow* ) ) ); forwardAction = actionCollection()->addAction ( "last_used_views_forward" ); forwardAction->setText( i18n( "Last Used Views" ) ); + forwardAction->setIcon( KIcon("go-next-view-page") ); forwardAction->setShortcut( Qt::CTRL | Qt::Key_Tab ); forwardAction->setWhatsThis( i18n( "Walk through last used Views
Opens a list to walk through the list of last used views." ) ); forwardAction->setStatusTip( i18n( "Walk through the list of last used views" ) ); connect( forwardAction, SIGNAL(triggered()), SLOT(walkForward()) ); backwardAction = actionCollection()->addAction ( "last_used_views_backward" ); backwardAction->setText( i18n( "Last Used Views (Reverse)" ) ); + backwardAction->setIcon( KIcon("go-previous-view-page") ); backwardAction->setShortcut( Qt::CTRL | Qt::SHIFT | Qt::Key_Tab ); backwardAction->setWhatsThis( i18n( "Walk through last used Views (Reverse)
Opens a list to walk through the list of last used views in reverse." ) ); backwardAction->setStatusTip( i18n( "Walk through the list of last used views" ) ); connect( backwardAction, SIGNAL(triggered()), SLOT(walkBackward()) ); view = new DocumentSwitcherTreeView( this ); view->setSelectionBehavior( QAbstractItemView::SelectRows ); view->setSelectionMode( QAbstractItemView::SingleSelection ); view->addAction( forwardAction ); view->addAction( backwardAction ); connect( view, SIGNAL(clicked(const QModelIndex&)), SLOT(switchToView(const QModelIndex&)) ); connect( view, SIGNAL(activated(const QModelIndex&)), SLOT(switchToView(const QModelIndex&)) ); model = new QStandardItemModel( view ); view->setModel( model ); } void DocumentSwitcherPlugin::walkForward() { Sublime::MainWindow* window = qobject_cast( KDevelop::ICore::self()->uiController()->activeMainWindow() ); if( !window || !documentLists.contains( window ) || !documentLists[window].contains( window->area() ) ) { kWarning() << "This should not happen, tried to walk through document list of an unknown mainwindow!"; return; } QModelIndex idx; if( !view->isVisible() ) { fillModel( window ); // center on main window view->move( window->pos().x() + (window->width() - view->width()) / 2, window->pos().y() + (window->height() - view->height()) / 2 ); idx = model->index( 1, 0 ); if( !idx.isValid() ) { idx = model->index( 0, 0 ); } view->show(); } else { int newrow = view->selectionModel()->currentIndex().row() + 1; if( newrow == model->rowCount() ) { newrow = 0; } idx = model->index( newrow, 0 ); } view->selectionModel()->select( idx, QItemSelectionModel::Rows | QItemSelectionModel::ClearAndSelect ); view->selectionModel()->setCurrentIndex( idx, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows ); } void DocumentSwitcherPlugin::fillModel( Sublime::MainWindow* window ) { model->clear(); QList views; foreach( Sublime::View* v, documentLists[window][window->area()] ) { model->appendRow( new QStandardItem( v->document()->statusIcon(), v->document()->title() ) ); } } void DocumentSwitcherPlugin::walkBackward() { Sublime::MainWindow* window = qobject_cast( KDevelop::ICore::self()->uiController()->activeMainWindow() ); if( !window || !documentLists.contains( window ) || !documentLists[window].contains( window->area() ) ) { kWarning() << "This should not happen, tried to walk through document list of an unknown mainwindow!"; return; } QModelIndex idx; if( !view->isVisible() ) { fillModel( window ); // center on mainwindow view->move( window->pos().x() + (window->width() - view->width()) / 2, window->pos().y() + (window->height() - view->height()) / 2 ); idx = model->index( model->rowCount()-1, 0 ); view->show(); } else { int newrow = view->selectionModel()->currentIndex().row() - 1; if( newrow == -1 ) { newrow = model->rowCount()-1; } idx = model->index( newrow, 0 ); } view->selectionModel()->select( idx, QItemSelectionModel::Rows | QItemSelectionModel::ClearAndSelect ); view->selectionModel()->setCurrentIndex( idx, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows ); } DocumentSwitcherPlugin::~DocumentSwitcherPlugin() { } void DocumentSwitcherPlugin::switchToView( const QModelIndex& idx ) { Q_UNUSED( idx ); view->hide(); if( view->selectionModel()->selectedRows().isEmpty() ) { return; } int row = view->selectionModel()->selectedRows().first().row(); Sublime::MainWindow* window = qobject_cast( KDevelop::ICore::self()->uiController()->activeMainWindow() ); if( window && documentLists.contains( window ) && documentLists[window].contains( window->area() ) ) { const QList l = documentLists[window][window->area()]; if( row >= 0 && row < l.size() ) { window->activateView( l.at( row ) ); } } } void DocumentSwitcherPlugin::unload() { foreach( QObject* mw, documentLists.keys() ) { removeMainWindow( mw ); } delete forwardAction; delete backwardAction; view->deleteLater(); } void DocumentSwitcherPlugin::storeAreaViewList( Sublime::MainWindow* mainwindow, Sublime::Area* area ) { if( !documentLists.contains( mainwindow ) || !documentLists[mainwindow].contains(area) ) { QMap > areas; kDebug() << "adding area views for area:" << area << area->title() << "mainwindow:" << mainwindow << mainwindow->windowTitle(); foreach( Sublime::View* v, area->views() ) { kDebug() << "view:" << v << v->document()->title(); } kDebug() << "done"; areas.insert( area, area->views() ); documentLists.insert( mainwindow, areas ); } } void DocumentSwitcherPlugin::addMainWindow( Sublime::MainWindow* mainwindow ) { if( !mainwindow ) { return; } kDebug() << "adding mainwindow:" << mainwindow << mainwindow->windowTitle(); kDebug() << "storing all views from area:" << mainwindow->area()->title() << mainwindow->area(); storeAreaViewList( mainwindow, mainwindow->area() ); kDebug() << "connecting signals on mainwindow"; connect( mainwindow, SIGNAL(areaChanged(Sublime::Area*)), SLOT(changeArea(Sublime::Area*)) ); connect( mainwindow, SIGNAL(activeViewChanged(Sublime::View*)), SLOT(changeView(Sublime::View*)) ); connect( mainwindow, SIGNAL(viewAdded(Sublime::View*)), SLOT(addView(Sublime::View*)) ); connect( mainwindow, SIGNAL(aboutToRemoveView(Sublime::View*)), SLOT(removeView(Sublime::View*)) ); connect( mainwindow, SIGNAL(destroyed(QObject*)), SLOT(removeMainWindow(QObject*))); mainwindow->installEventFilter( this ); } bool DocumentSwitcherPlugin::eventFilter( QObject* watched, QEvent* ev ) { Sublime::MainWindow* mw = dynamic_cast( watched ); if( mw && ev->type() == QEvent::WindowActivate ) { enableActions(mw); } return QObject::eventFilter( watched, ev ); } void DocumentSwitcherPlugin::addView( Sublime::View* view ) { Sublime::MainWindow* mainwindow = qobject_cast( sender() ); if( !mainwindow ) return; kDebug() << "got signal from mainwindow:" << mainwindow << mainwindow->windowTitle(); kDebug() << "its area is:" << mainwindow->area() << mainwindow->area()->title(); kDebug() << "adding view:" << view << view->document()->title(); enableActions( mainwindow ); documentLists[mainwindow][mainwindow->area()].append( view ); } void DocumentSwitcherPlugin::enableActions( Sublime::MainWindow* mw ) { forwardAction->setEnabled( documentLists[mw][mw->area()].size() > 1 ); backwardAction->setEnabled( documentLists[mw][mw->area()].size() > 1 ); } void DocumentSwitcherPlugin::removeMainWindow( QObject* obj ) { if( !obj || !documentLists.contains(obj) ) { return; } obj->removeEventFilter( this ); disconnect( obj, 0, this, 0 ); documentLists.remove( obj ); } void DocumentSwitcherPlugin::changeArea( Sublime::Area* area ) { Sublime::MainWindow* mainwindow = qobject_cast( sender() ); Q_ASSERT( mainwindow ); kDebug() << "area changed:" << area << area->title() << "mainwindow:" << mainwindow << mainwindow->windowTitle(); //Since the main-window only emits aboutToRemoveView for views within the current area, we must forget all areas except the active one documentLists.remove(mainwindow); if( !documentLists[mainwindow].contains( area ) ) { kDebug() << "got area change, storing its views"; storeAreaViewList( mainwindow, area ); } enableActions( mainwindow ); } void DocumentSwitcherPlugin::changeView( Sublime::View* view ) { if( !view ) return; Sublime::MainWindow* mainwindow = qobject_cast( sender() ); Q_ASSERT( mainwindow ); Sublime::Area* area = mainwindow->area(); int idx = documentLists[mainwindow][area].indexOf( view ); if( idx != -1 ) { documentLists[mainwindow][area].removeAt( idx ); } kDebug() << "moving view to front, list should now not contain this view anymore" << view << view->document()->title(); kDebug() << "current area is:" << area << area->title() << "mainwnidow:" << mainwindow << mainwindow->windowTitle();; kDebug() << "idx of this view in list:" << documentLists[mainwindow][area].indexOf( view ); documentLists[mainwindow][area].prepend( view ); enableActions(mainwindow); } void DocumentSwitcherPlugin::removeView( Sublime::View* view ) { if( !view ) return; Sublime::MainWindow* mainwindow = qobject_cast( sender() ); Q_ASSERT( mainwindow ); Sublime::Area* area = mainwindow->area(); int idx = documentLists[mainwindow][area].indexOf( view ); if( idx != -1 ) { documentLists[mainwindow][area].removeAt( idx ); } kDebug() << "removing view, list should now not contain this view anymore" << view << view->document()->title(); kDebug() << "current area is:" << area << area->title() << "mainwnidow:" << mainwindow << mainwindow->windowTitle();; kDebug() << "idx of this view in list:" << documentLists[mainwindow][area].indexOf( view ); enableActions(mainwindow); } #include "documentswitcherplugin.moc" diff --git a/plugins/projectmanagerview/projectmanagerview.cpp b/plugins/projectmanagerview/projectmanagerview.cpp index c426b2ccc..68f7ed556 100644 --- a/plugins/projectmanagerview/projectmanagerview.cpp +++ b/plugins/projectmanagerview/projectmanagerview.cpp @@ -1,218 +1,218 @@ /* This file is part of KDevelop Copyright 2005 Roberto Raggi Copyright 2007 Andreas Pakulat 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 "projectmanagerview.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "tests/modeltest.h" #include "projectproxymodel.h" #include "projectmanagerviewplugin.h" #include "proxyselectionmodel.h" #include "ui_projectmanagerview.h" using namespace KDevelop; ProjectManagerView::ProjectManagerView( ProjectManagerViewPlugin* plugin, QWidget *parent ) : QWidget( parent ), m_ui(new Ui::ProjectManagerView), m_plugin(plugin) { m_ui->setupUi( this ); setWindowTitle(i18n("Projects")); - setWindowIcon( SmallIcon( "kdevelop" ) ); //FIXME + setWindowIcon( SmallIcon( "project-development" ) ); setWhatsThis( i18n( "Project Manager" ) ); m_syncAction = new KAction(this); m_syncAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); m_syncAction->setText(i18n("Locate Current Document")); m_syncAction->setToolTip(i18n("Locates the current document in the project tree and selects it.")); m_syncAction->setIcon(KIcon("dirsync")); connect(m_syncAction, SIGNAL(triggered(bool)), this, SLOT(locateCurrentDocument())); addAction(m_syncAction); updateSyncAction(); addAction(plugin->actionCollection()->action("project_build")); addAction(plugin->actionCollection()->action("project_install")); addAction(plugin->actionCollection()->action("project_clean")); m_ui->projectTreeView->setWhatsThis( i18n( "Project Overview" ) ); QSizePolicy pol( QSizePolicy::Expanding, QSizePolicy::Expanding ); pol.setVerticalStretch( 6 ); m_ui->projectTreeView->setSizePolicy( pol ); connect(m_ui->projectTreeView, SIGNAL(activateUrl(const KUrl&)), this, SLOT(openUrl(const KUrl&))); // m_filters = new KLineEdit(this); // m_filters->setClearButtonShown(true); // connect(d->m_filters, SIGNAL(returnPressed()), this, SLOT(filtersChanged())); // vbox->addWidget( m_filters ); pol = QSizePolicy( QSizePolicy::Preferred, QSizePolicy::Preferred ); pol.setVerticalStretch( 2 ); m_ui->buildSetView->setProjectView( this ); m_ui->buildSetView->setSizePolicy( pol ); m_ui->buildSetView->setWhatsThis( i18n( "Build Items:" ) ); QStandardItemModel *overviewModel = ICore::self()->projectController()->projectModel(); m_modelFilter = new ProjectProxyModel( this ); m_modelFilter->setSourceModel(overviewModel); m_ui->projectTreeView->setModel( m_modelFilter ); m_ui->projectTreeView->setSelectionModel( new ProxySelectionModel( m_ui->projectTreeView, ICore::self()->projectController()->projectSelectionModel(), this ) ); connect( m_ui->projectTreeView->selectionModel(), SIGNAL(selectionChanged( const QItemSelection&, const QItemSelection&) ), this, SLOT(selectionChanged() ) ); connect( KDevelop::ICore::self()->documentController(), SIGNAL(documentClosed(KDevelop::IDocument*) ), SLOT(updateSyncAction())); connect( KDevelop::ICore::self()->documentController(), SIGNAL(documentOpened(KDevelop::IDocument*) ), SLOT(updateSyncAction())); connect( KDevelop::ICore::self()->projectController(), SIGNAL(projectAboutToBeOpened(KDevelop::IProject*)), SLOT(projectToBeOpened()) ); connect( KDevelop::ICore::self()->projectController(), SIGNAL(projectOpened(KDevelop::IProject*)), SLOT(projectOpened()) ); selectionChanged(); // Update the progress-state, but do so in a second so we // can be sure the button has been created QTimer::singleShot( 1000, this, SLOT(projectToBeOpened()) ); } void ProjectManagerView::projectOpened() { if( m_plugin->numProjectsBeingOpened() == 0 ) { emit hideProgressIndicator(); } } void ProjectManagerView::projectToBeOpened() { if( m_plugin->numProjectsBeingOpened() != 0 ) { emit showProgressIndicator(); } } void ProjectManagerView::selectionChanged() { m_ui->buildSetView->selectionChanged(); QList selected; foreach( const QModelIndex& idx, m_ui->projectTreeView->selectionModel()->selectedRows() ) { selected << m_modelFilter->itemFromProxyIndex( idx ); } KDevelop::ICore::self()->selectionController()->updateSelection( new ProjectItemContext( selected ) ); } void ProjectManagerView::updateSyncAction() { m_syncAction->setEnabled( KDevelop::ICore::self()->documentController()->activeDocument() ); } ProjectManagerView::~ProjectManagerView() { } QList ProjectManagerView::selectedItems() const { QList items; foreach( const QModelIndex &idx, m_ui->projectTreeView->selectionModel()->selectedIndexes() ) { KDevelop::ProjectBaseItem* item = ICore::self()->projectController()->projectModel()->item( m_modelFilter->mapToSource(idx) ); if( item ) items << item; else kDebug(9511) << "adding an unknown item"; } return items; } void ProjectManagerView::locateCurrentDocument() { KDevelop::IDocument *doc = ICore::self()->documentController()->activeDocument(); // We should _never_ get a null pointer for the document, as // the action is only enabled when there is an active document. Q_ASSERT(doc); foreach (IProject* proj, ICore::self()->projectController()->projects()) { foreach (KDevelop::ProjectFileItem* item, proj->filesForUrl(doc->url())) { QModelIndex index = m_modelFilter->proxyIndexFromItem(item); if (index.isValid()) { m_ui->projectTreeView->setCurrentIndex(index); m_ui->projectTreeView->expand(index); m_ui->projectTreeView->scrollTo(index); return; } } } } void ProjectManagerView::fileDirty( const QString &fileName ) { Q_UNUSED(fileName) } void ProjectManagerView::fileCreated( const QString &fileName ) { Q_UNUSED(fileName) } void ProjectManagerView::fileDeleted( const QString &fileName ) { Q_UNUSED(fileName) } void ProjectManagerView::openUrl( const KUrl& url ) { ICore::self()->documentController()->openDocument( url ); } #include "projectmanagerview.moc" diff --git a/plugins/projectmanagerview/projectmanagerviewplugin.cpp b/plugins/projectmanagerview/projectmanagerviewplugin.cpp index e7837aa25..50c0ff068 100644 --- a/plugins/projectmanagerview/projectmanagerviewplugin.cpp +++ b/plugins/projectmanagerview/projectmanagerviewplugin.cpp @@ -1,582 +1,585 @@ /* This file is part of KDevelop Copyright 2004 Roberto Raggi Copyright 2007 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. */ #include "projectmanagerviewplugin.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "projectmanagerview.h" #include "projectbuildsetmodel.h" #include "builditembuilderjob.h" using namespace KDevelop; K_PLUGIN_FACTORY(ProjectManagerFactory, registerPlugin(); ) K_EXPORT_PLUGIN(ProjectManagerFactory(KAboutData("kdevprojectmanagerview","kdevprojectmanagerview", ki18n("Project Management View"), "0.1", ki18n("Toolview to do all the project management stuff"), KAboutData::License_GPL))) class KDevProjectManagerViewFactory: public KDevelop::IToolViewFactory { public: KDevProjectManagerViewFactory( ProjectManagerViewPlugin *plugin ): mplugin( plugin ) {} virtual QWidget* create( QWidget *parent = 0 ) { return new ProjectManagerView( mplugin, parent ); } virtual Qt::DockWidgetArea defaultPosition() { return Qt::LeftDockWidgetArea; } virtual QString id() const { return "org.kdevelop.ProjectsView"; } virtual bool viewsWantProgressIndicator() const { return true; } private: ProjectManagerViewPlugin *mplugin; }; class ProjectManagerViewPluginPrivate { public: ProjectManagerViewPluginPrivate() {} KDevProjectManagerViewFactory *factory; QList ctxProjectItemList; KAction* m_buildAll; KAction* m_build; KAction* m_install; KAction* m_clean; KAction* m_configure; KAction* m_prune; ProjectBuildSetModel* buildSet; }; ProjectManagerViewPlugin::ProjectManagerViewPlugin( QObject *parent, const QVariantList& ) : IPlugin( ProjectManagerFactory::componentData(), parent ), d(new ProjectManagerViewPluginPrivate), projectsBeingOpened( 0 ) { d->buildSet = new ProjectBuildSetModel( this ); connect( core()->projectController(), SIGNAL( projectAboutToBeOpened( KDevelop::IProject* ) ), SLOT( projectToBeOpened() ) ); connect( core()->projectController(), SIGNAL( projectOpened( KDevelop::IProject* ) ), SLOT( projectOpened() ) ); connect( core()->projectController(), SIGNAL( projectOpened( KDevelop::IProject* ) ), d->buildSet, SLOT( loadFromProject( KDevelop::IProject* ) ) ); connect( core()->projectController(), SIGNAL( projectClosing( KDevelop::IProject* ) ), d->buildSet, SLOT( saveToProject( KDevelop::IProject* ) ) ); connect( core()->projectController(), SIGNAL( projectClosed( KDevelop::IProject* ) ), d->buildSet, SLOT( projectClosed( KDevelop::IProject* ) ) ); d->m_buildAll = new KAction( i18n("Build all Projects"), this ); d->m_buildAll->setIcon(KIcon("run-build")); connect( d->m_buildAll, SIGNAL(triggered()), this, SLOT(buildAllProjects()) ); actionCollection()->addAction( "project_buildall", d->m_buildAll ); d->m_build = new KAction( i18n("Build"), this ); d->m_build->setShortcut( Qt::Key_F8 ); d->m_build->setIcon(KIcon("run-build")); d->m_build->setEnabled( false ); connect( d->m_build, SIGNAL(triggered()), this, SLOT(buildProjectItems()) ); actionCollection()->addAction( "project_build", d->m_build ); d->m_install = new KAction( i18n("Install"), this ); + d->m_install->setIcon(KIcon("run-build-install")); d->m_install->setEnabled( false ); connect( d->m_install, SIGNAL(triggered()), this, SLOT(installProjectItems()) ); actionCollection()->addAction( "project_install", d->m_install ); d->m_clean = new KAction( i18n("Clean"), this ); + d->m_clean->setIcon(KIcon("run-build-clean")); d->m_clean->setEnabled( false ); connect( d->m_clean, SIGNAL(triggered()), this, SLOT(cleanProjectItems()) ); actionCollection()->addAction( "project_clean", d->m_clean ); d->m_configure = new KAction( i18n("Configure"), this ); - d->m_configure->setIcon(KIcon("configure")); + d->m_configure->setIcon(KIcon("run-build-configure")); d->m_configure->setEnabled( false ); connect( d->m_configure, SIGNAL(triggered()), this, SLOT(configureProjectItems()) ); actionCollection()->addAction( "project_configure", d->m_configure ); d->m_prune = new KAction( i18n("Prune"), this ); + d->m_prune->setIcon(KIcon("run-build-prune")); d->m_prune->setEnabled( false ); connect( d->m_prune, SIGNAL(triggered()), this, SLOT(pruneProjectItems()) ); actionCollection()->addAction( "project_prune", d->m_prune ); setXMLFile( "kdevprojectmanagerview.rc" ); d->factory = new KDevProjectManagerViewFactory( this ); core()->uiController()->addToolView( i18n("Projects"), d->factory ); connect( core()->selectionController(), SIGNAL(selectionChanged(KDevelop::Context*)), SLOT(updateActionState(KDevelop::Context*))); connect( d->buildSet, SIGNAL(rowsInserted(const QModelIndex&, int, int)), SLOT(updateFromBuildSetChange())); connect( d->buildSet, SIGNAL(rowsRemoved(const QModelIndex&, int, int)), SLOT(updateFromBuildSetChange())); connect( d->buildSet, SIGNAL(modelReset()), SLOT(updateFromBuildSetChange())); } void ProjectManagerViewPlugin::projectToBeOpened() { projectsBeingOpened++; } void ProjectManagerViewPlugin::projectOpened() { projectsBeingOpened--; } int ProjectManagerViewPlugin::numProjectsBeingOpened() { return projectsBeingOpened; } void ProjectManagerViewPlugin::updateFromBuildSetChange() { updateActionState( core()->selectionController()->currentSelection() ); } void ProjectManagerViewPlugin::updateActionState( KDevelop::Context* ctx ) { bool isEmpty = d->buildSet->items().isEmpty(); if( isEmpty ) { isEmpty = !ctx || ctx->type() != Context::ProjectItemContext || dynamic_cast( ctx )->items().isEmpty(); } d->m_build->setEnabled( !isEmpty ); d->m_install->setEnabled( !isEmpty ); d->m_clean->setEnabled( !isEmpty ); d->m_configure->setEnabled( !isEmpty ); d->m_prune->setEnabled( !isEmpty ); } ProjectManagerViewPlugin::~ProjectManagerViewPlugin() { delete d; } void ProjectManagerViewPlugin::unload() { kDebug() << "unloading manager view"; core()->uiController()->removeToolView(d->factory); } ContextMenuExtension ProjectManagerViewPlugin::contextMenuExtension( KDevelop::Context* context ) { d->ctxProjectItemList.clear(); if( context->type() != KDevelop::Context::ProjectItemContext ) return IPlugin::contextMenuExtension( context ); KDevelop::ProjectItemContext* ctx = dynamic_cast( context ); QList items = ctx->items(); if( items.isEmpty() ) return IPlugin::contextMenuExtension( context ); ContextMenuExtension menuExt; bool closeProjectsAdded = false; bool buildItemsAdded = false; //bool hasTargets = false; bool folderItemsAdded = false; bool fileItemsAdded = false; bool folderWithParentAdded = false; bool targetAdded = false; foreach( ProjectBaseItem* item, items ) { d->ctxProjectItemList << item; if ( !buildItemsAdded && ( item->target() || item->type() == ProjectBaseItem::BuildFolder ) ) { KAction* action = new KAction( i18n( "Build" ), this ); action->setIcon(KIcon("run-build")); connect( action, SIGNAL( triggered() ), this, SLOT(buildItemsFromContextMenu()) ); menuExt.addAction( ContextMenuExtension::BuildGroup, action ); action = new KAction( i18n( "Install" ), this ); action->setIcon(KIcon("run-install")); connect( action, SIGNAL( triggered() ), this, SLOT(installItemsFromContextMenu()) ); menuExt.addAction( ContextMenuExtension::BuildGroup, action ); action = new KAction( i18n( "Clean" ), this ); action->setIcon(KIcon("run-clean")); connect( action, SIGNAL( triggered() ), this, SLOT(cleanItemsFromContextMenu()) ); menuExt.addAction( ContextMenuExtension::BuildGroup, action ); action = new KAction( i18n( "Add to Buildset" ), this ); connect( action, SIGNAL(triggered() ), this, SLOT(addItemsFromContextMenuToBuildset() ) ); menuExt.addAction( ContextMenuExtension::BuildGroup, action ); buildItemsAdded = true; } KDevelop::ProjectFolderItem *prjitem = item->folder(); if ( !closeProjectsAdded && prjitem && prjitem->isProjectRoot() ) { KAction* close = new KAction( i18np( "Close Project", "Close Projects", items.count() ), this ); close->setIcon(KIcon("project-close")); connect( close, SIGNAL(triggered()), this, SLOT(closeProjects()) ); menuExt.addAction( ContextMenuExtension::ProjectGroup, close ); closeProjectsAdded = true; } if ( !folderItemsAdded && item->folder() ) { folderItemsAdded = true; KAction* action = new KAction( i18n( "Create File" ), this ); action->setIcon(KIcon("document-new")); connect( action, SIGNAL(triggered()), this, SLOT(createFileFromContextMenu()) ); menuExt.addAction( ContextMenuExtension::FileGroup, action ); action = new KAction( i18n( "Create Folder" ), this ); action->setIcon(KIcon("folder-new")); connect( action, SIGNAL(triggered()), this, SLOT(createFolderFromContextMenu()) ); menuExt.addAction( ContextMenuExtension::FileGroup, action ); action = new KAction( i18n( "Reload" ), this ); action->setIcon(KIcon("view-refresh")); connect( action, SIGNAL(triggered()), this, SLOT(reloadFromContextMenu()) ); menuExt.addAction( ContextMenuExtension::FileGroup, action ); } if ( !folderWithParentAdded && item->folder() && item->parent() ) { folderWithParentAdded = true; KAction* action = new KAction( i18n( "Remove Folder" ), this ); action->setIcon(KIcon("user-trash")); connect( action, SIGNAL(triggered()), this, SLOT(removeFolderFromContextMenu()) ); menuExt.addAction( ContextMenuExtension::FileGroup, action ); } if ( !fileItemsAdded && item->file() ) { fileItemsAdded = true; KAction* action = new KAction( i18n( "Remove File" ), this ); action->setIcon(KIcon("user-trash")); connect( action, SIGNAL(triggered()), this, SLOT(removeFileFromContextMenu()) ); menuExt.addAction( ContextMenuExtension::FileGroup, action ); } else if ( !targetAdded && item->target() ) { targetAdded = true; KAction* action = new KAction( i18n( "Create File" ), this ); action->setIcon(KIcon("document-new")); connect( action, SIGNAL(triggered()), this, SLOT(createFileInTargetFromContextMenu()) ); menuExt.addAction( ContextMenuExtension::FileGroup, action ); } //TODO: Port to launch framework // if(!hasTargets && item->executable()) // { // KAction* action = new KAction( i18n("Run"), this ); // action->setIcon(KIcon("system-run")); // connect( action, SIGNAL( triggered() ), this, SLOT( runTargetsFromContextMenu() ) ); // menuExt.addAction( ContextMenuExtension::ProjectGroup, action ); // } } return menuExt; } KDevelop::IProjectBuilder* ProjectManagerViewPlugin::getProjectBuilder( KDevelop::ProjectBaseItem* item ) { if( !item ) return 0; IProject* project = item->project(); if (!project) return 0; ProjectFolderItem* prjitem = project->projectItem(); IPlugin* fmgr = project->managerPlugin(); IBuildSystemManager* mgr = fmgr->extension(); if( mgr ) { return mgr->builder( prjitem ); } return 0; } void ProjectManagerViewPlugin::closeProjects() { QList projectsToClose; foreach( KDevelop::ProjectBaseItem* item, d->ctxProjectItemList ) { if( !projectsToClose.contains( item->project() ) ) { projectsToClose << item->project(); } } d->ctxProjectItemList.clear(); foreach( KDevelop::IProject* proj, projectsToClose ) { core()->projectController()->closeProject( proj ); } } void ProjectManagerViewPlugin::installItemsFromContextMenu() { ICore::self()->runController()->registerJob( new BuildItemBuilderJob( KDevelop::BuilderJob::Install, d->ctxProjectItemList ) ); d->ctxProjectItemList.clear(); } void ProjectManagerViewPlugin::cleanItemsFromContextMenu() { ICore::self()->runController()->registerJob( new BuildItemBuilderJob( KDevelop::BuilderJob::Clean, d->ctxProjectItemList ) ); d->ctxProjectItemList.clear(); } void ProjectManagerViewPlugin::buildItemsFromContextMenu() { ICore::self()->runController()->registerJob( new BuildItemBuilderJob( KDevelop::BuilderJob::Build, d->ctxProjectItemList ) ); d->ctxProjectItemList.clear(); } void ProjectManagerViewPlugin::buildAllProjects() { QList items; foreach( KDevelop::IProject* project, core()->projectController()->projects() ) { items << project->projectItem(); } ICore::self()->runController()->registerJob( new BuildItemBuilderJob( KDevelop::BuilderJob::Build, items ) ); } void ProjectManagerViewPlugin::runBuilderJob( KDevelop::BuilderJob::BuildType t ) { QList items; if( !d->buildSet->items().isEmpty() ) { ICore::self()->runController()->registerJob( new BuildItemBuilderJob( t, d->buildSet->items() ) ); } else { KDevelop::ProjectItemContext* ctx = dynamic_cast(ICore::self()->selectionController()->currentSelection()); ICore::self()->runController()->registerJob( new BuildItemBuilderJob( t, ctx->items() ) ); } } void ProjectManagerViewPlugin::installProjectItems() { runBuilderJob( KDevelop::BuilderJob::Install ); } void ProjectManagerViewPlugin::pruneProjectItems() { runBuilderJob( KDevelop::BuilderJob::Prune ); } void ProjectManagerViewPlugin::configureProjectItems() { runBuilderJob( KDevelop::BuilderJob::Configure ); } void ProjectManagerViewPlugin::cleanProjectItems() { runBuilderJob( KDevelop::BuilderJob::Clean ); } void ProjectManagerViewPlugin::buildProjectItems() { runBuilderJob( KDevelop::BuilderJob::Build ); } ProjectBuildSetModel* ProjectManagerViewPlugin::buildSet() { return d->buildSet; } void ProjectManagerViewPlugin::addItemsFromContextMenuToBuildset( ) { foreach( KDevelop::ProjectBaseItem* item, d->ctxProjectItemList ) { buildSet()->addProjectItem( item ); } } void ProjectManagerViewPlugin::runTargetsFromContextMenu( ) { foreach( KDevelop::ProjectBaseItem* item, d->ctxProjectItemList ) { KDevelop::ProjectExecutableTargetItem* t=item->executable(); if(t) { kDebug() << "Running target: " << t->text() << t->builtUrl(); } } } void ProjectManagerViewPlugin::projectConfiguration( ) { if( !d->ctxProjectItemList.isEmpty() ) { core()->projectController()->configureProject( d->ctxProjectItemList.at( 0 )->project() ); } } void ProjectManagerViewPlugin::reloadFromContextMenu( ) { foreach( KDevelop::ProjectBaseItem* item, d->ctxProjectItemList ) { item->project()->projectFileManager()->reload(item); } } void ProjectManagerViewPlugin::createFolderFromContextMenu( ) { foreach( KDevelop::ProjectBaseItem* item, d->ctxProjectItemList ) { if ( item->folder() ) { QWidget* window(ICore::self()->uiController()->activeMainWindow()->window()); QString name = QInputDialog::getText ( window, i18n ( "Create Folder" ), i18n ( "Folder Name" ) ); if (!name.isEmpty()) { KUrl url = item->folder()->url(); url.addPath( name ); if ( !KIO::NetAccess::mkdir( url, window ) ) { KMessageBox::error( window, i18n( "Cannot create folder." ) ); continue; } item->project()->projectFileManager()->addFolder( url, item->folder() ); } } } } void ProjectManagerViewPlugin::removeFolderFromContextMenu() { foreach( KDevelop::ProjectBaseItem* item, d->ctxProjectItemList ) { if ( item->folder() ) { QWidget* window(ICore::self()->uiController()->activeMainWindow()->window()); int q=KMessageBox::questionYesNo(window, i18n("Do you want to remove the directory from the filesystem too?")); if(q==KMessageBox::Yes) { if ( !KIO::NetAccess::del( item->folder()->url(), window ) ) { KMessageBox::error( window, i18n( "Cannot remove folder." ) ); continue; } } item->project()->projectFileManager()->removeFolder(item->folder()); } } } void ProjectManagerViewPlugin::removeFileFromContextMenu() { foreach( KDevelop::ProjectBaseItem* item, d->ctxProjectItemList ) { if ( item->file() ) { QWidget* window(ICore::self()->uiController()->activeMainWindow()->window()); int q=KMessageBox::questionYesNo(window, i18n("Do you want to remove the file from the filesystem too?")); if(q==KMessageBox::Yes) { if ( !KIO::NetAccess::del( item->file()->url(), window ) ) { KMessageBox::error( window, i18n( "Cannot remove the file." ) ); continue; } } item->project()->projectFileManager()->removeFile(item->file()); } } } ProjectFileItem* createFile(const ProjectFolderItem* item) { QWidget* window = ICore::self()->uiController()->activeMainWindow()->window(); QString name = QInputDialog::getText(window, i18n("Create File"), i18n("File Name")); if(name.isEmpty()) return 0; KUrl url=item->url(); url.addPath( name ); if (KIO::NetAccess::exists( url, KIO::NetAccess::SourceSide, window )) { KMessageBox::error( window, i18n( "This file exists already." ) ); return 0; } { KTemporaryFile temp; if ( !temp.open() || temp.write("\n") == -1 ) { KMessageBox::error( window, i18n( "Cannot create temporary file." ) ); return 0; } if ( !KIO::NetAccess::upload( temp.fileName(), url, window ) ) { KMessageBox::error( window, i18n( "Cannot create file." ) ); return 0; } } ProjectFileItem* ret=item->project()->projectFileManager()->addFile( url, item->folder() ); ICore::self()->documentController()->openDocument( url ); return ret; } void ProjectManagerViewPlugin::createFileFromContextMenu( ) { foreach( KDevelop::ProjectBaseItem* item, d->ctxProjectItemList ) { if ( item->folder() ) { createFile(item->folder()); } } } void ProjectManagerViewPlugin::createFileInTargetFromContextMenu( ) { foreach( KDevelop::ProjectBaseItem* item, d->ctxProjectItemList ) { if ( item->target() ) { ProjectFolderItem* folder=dynamic_cast(item->parent()); if(folder) { ProjectFileItem* f=createFile(folder); if(f) item->project()->buildSystemManager()->addFileToTarget(f, item->target()); } } } } #include "projectmanagerviewplugin.moc" diff --git a/shell/documentationview.cpp b/shell/documentationview.cpp index a559ab0cf..80c3810c6 100644 --- a/shell/documentationview.cpp +++ b/shell/documentationview.cpp @@ -1,78 +1,79 @@ /* Copyright 2009 Aleix Pol Gonzalez 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 "documentationview.h" #include #include #include #include #include #include DocumentationView::DocumentationView(QWidget* parent) : QWidget(parent) { + setWindowIcon(KIcon("documentation")); setLayout(new QVBoxLayout(this)); mActions=new KToolBar(this); layout()->addWidget(mActions); mBack=mActions->addAction(KIcon("go-previous"), i18n("Back")); mForward=mActions->addAction(KIcon("go-next"), i18n("Forward")); mBack->setEnabled(false); mForward->setEnabled(false); connect(mBack, SIGNAL(triggered()), this, SLOT(browseBack())); connect(mForward, SIGNAL(triggered()), this, SLOT(browseForward())); mCurrent=mHistory.begin(); layout()->addWidget(new QLabel(i18n("There is no documentation selected yet"), this)); } void DocumentationView::browseBack() { mCurrent--; mBack->setEnabled(mCurrent!=mHistory.begin()); mForward->setEnabled(true); replaceView((*mCurrent)->documentationWidget()); } void DocumentationView::browseForward() { mCurrent++; mForward->setEnabled(mCurrent+1!=mHistory.end()); mBack->setEnabled(true); replaceView((*mCurrent)->documentationWidget()); } void DocumentationView::replaceView(QWidget* newView) { delete layout()->takeAt(1); layout()->addWidget(newView); } void DocumentationView::showDocumentation(KSharedPtr< KDevelop::IDocumentation > doc) { kDebug(9529) << "showing" << doc; replaceView(doc->documentationWidget(this)); mBack->setEnabled( !mHistory.isEmpty() ); mForward->setEnabled(false); mHistory.append(doc); mCurrent=mHistory.end()-1; } diff --git a/shell/projectcontroller.cpp b/shell/projectcontroller.cpp index 77c9e8a6b..27c9f1b32 100644 --- a/shell/projectcontroller.cpp +++ b/shell/projectcontroller.cpp @@ -1,818 +1,819 @@ /* This file is part of KDevelop Copyright 2006 Adam Treat Copyright 2007 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. */ #include "projectcontroller.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "core.h" #include "project.h" #include "mainwindow.h" #include "shellextension.h" #include "plugincontroller.h" #include "uicontroller.h" #include "documentcontroller.h" #include "openprojectdialog.h" #include #include #include namespace KDevelop { bool reopenProjectsOnStartup() { KConfigGroup group = Core::self()->activeSession()->config()->group( "Project Manager" ); return group.readEntry( "Reopen Projects On Startup", false ); } bool parseAllProjectSources() { KConfigGroup group = Core::self()->activeSession()->config()->group( "Project Manager" ); return group.readEntry( "Parse All Project Sources", true ); } class ProjectControllerPrivate { public: QList m_projects; QMap< IProject*, QList > m_projectPlugins; QPointer m_recentAction; Core* m_core; // IProject* m_currentProject; ProjectModel* model; QItemSelectionModel* selectionModel; QMap > m_cfgDlgs; QPointer m_closeAllProjects; QPointer m_closeProject; QPointer m_openConfig; IProjectDialogProvider* dialog; QList m_currentlyOpening; // project-file urls that are being opened IProject* m_configuringProject; ProjectController* q; bool m_foundProjectFile; //Temporary flag used while searching the hierarchy for a project file ProjectControllerPrivate( ProjectController* p ) : m_core(0), model(0), selectionModel(0), dialog(0), m_configuringProject(0), q(p), m_foundProjectFile(false) { } void unloadAllProjectPlugins() { if( m_projects.isEmpty() ) m_core->pluginControllerInternal()->unloadProjectPlugins(); } void projectConfig( QObject * obj ) { if( !obj ) return; Project* proj = qobject_cast(obj); if( !proj ) return; if( !m_cfgDlgs.contains( proj ) ) { //@FIXME: compute a blacklist, based on a query for all KDevelop //plugins implementing IProjectManager, removing from that the //plugin that manages this project. Set this as blacklist on the //dialog //@FIXME: Currently it is important to set a parentApp on the kcms //that's different from the component name of the application, else //the plugin will show up on all projects settings dialogs. QStringList pluginsForPrj = findPluginsForProject( proj ); kDebug() << "Using pluginlist:" << pluginsForPrj; pluginsForPrj << "kdevplatformproject"; // for project-wide env settings. m_cfgDlgs[proj] = new KSettings::Dialog( pluginsForPrj, m_core->uiController()->activeMainWindow() ); m_cfgDlgs[proj]->setKCMArguments( QStringList() << proj->developerTempFile() << proj->projectTempFile() << proj->projectFileUrl().url() << proj->developerFileUrl().url() ); } m_configuringProject = proj; m_cfgDlgs[proj]->exec(); proj->projectConfiguration()->sync(); m_configuringProject = 0; } void saveListOfOpenedProjects() { KSharedConfig::Ptr config = Core::self()->activeSession()->config(); KConfigGroup group = config->group( "General Options" ); KUrl::List openProjects; foreach( IProject* project, m_projects ) { openProjects.append(project->projectFileUrl()); } group.writeEntry( "Open Projects", openProjects.toStringList() ); group.sync(); } QStringList findPluginsForProject( IProject* project ) { QList plugins = m_core->pluginController()->loadedPlugins(); QStringList pluginnames; for( QList::iterator it = plugins.begin(); it != plugins.end(); ++it ) { IPlugin* plugin = *it; IProjectFileManager* iface = plugin->extension(); if( !iface || plugin == project->managerPlugin() ) pluginnames << m_core->pluginController()->pluginInfo( plugin ).pluginName(); } return pluginnames; } void notifyProjectConfigurationChanged() { if( m_configuringProject ) { emit q->projectConfigurationChanged( m_configuringProject ); } } void updateActionStates( Context* ctx ) { ProjectItemContext* itemctx = dynamic_cast(ctx); m_openConfig->setEnabled( itemctx && itemctx->items().count() == 1 ); m_closeProject->setEnabled( itemctx && itemctx->items().count() > 0 ); } void openProjectConfig() { ProjectItemContext* ctx = dynamic_cast( Core::self()->selectionController()->currentSelection() ); if( ctx && ctx->items().count() == 1 ) { q->configureProject( ctx->items().at(0)->project() ); } } void closeSelectedProjects() { ProjectItemContext* ctx = dynamic_cast( Core::self()->selectionController()->currentSelection() ); if( ctx && ctx->items().count() > 0 ) { QSet projects; foreach( ProjectBaseItem* item, ctx->items() ) { projects.insert( item->project() ); } foreach( IProject* project, projects ) { q->closeProject( project ); } } } }; IProjectDialogProvider::IProjectDialogProvider() {} IProjectDialogProvider::~IProjectDialogProvider() {} ProjectDialogProvider::ProjectDialogProvider(ProjectControllerPrivate* const p) : d(p) {} ProjectDialogProvider::~ProjectDialogProvider() {} bool writeNewProjectFile( KSharedConfig::Ptr cfg, const QString& name, const QString& manager ) { if (!cfg->isConfigWritable(true)) { kDebug() << "can't write to configfile"; return false; } KConfigGroup grp = cfg->group( "Project" ); grp.writeEntry( "Name", name ); grp.writeEntry( "Manager", manager ); cfg->sync(); return true; } bool projectFileExists( const KUrl& u ) { if( u.isLocalFile() ) { return QFileInfo( u.toLocalFile() ).exists(); } else { return KIO::NetAccess::exists( u, KIO::NetAccess::DestinationSide, Core::self()->uiControllerInternal()->activeMainWindow() ); } } KUrl ProjectDialogProvider::askProjectConfigLocation(const KUrl& startUrl) { Q_ASSERT(d); OpenProjectDialog dlg( startUrl, Core::self()->uiController()->activeMainWindow() ); if(dlg.exec() == QDialog::Rejected) return KUrl(); KUrl projectFileUrl = dlg.projectFileUrl(); kDebug() << "selected project:" << projectFileUrl << dlg.projectName() << dlg.projectManager(); if( !projectFileExists( projectFileUrl ) ) { if( projectFileUrl.isLocalFile() ) { bool ok = writeNewProjectFile( KSharedConfig::openConfig( projectFileUrl.toLocalFile(), KConfig::SimpleConfig ), dlg.projectName(), dlg.projectManager() ); if (!ok) return KUrl(); } else { KTemporaryFile tmp; ///TODO: do we really want to set setAutoRemove to false?? tmp.setAutoRemove( false ); tmp.open(); bool ok = writeNewProjectFile( KSharedConfig::openConfig( tmp.fileName(), KConfig::SimpleConfig ), dlg.projectName(), dlg.projectManager() ); if (!ok) return KUrl(); ok = KIO::NetAccess::upload( tmp.fileName(), projectFileUrl, Core::self()->uiControllerInternal()->defaultMainWindow() ); if (!ok) { KMessageBox::error(d->m_core->uiControllerInternal()->defaultMainWindow(), i18n("Unable to create configuration file %1", projectFileUrl.url())); return KUrl(); } } } return projectFileUrl; } bool ProjectDialogProvider::userWantsReopen() { Q_ASSERT(d); return (KMessageBox::questionYesNo( d->m_core->uiControllerInternal()->defaultMainWindow(), i18n( "Reopen the current project?" ) ) == KMessageBox::No) ? false : true; } void ProjectController::setDialogProvider(IProjectDialogProvider* dialog) { Q_ASSERT(d->dialog); delete d->dialog; d->dialog = dialog; } ProjectController::ProjectController( Core* core ) : IProjectController( core ), d( new ProjectControllerPrivate( this ) ) { setObjectName("ProjectController"); d->m_core = core; d->model = new ProjectModel(); d->selectionModel = new QItemSelectionModel(d->model); if(!(Core::self()->setupFlags() & Core::NoUi)) setupActions(); loadSettings(false); d->dialog = new ProjectDialogProvider(d); KSettings::Dispatcher::registerComponent( KComponentData("kdevplatformproject"), this, "notifyProjectConfigurationChanged" ); } void ProjectController::setupActions() { KActionCollection * ac = d->m_core->uiControllerInternal()->defaultMainWindow()->actionCollection(); KAction *action; action = ac->addAction( "project_open" ); action->setText(i18n( "Open / Import Project..." ) ); connect( action, SIGNAL( triggered( bool ) ), SLOT( openProject() ) ); action->setToolTip( i18n( "Open / Import Oroject" ) ); action->setWhatsThis( i18n( "Open / Import project

Open an existing KDevelop 4 project or import an existing Project into KDevelop 4. This entry allows to select a KDevelop4 project file or an existing directory to open it in KDevelop. When opening an existing directory that doesn't yet have a KDevelop4 project file the file will be created.

" ) ); action->setIcon(KIcon("project-open")); // action = ac->addAction( "project_close" ); // action->setText( i18n( "C&lose Project" ) ); // connect( action, SIGNAL( triggered( bool ) ), SLOT( closeProject() ) ); // action->setToolTip( i18n( "Close project" ) ); // action->setWhatsThis( i18n( "Close project

Closes the current project." ) ); // action->setEnabled( false ); d->m_closeAllProjects = action = ac->addAction( "project_close_all" ); action->setText( i18n( "Close All Projects" ) ); connect( action, SIGNAL( triggered( bool ) ), SLOT( closeAllProjects() ) ); action->setToolTip( i18n( "Close all currently open projects" ) ); action->setWhatsThis( i18n( "Close all projects

Closes all of the currently open projects.

" ) ); action->setEnabled( false ); action->setIcon(KIcon("project-development-close-all")); d->m_closeProject = action = ac->addAction( "project_close" ); connect( action, SIGNAL( triggered( bool ) ), SLOT( closeSelectedProjects() ) ); action->setText( i18n( "Close Project(s)" ) ); action->setIcon( KIcon( "project-development-close" ) ); action->setToolTip( i18n( "Closes all currently selected projects" ) ); action->setEnabled( false ); d->m_openConfig = action = ac->addAction( "project_open_config" ); connect( action, SIGNAL( triggered( bool ) ), SLOT( openProjectConfig() ) ); action->setText( i18n( "Open Configuration..." ) ); + action->setIcon( KIcon("configure") ); action->setEnabled( false ); KSharedConfig * config = KGlobal::config().data(); // KConfigGroup group = config->group( "General Options" ); d->m_recentAction = new KRecentFilesAction( this ); connect( d->m_recentAction, SIGNAL(urlSelected(const KUrl&)), SLOT( openProject( const KUrl& ) )); ac->addAction( "project_open_recent", d->m_recentAction ); d->m_recentAction->setText( i18n( "Open Recent" ) ); d->m_recentAction->setToolTip( i18n( "Open recent project" ) ); d->m_recentAction->setWhatsThis( i18n( "Open recent project

Opens recently opened project.

" ) ); d->m_recentAction->loadEntries( KConfigGroup(config, "RecentProjects") ); KAction* openProjectForFileAction = new KAction( this ); ac->addAction("project_open_for_file", openProjectForFileAction); openProjectForFileAction->setText(i18n("Open Project for Current File")); connect( openProjectForFileAction, SIGNAL(triggered(bool)), SLOT(openProjectForUrlSlot(bool))); } ProjectController::~ProjectController() { delete d->model; delete d->dialog; delete d; } void ProjectController::cleanup() { KSharedConfig::Ptr config = Core::self()->activeSession()->config(); KConfigGroup group = config->group( "General Options" ); KUrl::List openProjects; foreach( IProject* project, d->m_projects ) { openProjects.append(project->projectFileUrl()); closeProject( project ); } group.writeEntry( "Open Projects", openProjects.toStringList() ); } void ProjectController::initialize() { if (reopenProjectsOnStartup()) { KSharedConfig::Ptr config = Core::self()->activeSession()->config(); KConfigGroup group = config->group( "General Options" ); KUrl::List openProjects = group.readEntry( "Open Projects", QStringList() ); foreach (const KUrl& url, openProjects) openProject(url); } connect( Core::self()->selectionController(), SIGNAL(selectionChanged(KDevelop::Context*)), SLOT(updateActionStates(KDevelop::Context*)) ); } void ProjectController::loadSettings( bool projectIsLoaded ) { Q_UNUSED(projectIsLoaded) } void ProjectController::saveSettings( bool projectIsLoaded ) { Q_UNUSED( projectIsLoaded ); } int ProjectController::projectCount() const { return d->m_projects.count(); } IProject* ProjectController::projectAt( int num ) const { if( !d->m_projects.isEmpty() && num >= 0 && num < d->m_projects.count() ) return d->m_projects.at( num ); return 0; } QList ProjectController::projects() const { return d->m_projects; } void ProjectController::eventuallyOpenProjectFile(KIO::Job* _job, KIO::UDSEntryList entries ) { KIO::SimpleJob* job(dynamic_cast(_job)); Q_ASSERT(job); foreach(KIO::UDSEntry entry, entries) { if(d->m_foundProjectFile) break; if(!entry.isDir()) { QString name = entry.stringValue( KIO::UDSEntry::UDS_NAME ); if(name.endsWith(".kdev4")) { //We have found a project-file, open it KUrl u(job->url()); u.addPath(name); openProject(u); d->m_foundProjectFile = true; } } } } void ProjectController::openProjectForUrlSlot(bool) { if(ICore::self()->documentController()->activeDocument()) { KUrl url = ICore::self()->documentController()->activeDocument()->url(); IProject* project = ICore::self()->projectController()->findProjectForUrl(url); if(!project) { openProjectForUrl(url); }else{ KMessageBox::error(Core::self()->uiController()->activeMainWindow(), i18n("Project already open: %1", project->name())); } }else{ KMessageBox::error(Core::self()->uiController()->activeMainWindow(), i18n("No active document")); } } void ProjectController::openProjectForUrl(const KUrl& sourceUrl) { KUrl dirUrl = sourceUrl.upUrl(); KUrl testAt = dirUrl; d->m_foundProjectFile = false; while(!testAt.path().isEmpty()) { KUrl testProjectFile(testAt); KIO::ListJob* job = KIO::listDir(testAt); connect(job, SIGNAL(entries(KIO::Job*,KIO::UDSEntryList)), SLOT(eventuallyOpenProjectFile(KIO::Job*,KIO::UDSEntryList))); KIO::NetAccess::synchronousRun(job, ICore::self()->uiController()->activeMainWindow()); if(d->m_foundProjectFile) { //Fine! We have directly opened the project d->m_foundProjectFile = false; return; } KUrl oldTest = testAt; testAt = testAt.upUrl(); if(oldTest == testAt) break; } KUrl askForOpen = d->dialog->askProjectConfigLocation(dirUrl); if(askForOpen.isValid()) openProject(askForOpen); } void ProjectController::openProject( const KUrl &projectFile ) { KUrl url = projectFile; if ( url.isEmpty() ) { url = d->dialog->askProjectConfigLocation(); if ( url.isEmpty() ) return; } if ( !url.isValid() ) { KMessageBox::error(Core::self()->uiControllerInternal()->activeMainWindow(), i18n("Invalid Location: %1", url.prettyUrl())); return; } if ( d->m_currentlyOpening.contains(url)) { kDebug() << "Already opening " << url << ". Aborting."; KPassivePopup::message( i18n( "Project already being opened"), i18n( "Already opening %1, not opening again", url.prettyUrl() ), Core::self()->uiController()->activeMainWindow() ); return; } foreach( IProject* project, d->m_projects ) { if ( url == project->projectFileUrl() ) { if ( d->dialog->userWantsReopen() ) { // close first, then open again by falling through closeProject(project); } else { // abort return; } } } //FIXME Create the hidden directory if it doesn't exist if ( loadProjectPart() ) { //The project file has been opened. //Now we can load settings for all of the Core objects including this one!! // Core::loadSettings(); d->m_core->pluginControllerInternal()->loadProjectPlugins(); } else { KMessageBox::error(Core::self()->uiControllerInternal()->activeMainWindow(), i18n("Cannot Load Projects View plugin, aborting.")); return; } Project* project = new Project(); emit projectAboutToBeOpened( project ); if ( !project->open( url ) ) { KMessageBox::error(Core::self()->uiControllerInternal()->activeMainWindow(), i18n( "Project could not be opened: %1", url.prettyUrl() )); delete project; return; } d->m_currentlyOpening << url; d->m_closeAllProjects->setEnabled(true); return; } void ProjectController::projectImportingFinished( IProject* project ) { if( !project ) { kWarning() << "OOOPS: 0-pointer project"; return; } IPlugin *managerPlugin = project->managerPlugin(); QList pluglist; pluglist.append( managerPlugin ); d->m_projectPlugins.insert( project, pluglist ); d->m_projects.append( project ); d->saveListOfOpenedProjects(); // KActionCollection * ac = d->m_core->uiController()->defaultMainWindow()->actionCollection(); // QAction * action; //action = ac->action( "project_close" ); //action->setEnabled( true ); d->m_recentAction->addUrl( project->projectFileUrl() ); KSharedConfig * config = KGlobal::config().data(); KConfigGroup recentGroup = config->group("RecentProjects"); d->m_recentAction->saveEntries( recentGroup ); config->sync(); d->m_currentlyOpening.removeAll(project->projectFileUrl()); emit projectOpened( project ); if (parseAllProjectSources()) { KJob* parseProjectJob = new KDevelop::ParseProjectJob(project); ICore::self()->runController()->registerJob(parseProjectJob); } KUrl::List parseList; // Add all currently open files that belong to the project to the background-parser, // since more information may be available for parsing them now(Like include-paths). foreach(IDocument* document, Core::self()->documentController()->openDocuments()) { if(!project->filesForUrl(document->url()).isEmpty()) { parseList.append(document->url()); } } Core::self()->languageController()->backgroundParser()->addDocumentList( parseList, KDevelop::TopDUContext::AllDeclarationsContextsAndUses, 10 ); } // helper method for closeProject() void ProjectController::unloadUnusedProjectPlugins(IProject* proj) { QList pluginsForProj = d->m_projectPlugins.value( proj ); d->m_projectPlugins.remove( proj ); QList otherProjectPlugins; Q_FOREACH( QList _list, d->m_projectPlugins ) { otherProjectPlugins << _list; } QSet pluginsForProjSet = QSet::fromList( pluginsForProj ); QSet otherPrjPluginsSet = QSet::fromList( otherProjectPlugins ); // loaded - target = tobe unloaded. QSet tobeRemoved = pluginsForProjSet.subtract( otherPrjPluginsSet ); Q_FOREACH( IPlugin* _plugin, tobeRemoved ) { KPluginInfo _plugInfo = Core::self()->pluginController()->pluginInfo( _plugin ); if( _plugInfo.isValid() ) { QString _plugName = _plugInfo.pluginName(); kDebug() << "about to unloading :" << _plugName; Core::self()->pluginController()->unloadPlugin( _plugName ); } } } // helper method for closeProject() void ProjectController::closeAllOpenedFiles(IProject* proj) { Q_FOREACH( ProjectFileItem *fileItem, proj->files() ) { Core::self()->documentControllerInternal()->closeDocument( fileItem->url() ); } } // helper method for closeProject() void ProjectController::initializePluginCleanup(IProject* proj) { // Unloading (and thus deleting) these plugins is not a good idea just yet // as we're being called by the view part and it gets deleted when we unload the plugin(s) // TODO: find a better place to unload connect(proj, SIGNAL(destroyed(QObject*)), this, SLOT(unloadAllProjectPlugins())); if (d->m_closeAllProjects) { d->m_closeAllProjects->setEnabled(false); } } void ProjectController::closeProject(IProject* proj) { if(!proj || d->m_projects.indexOf(proj) == -1) { return; } d->m_projects.removeAll(proj); emit projectClosing(proj); //Core::self()->saveSettings(); // The project file is being closed. // Now we can save settings for all of the Core // objects including this one!! unloadUnusedProjectPlugins(proj); closeAllOpenedFiles(proj); proj->close(); proj->deleteLater(); //be safe when deleting if (d->m_projects.isEmpty()) { initializePluginCleanup(proj); } d->saveListOfOpenedProjects(); emit projectClosed(proj); return; } bool ProjectController::loadProjectPart() { return true; } ProjectModel* ProjectController::projectModel() { return d->model; } IProject* ProjectController::findProjectForUrl( const KUrl& url ) const { Q_FOREACH( IProject* proj, d->m_projects ) { if( proj->inProject( url ) ) return proj; } return 0; } IProject* ProjectController::findProjectByName( const QString& name ) { Q_FOREACH( IProject* proj, d->m_projects ) { if( proj->name() == name ) { return proj; } } return 0; } void ProjectController::configureProject( IProject* project ) { d->projectConfig( project ); } void ProjectController::addProject(IProject* project) { d->m_projects.append( project ); } void ProjectController::closeAllProjects() { foreach (IProject* project, projects()) { closeProject(project); } } QItemSelectionModel* ProjectController::projectSelectionModel() { return d->selectionModel; } bool ProjectController::isProjectNameUsed( const QString& name ) const { foreach( IProject* p, projects() ) { if( p->name() == name ) { return true; } } return false; } KUrl ProjectController::projectsBaseDirectory() const { KConfigGroup group = Core::self()->activeSession()->config()->group( "Project Manager" ); return group.readEntry( "Projects Base Directory", KUrl( QDir::homePath()+"/projects" ) ); } QString ProjectController::prettyFileName(KUrl url, FormattingOptions format) const { IProject* project = Core::self()->projectController()->findProjectForUrl(url); QString prefixText = url.upUrl().pathOrUrl(KUrl::AddTrailingSlash); if (project) { if (format == FormatHtml) { prefixText = "" + project->name() + "/"; } else { prefixText = project->name() + '/'; } prefixText += project->relativeUrl(url.upUrl()).path(KUrl::AddTrailingSlash); } if (format == FormatHtml) { return prefixText + "" + url.fileName() + ""; } else { return prefixText + url.fileName(); } } } #include "projectcontroller.moc"