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>
29 #include <QItemSelectionModel>
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>
55 #include "mainwindow.h"
56 #include "shellextension.h"
57 #include "plugincontroller.h"
58 #include "uicontroller.h"
59 #include "documentcontroller.h"
64 class ProjectControllerPrivate
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
;
74 // IProject* m_currentProject;
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
)
95 Project
* proj
= qobject_cast
<Project
*>(obj
);
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
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();
139 IProjectDialogProvider::IProjectDialogProvider()
142 IProjectDialogProvider::~IProjectDialogProvider()
145 ProjectDialogProvider::ProjectDialogProvider(ProjectControllerPrivate
* const p
) : d(p
)
148 ProjectDialogProvider::~ProjectDialogProvider()
151 KUrl
ProjectDialogProvider::askProjectConfigLocation()
154 KSharedConfig
* config
= KGlobal::config().data();
155 KConfigGroup group
= config
->group( "General Options" );
156 QString dir
= group
.readEntry( "DefaultProjectsDirectory",
159 QString projectFileInfo
= ShellExtension::getInstance()->projectFileExtension()
160 + "|" + ShellExtension::getInstance()->projectFileDescription()
162 KUrl response
= KFileDialog::getOpenUrl( dir
, projectFileInfo
,
163 d
->m_core
->uiControllerInternal()->defaultMainWindow(),
164 i18n( "Open Project" ) );
168 bool ProjectDialogProvider::userWantsReopen()
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
)
183 ProjectController::ProjectController( Core
* core
)
184 : IProjectController( core
), d( new ProjectControllerPrivate
)
186 d
->reopenProjectsOnStartup
= false;
187 d
->parseAllProjectSources
= false;
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();
198 d
->dialog
= new ProjectDialogProvider(d
);
201 void ProjectController::setupActions()
203 KActionCollection
* ac
=
204 d
->m_core
->uiControllerInternal()->defaultMainWindow()->actionCollection();
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()
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
)
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
);
310 QList
<IProject
*> ProjectController::projects() const
312 return d
->m_projects
;
315 bool ProjectController::openProject( const KUrl
&projectFile
)
317 KUrl url
= projectFile
;
321 url
= d
->dialog
->askProjectConfigLocation();
324 if ( !url
.isValid() )
328 if ( d
->m_currentlyOpening
.contains(url
))
330 kDebug() << "Already opening " << url
<< ". Aborting.";
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
);
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
);
358 Project
* project
= new Project();
359 if ( !project
->open( url
) )
365 d
->m_currentlyOpening
<< url
;
366 d
->m_closeAllProjects
->setEnabled(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();
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
);
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() );
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 );
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();
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
);
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)
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
);
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
527 bool ProjectController::loadProjectPart()
533 ProjectModel
* ProjectController::projectModel()
538 IProject
* ProjectController::findProjectForUrl( const KUrl
& url
) const
540 Q_FOREACH( IProject
* proj
, d
->m_projects
)
542 if( proj
->inProject( url
) )
548 IProject
* ProjectController::findProjectByName( const QString
& name
)
550 Q_FOREACH( IProject
* proj
, d
->m_projects
)
552 if( proj
->name() == name
)
560 bool ProjectController::configureProject( IProject
* project
)
562 d
->projectConfig( project
);
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"