00001 #include <qfile.h>
00002 #include <qfileinfo.h>
00003 #include <qdom.h>
00004 #include <qstringlist.h>
00005 #include <qptrlist.h>
00006 #include <qvbox.h>
00007 #include <qsize.h>
00008 #include <qtimer.h>
00009
00010 class QDomDocument;
00011
00012 #include <kmessagebox.h>
00013 #include <kdebug.h>
00014 #include <klocale.h>
00015 #include <kservice.h>
00016 #include <ktrader.h>
00017 #include <kfiledialog.h>
00018 #include <kmainwindow.h>
00019 #include <kparts/componentfactory.h>
00020 #include <kaction.h>
00021 #include <kapplication.h>
00022 #include <kcmdlineargs.h>
00023 #include <kprocess.h>
00024 #include <kglobal.h>
00025 #include <kstandarddirs.h>
00026 #include <kio/netaccess.h>
00027 #include <ktempfile.h>
00028 #include <kmenubar.h>
00029 #include <kstatusbar.h>
00030 #include <kiconloader.h>
00031
00032 #include "kdevproject.h"
00033 #include "kdevlanguagesupport.h"
00034 #include "kdevplugin.h"
00035 #include "kdevcreatefile.h"
00036
00037
00038 #include "toplevel.h"
00039 #include "core.h"
00040 #include "api.h"
00041 #include "plugincontroller.h"
00042 #include "partcontroller.h"
00043 #include "codemodel.h"
00044 #include "partselectwidget.h"
00045 #include "languageselectwidget.h"
00046 #include "generalinfowidget.h"
00047 #include "projectsession.h"
00048 #include "domutil.h"
00049
00050 #include "projectmanager.h"
00051
00052 QString ProjectInfo::sessionFile() const
00053 {
00054 QString sf = m_projectURL.path(-1);
00055 sf.truncate(sf.length() - 8);
00056 sf += "kdevses";
00057 return sf;
00058 }
00059
00060 QString ProjectManager::projectDirectory( const QString& path, bool absolute ) {
00061 if(absolute)
00062 return path;
00063 KURL url(ProjectManager::getInstance()->projectFile(), path);
00064 url.cleanPath();
00065 return url.path(-1);
00066 }
00067
00068 ProjectManager *ProjectManager::s_instance = 0;
00069
00070 ProjectManager::ProjectManager()
00071 : m_info(0L)
00072 ,m_pProjectSession(new ProjectSession)
00073 {
00074 }
00075
00076 ProjectManager::~ProjectManager()
00077 {
00078 delete m_pProjectSession;
00079 delete m_info;
00080 }
00081
00082 ProjectManager *ProjectManager::getInstance()
00083 {
00084 if (!s_instance)
00085 s_instance = new ProjectManager;
00086 return s_instance;
00087 }
00088
00089 void ProjectManager::createActions( KActionCollection* ac )
00090 {
00091 KAction *action;
00092
00093 action = new KAction(i18n("&Open Project..."), "project_open", 0,
00094 this, SLOT(slotOpenProject()),
00095 ac, "project_open");
00096 action->setToolTip( i18n("Open project"));
00097 action->setWhatsThis(i18n("<b>Open project</b><p>Opens a KDevelop3 or KDevelop2 project."));
00098
00099 m_openRecentProjectAction =
00100 new KRecentFilesAction(i18n("Open &Recent Project"), 0,
00101 this, SLOT(loadProject(const KURL &)),
00102 ac, "project_open_recent");
00103 m_openRecentProjectAction->setToolTip(i18n("Open recent project"));
00104 m_openRecentProjectAction->setWhatsThis(i18n("<b>Open recent project</b><p>Opens recently opened project."));
00105 m_openRecentProjectAction->loadEntries(kapp->config(), "RecentProjects");
00106
00107 m_closeProjectAction =
00108 new KAction(i18n("C&lose Project"), "fileclose",0,
00109 this, SLOT(closeProject()),
00110 ac, "project_close");
00111 m_closeProjectAction->setEnabled(false);
00112 m_closeProjectAction->setToolTip(i18n("Close project"));
00113 m_closeProjectAction->setWhatsThis(i18n("<b>Close project</b><p>Closes the current project."));
00114
00115 m_projectOptionsAction = new KAction(i18n("Project &Options..."), "configure", 0,
00116 this, SLOT(slotProjectOptions()),
00117 ac, "project_options" );
00118 m_projectOptionsAction->setToolTip(i18n("Project options"));
00119 m_projectOptionsAction->setWhatsThis(i18n("<b>Project options</b><p>Lets you customize project options."));
00120 m_projectOptionsAction->setEnabled(false);
00121
00122 m_activeLanguage = new KSelectAction(i18n("&Active Language"), 0, ac, "project_active_language");
00123 m_activeLanguage->setWhatsThis(i18n("<b>Active language</b><p>Sets the active programming language."));
00124 m_activeLanguage->setEnabled(false);
00125 connect(m_activeLanguage, SIGNAL(activated(const QString&)),
00126 this, SLOT(switchLanguage(const QString&)));
00127 }
00128
00129 void ProjectManager::slotOpenProject()
00130 {
00131 KConfig *config = kapp->config();
00132 config->setGroup("General Options");
00133 QString defaultProjectsDir = config->readPathEntry("DefaultProjectsDir", QDir::homeDirPath()+"/");
00134
00135 KURL url = KFileDialog::getOpenURL(defaultProjectsDir,
00136 i18n("*.kdevelop|KDevelop 3 Project Files\n"
00137 "*.kdevprj|KDevelop 2 Project Files"),
00138 TopLevel::getInstance()->main(), i18n("Open Project") );
00139
00140 if( url.isEmpty() )
00141 return;
00142
00143 if (url.path().endsWith("kdevprj"))
00144 loadKDevelop2Project( url );
00145 else
00146 loadProject( url );
00147 }
00148
00149 void ProjectManager::slotProjectOptions()
00150 {
00151 KDialogBase dlg(KDialogBase::IconList, i18n("Project Options"),
00152 KDialogBase::Ok|KDialogBase::Cancel, KDialogBase::Ok, TopLevel::getInstance()->main(),
00153 "project options dialog");
00154
00155 QVBox *box = dlg.addVBoxPage( i18n("General"), i18n("General"), DesktopIcon("kdevelop") );
00156 GeneralInfoWidget *g = new GeneralInfoWidget(*API::getInstance()->projectDom(), box, "general informations widget");
00157 connect (&dlg, SIGNAL(okClicked()), g, SLOT(accept()));
00158
00159 QVBox *vbox = dlg.addVBoxPage( i18n("Plugins"), i18n("Plugins"), DesktopIcon("kdf") );
00160 PartSelectWidget *w = new PartSelectWidget(*API::getInstance()->projectDom(), vbox, "part selection widget");
00161 vbox = dlg.addVBoxPage(i18n("Languages"));
00162 LanguageSelectWidget *lw = new LanguageSelectWidget(*API::getInstance()->projectDom(), vbox, "language selection widget");
00163 connect( &dlg, SIGNAL(okClicked()), w, SLOT(accept()) );
00164 connect( &dlg, SIGNAL(okClicked()), lw, SLOT(accept()) );
00165 connect( w, SIGNAL(accepted()), this, SLOT(loadLocalParts()) );
00166 connect( lw, SIGNAL(accepted()), this, SLOT(updateActiveLangMenu()) );
00167
00168 KConfig *config = kapp->config();
00169 config->setGroup("Project Settings Dialog");
00170 int height = config->readNumEntry( "Height", 600 );
00171 int width = config->readNumEntry( "Width", 800 );
00172
00173 dlg.resize( width, height );
00174
00175 Core::getInstance()->doEmitProjectConfigWidget(&dlg);
00176 dlg.exec();
00177
00178 saveProjectFile();
00179
00180 config->setGroup("Project Settings Dialog");
00181 config->writeEntry( "Height", dlg.size().height() );
00182 config->writeEntry( "Width", dlg.size().width() );
00183 }
00184
00185 void ProjectManager::updateActiveLangMenu()
00186 {
00187 getGeneralInfo();
00188 QStringList list( m_info->m_secondaryLanguages );
00189 list.prepend( m_info->m_language );
00190 m_activeLanguage->setItems( list );
00191 m_activeLanguage->setEnabled( m_info->m_secondaryLanguages.count() > 0 );
00192 m_activeLanguage->setCurrentItem(m_activeLanguage->items().findIndex(m_info->m_activeLanguage));
00193 }
00194
00195 void ProjectManager::switchLanguage(const QString& lang)
00196 {
00197
00198 if ( !m_info ) return;
00199
00200
00201 PluginController::getInstance()->unloadPlugins( m_info->m_loadParts );
00202 unloadLanguageSupport();
00203 m_info->m_loadParts.clear();
00204 loadLanguageSupport(lang);
00205 loadLocalParts();
00206 Core::getInstance()->doEmitLanguageChanged();
00207 }
00208
00209 void ProjectManager::loadSettings()
00210 {
00211 }
00212
00213 void ProjectManager::saveSettings()
00214 {
00215 KConfig *config = kapp->config();
00216
00217 if (projectLoaded())
00218 {
00219 config->setGroup("General Options");
00220 config->writePathEntry("Last Project", ProjectManager::getInstance()->projectFile().url());
00221 }
00222
00223 m_openRecentProjectAction->saveEntries(config, "RecentProjects");
00224 }
00225
00226 void ProjectManager::loadDefaultProject()
00227 {
00228 KConfig *config = kapp->config();
00229 config->setGroup("General Options");
00230 QString project = config->readPathEntry("Last Project");
00231 bool readProject = config->readBoolEntry("Read Last Project On Startup", true);
00232 if (!project.isEmpty() && readProject)
00233 {
00234 loadProject(KURL(project));
00235 }
00236 }
00237
00238 bool ProjectManager::loadProject(const KURL &url)
00239 {
00240 if (!url.isValid())
00241 return false;
00242
00243
00244 if( url.path() == projectFile().path() )
00245 {
00246 if (KMessageBox::questionYesNo(TopLevel::getInstance()->main(),
00247 i18n("Are you sure you want to reopen the current project?")) == KMessageBox::No)
00248 return false;
00249 }
00250
00251 TopLevel::getInstance()->main()->menuBar()->setEnabled( false );
00252 kapp->setOverrideCursor( waitCursor );
00253
00254 if( projectLoaded() && !closeProject() )
00255 {
00256 m_openRecentProjectAction->setCurrentItem( -1 );
00257 TopLevel::getInstance()->main()->menuBar()->setEnabled( true );
00258 kapp->restoreOverrideCursor();
00259 return false;
00260 }
00261
00262 m_info = new ProjectInfo;
00263 m_info->m_projectURL = url;
00264
00265 QTimer::singleShot( 0, this, SLOT(slotLoadProject()) );
00266
00267
00268 return true;
00269 }
00270
00271 void ProjectManager::slotLoadProject( )
00272 {
00273 if( !loadProjectFile() )
00274 {
00275 m_openRecentProjectAction->removeURL(m_info->m_projectURL);
00276 delete m_info; m_info = 0;
00277 saveSettings();
00278 TopLevel::getInstance()->main()->menuBar()->setEnabled( true );
00279 kapp->restoreOverrideCursor();
00280 return;
00281 }
00282
00283 getGeneralInfo();
00284
00285 updateActiveLangMenu();
00286
00287 if( !loadLanguageSupport(m_info->m_language) ) {
00288 delete m_info; m_info = 0;
00289 TopLevel::getInstance()->main()->menuBar()->setEnabled( true );
00290 kapp->restoreOverrideCursor();
00291 return;
00292 }
00293
00294 if( !loadProjectPart() ) {
00295 unloadLanguageSupport();
00296 delete m_info; m_info = 0;
00297 TopLevel::getInstance()->main()->menuBar()->setEnabled( true );
00298 kapp->restoreOverrideCursor();
00299 return;
00300 }
00301
00302 TopLevel::getInstance()->statusBar()->message( i18n("Loading project plugins...") );
00303 loadLocalParts();
00304
00305
00306 if (m_info->m_projectURL.isLocalFile())
00307 {
00308
00309 if (!m_pProjectSession->restoreFromFile(m_info->sessionFile(), PluginController::getInstance()->loadedPlugins() ))
00310 {
00311 kdWarning() << i18n("error during restoring of the KDevelop session !") << endl;
00312 }
00313 }
00314
00315 m_openRecentProjectAction->addURL(projectFile());
00316
00317 m_closeProjectAction->setEnabled(true);
00318 m_projectOptionsAction->setEnabled(true);
00319
00320 Core::getInstance()->doEmitProjectOpened();
00321
00322 TopLevel::getInstance()->main()->menuBar()->setEnabled( true );
00323 kapp->restoreOverrideCursor();
00324
00325 TopLevel::getInstance()->statusBar()->message( i18n("Project loaded."), 3000 );
00326
00327 return;
00328 }
00329
00330
00331 bool ProjectManager::closeProject( bool exiting )
00332 {
00333 if( !projectLoaded() )
00334 return true;
00335
00336
00337 if (m_info->m_projectURL.isLocalFile())
00338 {
00339 m_pProjectSession->saveToFile(m_info->sessionFile(), PluginController::getInstance()->loadedPlugins() );
00340 }
00341
00342 if ( !PartController::getInstance()->querySaveFiles() )
00343 return false;
00344
00345 Core::getInstance()->doEmitProjectClosed();
00346
00347 TopLevel::getInstance()->prepareToCloseViews();
00348
00349
00350 PluginController::getInstance()->unloadPlugins( m_info->m_loadParts );
00351 unloadLanguageSupport();
00352 unloadProjectPart();
00353
00355 saveProjectFile();
00356
00357 API::getInstance()->setProjectDom(0);
00358 API::getInstance()->codeModel()->wipeout();
00359
00360 delete m_info;
00361 m_info = 0;
00362
00363 m_closeProjectAction->setEnabled(false);
00364 m_projectOptionsAction->setEnabled(false);
00365 m_activeLanguage->setEnabled(false);
00366
00367 if ( !exiting )
00368 {
00369 PartController::getInstance()->slotCloseAllWindows();
00370 }
00371
00372 return true;
00373 }
00374
00375 bool ProjectManager::loadProjectFile()
00376 {
00377 QString path;
00378 if (!KIO::NetAccess::download(m_info->m_projectURL, path, 0)) {
00379 KMessageBox::sorry(TopLevel::getInstance()->main(),
00380 i18n("Could not read project file: %1").arg(m_info->m_projectURL.prettyURL()));
00381 return false;
00382 }
00383
00384 QFile fin(path);
00385 if (!fin.open(IO_ReadOnly))
00386 {
00387 KMessageBox::sorry(TopLevel::getInstance()->main(),
00388 i18n("Could not read project file: %1").arg(m_info->m_projectURL.prettyURL()));
00389 return false;
00390 }
00391
00392 int errorLine, errorCol;
00393 QString errorMsg;
00394 if (!m_info->m_document.setContent(&fin, &errorMsg, &errorLine, &errorCol))
00395 {
00396 KMessageBox::sorry(TopLevel::getInstance()->main(),
00397 i18n("This is not a valid project file.\n"
00398 "XML error in line %1, column %2:\n%3")
00399 .arg(errorLine).arg(errorCol).arg(errorMsg));
00400 fin.close();
00401 KIO::NetAccess::removeTempFile(path);
00402 return false;
00403 }
00404 if (m_info->m_document.documentElement().nodeName() != "kdevelop")
00405 {
00406 KMessageBox::sorry(TopLevel::getInstance()->main(),
00407 i18n("This is not a valid project file."));
00408 fin.close();
00409 KIO::NetAccess::removeTempFile(path);
00410 return false;
00411 }
00412
00413 fin.close();
00414 KIO::NetAccess::removeTempFile(path);
00415
00416 API::getInstance()->setProjectDom(&m_info->m_document);
00417
00418 return true;
00419 }
00420
00421 bool ProjectManager::saveProjectFile()
00422 {
00423 Q_ASSERT( API::getInstance()->projectDom() );
00424
00425 if (m_info->m_projectURL.isLocalFile()) {
00426 QFile fout(m_info->m_projectURL.path());
00427 if( !fout.open(IO_WriteOnly) ) {
00428 KMessageBox::sorry(TopLevel::getInstance()->main(), i18n("Could not write the project file."));
00429 return false;
00430 }
00431
00432 QTextStream stream(&fout);
00433 API::getInstance()->projectDom()->save(stream, 2);
00434 fout.close();
00435 } else {
00436 KTempFile fout(QString::fromLatin1("kdevelop3"));
00437 fout.setAutoDelete(true);
00438 if (fout.status() != 0) {
00439 KMessageBox::sorry(TopLevel::getInstance()->main(), i18n("Could not write the project file."));
00440 return false;
00441 }
00442 API::getInstance()->projectDom()->save(*(fout.textStream()), 2);
00443 fout.close();
00444 KIO::NetAccess::upload(fout.name(), m_info->m_projectURL, 0);
00445 }
00446
00447 return true;
00448 }
00449
00450 static QString getAttribute(QDomElement elem, QString attr)
00451 {
00452 QDomElement el = elem.namedItem(attr).toElement();
00453 return el.firstChild().toText().data();
00454 }
00455
00456 static void getAttributeList(QDomElement elem, QString attr, QString tag, QStringList &list)
00457 {
00458 list.clear();
00459
00460 QDomElement el = elem.namedItem(attr).toElement();
00461 QDomElement item = el.firstChild().toElement();
00462 while (!item.isNull())
00463 {
00464 if (item.tagName() == tag)
00465 list << item.firstChild().toText().data();
00466 item = item.nextSibling().toElement();
00467 }
00468 }
00469
00470 void ProjectManager::getGeneralInfo()
00471 {
00472 QDomElement docEl = m_info->m_document.documentElement();
00473 QDomElement generalEl = docEl.namedItem("general").toElement();
00474
00475 m_info->m_projectPlugin = getAttribute(generalEl, "projectmanagement");
00476 m_info->m_language = getAttribute(generalEl, "primarylanguage");
00477
00478 getAttributeList(generalEl, "ignoreparts", "part", m_info->m_ignoreParts);
00479 getAttributeList(generalEl, "keywords", "keyword", m_info->m_keywords);
00480 getAttributeList(generalEl, "secondaryLanguages", "language", m_info->m_secondaryLanguages);
00481 }
00482
00483 bool ProjectManager::loadProjectPart()
00484 {
00485 KService::Ptr projectService = KService::serviceByDesktopName(m_info->m_projectPlugin);
00486 if (!projectService) {
00487
00488 projectService = KService::serviceByDesktopName(m_info->m_projectPlugin.lower());
00489 }
00490 if (!projectService) {
00491 KMessageBox::sorry(TopLevel::getInstance()->main(),
00492 i18n("No project management plugin %1 found.")
00493 .arg(m_info->m_projectPlugin));
00494 return false;
00495 }
00496
00497 KDevProject *projectPart = KParts::ComponentFactory
00498 ::createInstanceFromService< KDevProject >( projectService, API::getInstance(), 0,
00499 PluginController::argumentsFromService( projectService ) );
00500 if ( !projectPart ) {
00501 KMessageBox::sorry(TopLevel::getInstance()->main(),
00502 i18n("Could not create project management plugin %1.")
00503 .arg(m_info->m_projectPlugin));
00504 return false;
00505 }
00506
00507 API::getInstance()->setProject( projectPart );
00508
00509 QDomDocument& dom = *API::getInstance()->projectDom();
00510 QString path = DomUtil::readEntry(dom,"/general/projectdirectory", ".");
00511 bool absolute = DomUtil::readBoolEntry(dom,"/general/absoluteprojectpath",false);
00512 QString projectDir = projectDirectory( path, absolute );
00513 kdDebug(9000) << "projectDir: " << projectDir << " projectName: " << m_info->m_projectURL.fileName() << endl;
00514
00515 projectPart->openProject(projectDir, m_info->m_projectURL.fileName());
00516
00517 PluginController::getInstance()->integratePart( projectPart );
00518
00519 return true;
00520 }
00521
00522 void ProjectManager::unloadProjectPart()
00523 {
00524 KDevProject *projectPart = API::getInstance()->project();
00525 if( !projectPart ) return;
00526 PluginController::getInstance()->removePart( projectPart );
00527 projectPart->closeProject();
00528 delete projectPart;
00529 API::getInstance()->setProject(0);
00530 }
00531
00532 bool ProjectManager::loadLanguageSupport(const QString& lang)
00533 {
00534 kdDebug(9000) << "Looking for language support for " << lang << endl;
00535
00536 if (lang == m_info->m_activeLanguage)
00537 {
00538 kdDebug(9000) << "Language support already loaded" << endl;
00539 return true;
00540 }
00541
00542 KTrader::OfferList languageSupportOffers =
00543 KTrader::self()->query(QString::fromLatin1("KDevelop/LanguageSupport"),
00544 QString::fromLatin1("[X-KDevelop-Language] == '%1' and [X-KDevelop-Version] == %2").arg(m_info->m_language).arg(KDEVELOP_PLUGIN_VERSION));
00545
00546 if (languageSupportOffers.isEmpty()) {
00547 KMessageBox::sorry(TopLevel::getInstance()->main(),
00548 i18n("No language plugin for %1 found.")
00549 .arg(lang));
00550 return false;
00551 }
00552
00553 KService::Ptr languageSupportService = *languageSupportOffers.begin();
00554 KDevLanguageSupport *langSupport = KParts::ComponentFactory
00555 ::createInstanceFromService<KDevLanguageSupport>( languageSupportService,
00556 API::getInstance(),
00557 0,
00558 PluginController::argumentsFromService( languageSupportService ) );
00559
00560 if ( !langSupport ) {
00561 KMessageBox::sorry(TopLevel::getInstance()->main(),
00562 i18n("Could not create language plugin for %1.")
00563 .arg(m_info->m_language));
00564 return false;
00565 }
00566
00567 API::getInstance()->setLanguageSupport( langSupport );
00568 PluginController::getInstance()->integratePart( langSupport );
00569 m_info->m_activeLanguage = lang;
00570 m_activeLanguage->setCurrentItem(m_activeLanguage->items().findIndex(lang));
00571
00572 kdDebug(9000) << "Language support for " << lang << " successfully loaded." << endl;
00573 return true;
00574 }
00575
00576 void ProjectManager::unloadLanguageSupport()
00577 {
00578 KDevLanguageSupport *langSupport = API::getInstance()->languageSupport();
00579 if( !langSupport ) return;
00580 kdDebug(9000) << "Language support for " << langSupport->name() << " unloading..." << endl;
00581 PluginController::getInstance()->removePart( langSupport );
00582 delete langSupport;
00583 API::getInstance()->setLanguageSupport(0);
00584 }
00585
00586 void ProjectManager::loadLocalParts()
00587 {
00588
00589 getGeneralInfo();
00590
00591 PluginController::getInstance()->unloadPlugins( m_info->m_ignoreParts );
00592 PluginController::getInstance()->loadLocalParts( m_info, m_info->m_loadParts, m_info->m_ignoreParts );
00593 }
00594
00595 KURL ProjectManager::projectFile() const
00596 {
00597 if (!m_info)
00598 return KURL();
00599 return m_info->m_projectURL;
00600 }
00601
00602 bool ProjectManager::projectLoaded() const
00603 {
00604 return m_info != 0;
00605 }
00606
00607 ProjectSession* ProjectManager::projectSession() const
00608 {
00609 return m_pProjectSession;
00610 }
00611
00612 bool ProjectManager::loadKDevelop2Project( const KURL & url )
00613 {
00614 if( !url.isValid() || !url.isLocalFile() ){
00615 KMessageBox::sorry(0, i18n("Invalid URL."));
00616 return false;
00617 }
00618
00619 QString cmd = KGlobal::dirs()->findExe( "kdevprj2kdevelop" );
00620 if (cmd.isEmpty()) {
00621 KMessageBox::sorry(0, i18n("You do not have 'kdevprj2kdevelop' installed."));
00622 return false;
00623 }
00624
00625 QFileInfo fileInfo( url.path() );
00626
00627 KShellProcess proc( "/bin/sh" );
00628 proc.setWorkingDirectory( fileInfo.dirPath(true) );
00629 proc << "perl" << cmd << KShellProcess::quote( url.path() );
00630 proc.start( KProcess::Block );
00631
00632 QString projectFile = fileInfo.dirPath( true ) + "/" + fileInfo.baseName() + ".kdevelop";
00633 return loadProject( KURL(projectFile) );
00634 }
00635
00636 #include "projectmanager.moc"