Fix no newlines warnings. Patch by Peter Oberndorfer
[kdevelopdvcssupport.git] / shell / projectcontroller.cpp
blob837cd4fb183c29696ea81ad5f454026300234895
1 /* This file is part of KDevelop
2 Copyright 2006 Adam Treat <treat@kde.org>
3 Copyright 2007 Anreas Pakulat <apaku@gmx.de>
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
15 You should have received a copy of the GNU Library General Public License
16 along with this library; see the file COPYING.LIB. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
21 #include "projectcontroller.h"
23 #include <QtCore/QDir>
24 #include <QtCore/QSignalMapper>
25 #include <QtGui/QAction>
26 #include <QSet>
27 #include <QList>
28 #include <QMap>
29 #include <QItemSelectionModel>
31 #include <kaction.h>
32 #include <kconfig.h>
33 #include <klocale.h>
34 #include <kservice.h>
35 #include <kstandardaction.h>
36 #include <kmessagebox.h>
37 #include <kxmlguiwindow.h>
38 #include <kfiledialog.h>
39 #include <kactioncollection.h>
40 #include <kservicetypetrader.h>
41 #include <krecentfilesaction.h>
42 #include <kactionmenu.h>
43 #include <ksettings/dialog.h>
44 #include <kstandarddirs.h>
46 #include "sublime/area.h"
47 #include <interfaces/iplugin.h>
48 #include <project/interfaces/iprojectfilemanager.h>
49 #include <project/projectmodel.h>
50 #include <interfaces/ilanguagecontroller.h>
51 #include <language/backgroundparser/backgroundparser.h>
53 #include "core.h"
54 #include "project.h"
55 #include "mainwindow.h"
56 #include "shellextension.h"
57 #include "plugincontroller.h"
58 #include "uicontroller.h"
59 #include "documentcontroller.h"
61 namespace KDevelop
64 class ProjectControllerPrivate
66 public:
67 QList<IProject*> m_projects;
68 QMap< IProject*, QList<IPlugin*> > m_projectPlugins;
69 QMap< IProject*, QPointer<QAction> > m_configActions;
70 QPointer<KRecentFilesAction> m_recentAction;
71 KActionMenu *m_projectConfigAction;
72 QSignalMapper *m_signalMapper;
73 Core* m_core;
74 // IProject* m_currentProject;
75 ProjectModel* model;
76 QItemSelectionModel* selectionModel;
77 QMap<IProject*, QPointer<KSettings::Dialog> > m_cfgDlgs;
78 QPointer<QAction> m_closeAllProjects;
79 IProjectDialogProvider* dialog;
80 QList<KUrl> m_currentlyOpening; // project-file urls that are being opened
82 bool reopenProjectsOnStartup;
83 bool parseAllProjectSources;
85 void unloadAllProjectPlugins()
87 if( m_projects.isEmpty() )
88 m_core->pluginController()->unloadPlugins( IPluginController::Project );
91 void projectConfig( QObject * obj )
93 if( !obj )
94 return;
95 Project* proj = qobject_cast<Project*>(obj);
96 if( !proj )
97 return;
98 if( !m_cfgDlgs.contains( proj ) )
100 //@FIXME: compute a blacklist, based on a query for all KDevelop
101 //plugins implementing IProjectManager, removing from that the
102 //plugin that manages this project. Set this as blacklist on the
103 //dialog
104 //@FIXME: Currently its important to set a parentApp on the kcm's
105 //thats different from the component name of the application, else
106 //the plugin will show up on all projects settings dialogs.
108 QStringList pluginsForPrj = findPluginsForProject( proj );
109 kDebug() << "Using pluginlist:" << pluginsForPrj;
110 pluginsForPrj << "kdevplatformproject"; // for project-wide env settings.
111 m_cfgDlgs[proj] = new KSettings::Dialog( pluginsForPrj,
112 m_core->uiController()->activeMainWindow() );
113 m_cfgDlgs[proj]->setKCMArguments( QStringList()
114 << proj->developerTempFile()
115 << proj->projectTempFile()
116 << proj->projectFileUrl().url()
117 << proj->developerFileUrl().url() );
119 m_cfgDlgs[proj]->show();
121 QStringList findPluginsForProject( IProject* project )
123 QList<IPlugin*> plugins = m_core->pluginController()->loadedPlugins();
124 QStringList pluginnames;
125 kDebug() << "managerplugin:" << project->managerPlugin();
126 for( QList<IPlugin*>::iterator it = plugins.begin(); it != plugins.end(); it++ )
128 IPlugin* plugin = *it;
129 IProjectFileManager* iface = plugin->extension<KDevelop::IProjectFileManager>();
130 kDebug() << "Checking plugin:" << plugin << "with iface" << iface;
131 if( !iface || plugin == project->managerPlugin() )
132 pluginnames << m_core->pluginController()->pluginInfo( plugin ).pluginName();
135 return pluginnames;
139 IProjectDialogProvider::IProjectDialogProvider()
142 IProjectDialogProvider::~IProjectDialogProvider()
145 ProjectDialogProvider::ProjectDialogProvider(ProjectControllerPrivate* const p) : d(p)
148 ProjectDialogProvider::~ProjectDialogProvider()
151 KUrl ProjectDialogProvider::askProjectConfigLocation()
153 Q_ASSERT(d);
154 KSharedConfig * config = KGlobal::config().data();
155 KConfigGroup group = config->group( "General Options" );
156 QString dir = group.readEntry( "DefaultProjectsDirectory",
157 QDir::homePath() );
159 QString projectFileInfo = ShellExtension::getInstance()->projectFileExtension()
160 + "|" + ShellExtension::getInstance()->projectFileDescription()
161 + "\n";
162 KUrl response = KFileDialog::getOpenUrl( dir, projectFileInfo,
163 d->m_core->uiControllerInternal()->defaultMainWindow(),
164 i18n( "Open Project" ) );
165 return response;
168 bool ProjectDialogProvider::userWantsReopen()
170 Q_ASSERT(d);
171 return (KMessageBox::questionYesNo( d->m_core->uiControllerInternal()->defaultMainWindow(),
172 i18n( "Reopen the current project?" ) )
173 == KMessageBox::No) ? false : true;
176 void ProjectController::setDialogProvider(IProjectDialogProvider* dialog)
178 Q_ASSERT(d->dialog);
179 delete d->dialog;
180 d->dialog = dialog;
183 ProjectController::ProjectController( Core* core )
184 : IProjectController( core ), d( new ProjectControllerPrivate )
186 d->reopenProjectsOnStartup = false;
187 d->parseAllProjectSources = false;
188 d->m_core = core;
189 d->m_signalMapper = new QSignalMapper( this );
190 connect( d->m_signalMapper, SIGNAL( mapped( QObject* ) ),
191 this, SLOT( projectConfig( QObject* ) ) );
192 // d->m_currentProject = 0;
193 d->model = new ProjectModel();
194 d->selectionModel = new QItemSelectionModel(d->model);
195 if(!(Core::self()->setupFlags() & Core::NoUi)) setupActions();
197 loadSettings(false);
198 d->dialog = new ProjectDialogProvider(d);
201 void ProjectController::setupActions()
203 KActionCollection * ac =
204 d->m_core->uiControllerInternal()->defaultMainWindow()->actionCollection();
206 QAction *action;
208 action = ac->addAction( "project_open" );
209 action->setText(i18n( "&Open Project..." ) );
210 connect( action, SIGNAL( triggered( bool ) ), SLOT( openProject() ) );
211 action->setToolTip( i18n( "Open project" ) );
212 action->setWhatsThis( i18n( "<b>Open project</b><p>Opens a KDevelop 4 project.</p>" ) );
213 action->setIcon(KIcon("project-open"));
215 // action = ac->addAction( "project_close" );
216 // action->setText( i18n( "C&lose Project" ) );
217 // connect( action, SIGNAL( triggered( bool ) ), SLOT( closeProject() ) );
218 // action->setToolTip( i18n( "Close project" ) );
219 // action->setWhatsThis( i18n( "<b>Close project</b><p>Closes the current project." ) );
220 // action->setEnabled( false );
222 d->m_closeAllProjects = action = ac->addAction( "project_close_all" );
223 action->setText( i18n( "Close All Projects" ) );
224 connect( action, SIGNAL( triggered( bool ) ), SLOT( closeAllProjects() ) );
225 action->setToolTip( i18n( "Close all currently open projects" ) );
226 action->setWhatsThis( i18n( "<b>Close all projects</b><p>Closes all of the currently open projects." ) );
227 action->setEnabled( false );
228 action->setIcon(KIcon("window-close"));
230 KSharedConfig * config = KGlobal::config().data();
231 // KConfigGroup group = config->group( "General Options" );
233 d->m_recentAction = new KRecentFilesAction( this );
234 connect( d->m_recentAction, SIGNAL(urlSelected(const KUrl&)), SLOT(
235 openProject( const KUrl& ) ));
236 ac->addAction( "project_open_recent", d->m_recentAction );
237 d->m_recentAction->setText( i18n( "Open Recent" ) );
238 d->m_recentAction->setToolTip( i18n( "Open recent project" ) );
239 d->m_recentAction->setWhatsThis(
240 i18n( "<b>Open recent project</b><p>Opens recently opened project.</p>" ) );
241 d->m_recentAction->loadEntries( KConfigGroup(config, "RecentProjects") );
243 d->m_projectConfigAction = new KActionMenu( i18n("Configure Project"), ac );
244 ac->addAction( "project_config_menu", d->m_projectConfigAction );
245 d->m_projectConfigAction->setIcon(KIcon("configure"));
248 ProjectController::~ProjectController()
250 delete d->model;
251 delete d->dialog;
252 delete d;
255 void ProjectController::cleanup()
257 KSharedConfig * config = KGlobal::config().data();
258 KConfigGroup group = config->group( "General Options" );
260 KUrl::List openProjects;
262 foreach( IProject* project, d->m_projects ) {
263 openProjects.append(project->projectFileUrl());
264 closeProject( project );
267 group.writeEntry( "Open Projects", openProjects.toStringList() );
270 void ProjectController::initialize()
272 if (d->reopenProjectsOnStartup) {
273 KSharedConfig * config = KGlobal::config().data();
274 KConfigGroup group = config->group( "General Options" );
275 KUrl::List openProjects = group.readEntry( "Open Projects", QStringList() );
277 foreach (const KUrl& url, openProjects)
278 openProject(url);
282 void ProjectController::loadSettings( bool projectIsLoaded )
284 Q_UNUSED(projectIsLoaded)
286 KConfigGroup config(KGlobal::config(), "Project Manager");
288 d->reopenProjectsOnStartup = config.readEntry("Reopen Projects On Startup", false);
289 d->parseAllProjectSources = config.readEntry("Parse All Project Sources", false);
292 void ProjectController::saveSettings( bool projectIsLoaded )
294 Q_UNUSED( projectIsLoaded );
298 int ProjectController::projectCount() const
300 return d->m_projects.count();
303 IProject* ProjectController::projectAt( int num ) const
305 if( !d->m_projects.isEmpty() && num >= 0 && num < d->m_projects.count() )
306 return d->m_projects.at( num );
307 return 0;
310 QList<IProject*> ProjectController::projects() const
312 return d->m_projects;
315 bool ProjectController::openProject( const KUrl &projectFile )
317 KUrl url = projectFile;
319 if ( url.isEmpty() )
321 url = d->dialog->askProjectConfigLocation();
324 if ( !url.isValid() )
326 return false;
328 if ( d->m_currentlyOpening.contains(url))
330 kDebug() << "Already opening " << url << ". Aborting.";
331 return false;
334 foreach( IProject* project, d->m_projects )
336 if ( url == project->projectFileUrl() )
338 if ( d->dialog->userWantsReopen() )
339 { // close first, then open again by falling through
340 closeProject(project);
341 } else { // abort
342 return false;
347 //FIXME Create the hidden directory if it doesn't exist
348 if ( loadProjectPart() )
350 //The project file has been opened.
351 //Now we can load settings for all of the Core objects including this one!!
352 // Core::loadSettings();
353 d->m_core->pluginController()->loadPlugins( IPluginController::Project );
355 else
356 return false;
358 Project* project = new Project();
359 if ( !project->open( url ) )
361 delete project;
362 return false;
365 d->m_currentlyOpening << url;
366 d->m_closeAllProjects->setEnabled(true);
367 return true;
370 bool ProjectController::projectImportingFinished( IProject* project )
372 IPlugin *managerPlugin = project->managerPlugin();
373 QList<IPlugin*> pluglist;
374 pluglist.append( managerPlugin );
375 d->m_projectPlugins.insert( project, pluglist );
377 ProjectFolderItem *topItem = project->projectItem();
378 d->model->insertRow( d->model->rowCount(), topItem );
380 d->m_projects.append( project );
382 // KActionCollection * ac = d->m_core->uiController()->defaultMainWindow()->actionCollection();
383 // QAction * action;
385 //action = ac->action( "project_close" );
386 //action->setEnabled( true );
388 d->m_recentAction->addUrl( project->projectFileUrl() );
389 KSharedConfig * config = KGlobal::config().data();
390 KConfigGroup recentGroup = config->group("RecentProjects");
391 d->m_recentAction->saveEntries( recentGroup );
393 config->sync();
394 QAction* qa = new QAction( project->name(), d->m_projectConfigAction );
395 d->m_configActions.insert( project, qa );
396 connect( qa, SIGNAL( triggered() ), d->m_signalMapper, SLOT( map() ) );
397 d->m_signalMapper->setMapping( qa, project );
398 d->m_projectConfigAction->addAction( qa );
400 d->m_currentlyOpening.removeAll(project->projectFileUrl());
401 emit projectOpened( project );
403 KUrl::List parseList;
404 if (d->parseAllProjectSources)
406 // Add the project files to the background parser to be parsed.
407 QList<ProjectFileItem*> files = project->files();
408 foreach ( ProjectFileItem* file, files )
410 parseList.append( file->url() );
412 } else
414 // Add all currently open files that belong to the project to the background-parser,
415 // since more information may be available for parsing them now(Like include-paths).
416 foreach(IDocument* document, Core::self()->documentController()->openDocuments())
418 if(!project->filesForUrl(document->url()).isEmpty())
420 parseList.append(document->url());
424 //Add low-priority parse jobs, with only the minimum parsed information
425 Core::self()->languageController()->backgroundParser()->addDocumentList( parseList, KDevelop::TopDUContext::VisibleDeclarationsAndContexts, 10000 );
427 return true;
430 // helper method for closeProject()
431 void ProjectController::unloadUnusedProjectPlugins(IProject* proj)
433 QList<IPlugin*> pluginsForProj = d->m_projectPlugins.value( proj );
434 d->m_projectPlugins.remove( proj );
436 QList<IPlugin*> otherProjectPlugins;
437 Q_FOREACH( QList<IPlugin*> _list, d->m_projectPlugins )
439 otherProjectPlugins << _list;
442 QSet<IPlugin*> pluginsForProjSet = QSet<IPlugin*>::fromList( pluginsForProj );
443 QSet<IPlugin*> otherPrjPluginsSet = QSet<IPlugin*>::fromList( otherProjectPlugins );
444 // loaded - target = tobe unloaded.
445 QSet<IPlugin*> tobeRemoved = pluginsForProjSet.subtract( otherPrjPluginsSet );
446 Q_FOREACH( IPlugin* _plugin, tobeRemoved )
448 KPluginInfo _plugInfo = Core::self()->pluginController()->pluginInfo( _plugin );
449 if( _plugInfo.isValid() )
451 QString _plugName = _plugInfo.pluginName();
452 kDebug() << "about to unloading :" << _plugName;
453 Core::self()->pluginController()->unloadPlugin( _plugName );
458 // helper method for closeProject()
459 void ProjectController::disableProjectCloseAction()
461 MainWindow *mw = d->m_core->uiControllerInternal()->defaultMainWindow();
462 if (!mw) return;
463 KActionCollection * ac = mw->actionCollection();
464 QAction *action = ac->action("project_close");
465 if (action) action->setEnabled(false);
468 // helper method for closeProject()
469 void ProjectController::closeAllOpenedFiles(IProject* proj)
471 Q_FOREACH( ProjectFileItem *fileItem, proj->files() )
473 Core::self()->documentControllerInternal()->closeDocument( fileItem->url() );
477 // helper method for closeProject()
478 void ProjectController::deleteProjectSettingsMenu(IProject* proj)
480 QPointer<QAction> configAction = d->m_configActions.take(proj);
481 delete configAction;
482 if( d->m_cfgDlgs.contains(proj) )
484 delete d->m_cfgDlgs.take(proj);
488 // helper method for closeProject()
489 void ProjectController::initializePluginCleanup(IProject* proj)
491 // Unloading (and thus deleting) these plugins is not a good idea just yet
492 // as we're being called by the view part and it gets deleted when we unload the plugin(s)
493 // TODO: find a better place to unload
494 connect(proj, SIGNAL(destroyed(QObject*)), this, SLOT(unloadAllProjectPlugins()));
495 if (d->m_closeAllProjects)
497 d->m_closeAllProjects->setEnabled(false);
501 bool ProjectController::closeProject(IProject* proj)
503 if(!proj || d->m_projects.indexOf(proj) == -1)
505 return false;
507 emit projectClosing(proj);
508 //Core::self()->saveSettings(); // The project file is being closed.
509 // Now we can save settings for all of the Core
510 // objects including this one!!
511 //disableProjectCloseAction();
512 unloadUnusedProjectPlugins(proj);
513 closeAllOpenedFiles(proj);
514 deleteProjectSettingsMenu(proj);
515 proj->close();
516 proj->deleteLater(); //be safe when deleting
517 d->m_projects.removeAll(proj);
518 if (d->m_projects.isEmpty())
520 initializePluginCleanup(proj);
522 emit projectClosed(proj);
523 //PluginController::self() ->changeProfile( m_oldProfileName ); //FIXME
524 return true;
527 bool ProjectController::loadProjectPart()
530 return true;
533 ProjectModel* ProjectController::projectModel()
535 return d->model;
538 IProject* ProjectController::findProjectForUrl( const KUrl& url ) const
540 Q_FOREACH( IProject* proj, d->m_projects )
542 if( proj->inProject( url ) )
543 return proj;
545 return 0;
548 IProject* ProjectController::findProjectByName( const QString& name )
550 Q_FOREACH( IProject* proj, d->m_projects )
552 if( proj->name() == name )
554 return proj;
557 return 0;
560 bool ProjectController::configureProject( IProject* project )
562 d->projectConfig( project );
563 return true;
566 void ProjectController::addProject(IProject* project)
568 d->m_projects.append( project );
573 void ProjectController::closeAllProjects()
575 foreach (IProject* project, projects())
577 closeProject(project);
581 QItemSelectionModel* ProjectController::projectSelectionModel()
583 return d->selectionModel;
588 #include "projectcontroller.moc"