You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdevelop/src/projectmanager.cpp

669 lines
20 KiB

#include <tqfile.h>
#include <tqfileinfo.h>
#include <tqdir.h>
#include <tqdom.h>
#include <tqstringlist.h>
#include <tqptrlist.h>
#include <tqvbox.h>
#include <tqsize.h>
#include <tqtimer.h>
class TQDomDocument;
#include <kmessagebox.h>
#include <kdebug.h>
#include <klocale.h>
#include <kservice.h>
#include <ktrader.h>
#include <kfiledialog.h>
#include <kmainwindow.h>
#include <kparts/componentfactory.h>
#include <kaction.h>
#include <kapplication.h>
#include <kcmdlineargs.h>
#include <kprocess.h>
#include <kglobal.h>
#include <kstandarddirs.h>
#include <kio/netaccess.h>
#include <ktempfile.h>
#include <kmenubar.h>
#include <kstatusbar.h>
#include <kiconloader.h>
#include "kdevproject.h"
#include "kdevlanguagesupport.h"
#include "kdevplugin.h"
#include "kdevcreatefile.h"
#include "kdevversioncontrol.h"
#include "toplevel.h"
#include "core.h"
#include "api.h"
#include "plugincontroller.h"
#include "partcontroller.h"
#include "codemodel.h"
// #include "partselectwidget.h"
#include "languageselectwidget.h"
#include "generalinfowidget.h"
#include "projectsession.h"
#include "domutil.h"
#include "settings.h"
#include "projectmanager.h"
TQString ProjectInfo::sessionFile() const
{
TQString sf = m_projectURL.path(-1);
sf.truncate(sf.length() - 8); // without ".tdevelop"
sf += "kdevses"; // suffix for a TDevelop session file
return sf;
}
TQString ProjectManager::projectDirectory( const TQString& path, bool absolute ) {
if(absolute)
return path;
KURL url(ProjectManager::getInstance()->projectFile(), path);
url.cleanPath();
return url.path(-1);
}
ProjectManager *ProjectManager::s_instance = 0;
ProjectManager::ProjectManager()
: m_info(0L)
,m_pProjectSession(new ProjectSession)
{
}
ProjectManager::~ProjectManager()
{
delete m_pProjectSession;
delete m_info;
}
ProjectManager *ProjectManager::getInstance()
{
if (!s_instance)
s_instance = new ProjectManager;
return s_instance;
}
void ProjectManager::createActions( KActionCollection* ac )
{
KAction *action;
action = new KAction(i18n("&Open Project..."), "project_open", 0,
this, TQT_SLOT(slotOpenProject()),
ac, "project_open");
action->setToolTip( i18n("Open project"));
action->setWhatsThis(i18n("<b>Open project</b><p>Opens a KDevelop3 or KDevelop2 project."));
m_openRecentProjectAction =
new KRecentFilesAction(i18n("Open &Recent Project"), 0,
this, TQT_SLOT(loadProject(const KURL &)),
ac, "project_open_recent");
m_openRecentProjectAction->setToolTip(i18n("Open recent project"));
m_openRecentProjectAction->setWhatsThis(i18n("<b>Open recent project</b><p>Opens recently opened project."));
m_openRecentProjectAction->loadEntries(kapp->config(), "RecentProjects");
m_closeProjectAction =
new KAction(i18n("C&lose Project"), "fileclose",0,
this, TQT_SLOT(closeProject()),
ac, "project_close");
m_closeProjectAction->setEnabled(false);
m_closeProjectAction->setToolTip(i18n("Close project"));
m_closeProjectAction->setWhatsThis(i18n("<b>Close project</b><p>Closes the current project."));
m_projectOptionsAction = new KAction(i18n("Project &Options"), "configure", 0,
this, TQT_SLOT(slotProjectOptions()),
ac, "project_options" );
m_projectOptionsAction->setToolTip(i18n("Project options"));
m_projectOptionsAction->setWhatsThis(i18n("<b>Project options</b><p>Lets you customize project options."));
m_projectOptionsAction->setEnabled(false);
}
void ProjectManager::slotOpenProject()
{
KConfig *config = kapp->config();
config->setGroup("General Options");
TQString defaultProjectsDir = config->readPathEntry("DefaultProjectsDir", TQDir::homeDirPath()+"/");
KURL url = KFileDialog::getOpenURL(defaultProjectsDir,
i18n("*.tdevelop|KDevelop 3 Project Files\n"
"*.kdevprj|KDevelop 2 Project Files"),
TopLevel::getInstance()->main(), i18n("Open Project") );
if( url.isEmpty() )
return;
if (url.path().endsWith("kdevprj"))
loadKDevelop2Project( url );
else
loadProject( url );
}
void ProjectManager::slotProjectOptions()
{
KDialogBase dlg(KDialogBase::IconList, i18n("Project Options"),
KDialogBase::Ok|KDialogBase::Cancel, KDialogBase::Ok, TopLevel::getInstance()->main(),
"project options dialog");
TQVBox *box = dlg.addVBoxPage( i18n("General"), i18n("General"), BarIcon( "tdevelop", KIcon::SizeMedium ) );
GeneralInfoWidget *g = new GeneralInfoWidget(*API::getInstance()->projectDom(), box, "general informations widget");
connect (&dlg, TQT_SIGNAL(okClicked()), g, TQT_SLOT(accept()));
KConfig *config = kapp->config();
config->setGroup("Project Settings Dialog");
int height = config->readNumEntry( "Height", 600 );
int width = config->readNumEntry( "Width", 800 );
dlg.resize( width, height );
Core::getInstance()->doEmitProjectConfigWidget(&dlg);
dlg.exec();
saveProjectFile();
config->setGroup("Project Settings Dialog");
config->writeEntry( "Height", dlg.size().height() );
config->writeEntry( "Width", dlg.size().width() );
}
void ProjectManager::loadSettings()
{
}
void ProjectManager::saveSettings()
{
KConfig *config = kapp->config();
if (projectLoaded())
{
config->setGroup("General Options");
config->writePathEntry("Last Project", ProjectManager::getInstance()->projectFile().url());
}
m_openRecentProjectAction->saveEntries(config, "RecentProjects");
}
void ProjectManager::loadDefaultProject()
{
KConfig *config = kapp->config();
config->setGroup("General Options");
TQString project = config->readPathEntry("Last Project");
bool readProject = config->readBoolEntry("Read Last Project On Startup", true);
if (!project.isEmpty() && readProject)
{
loadProject(KURL(project));
}
}
bool ProjectManager::loadProject(const KURL &projectURL)
{
KURL url = projectURL;
if (!url.isValid())
return false;
if (url.isLocalFile())
{
TQDir dir(url.path());
TQString can_path = dir.canonicalPath();
//if the directory doesn't exist, the returned string is null which confuses the user later on. so better short-cut here
if(can_path.isNull())
return false;
else
url.setPath(can_path);
}
// reopen the already opened project?
if( projectLoaded() && url.path() == projectFile().path() )
{
if (KMessageBox::questionYesNo(TopLevel::getInstance()->main(),
i18n("Are you sure you want to reopen the current project?"), TQString(), i18n("Reopen"), i18n("Do Not Reopen")) == KMessageBox::No)
return false;
}
TopLevel::getInstance()->main()->menuBar()->setEnabled( false );
kapp->setOverrideCursor( waitCursor );
if( projectLoaded() && !closeProject() )
{
m_openRecentProjectAction->setCurrentItem( -1 );
TopLevel::getInstance()->main()->menuBar()->setEnabled( true );
kapp->restoreOverrideCursor();
return false;
}
m_info = new ProjectInfo;
m_info->m_projectURL = url;
TQTimer::singleShot( 0, this, TQT_SLOT(slotLoadProject()) );
// no one cares about this value
return true;
}
void ProjectManager::slotLoadProject( )
{
if( !loadProjectFile() )
{
m_openRecentProjectAction->removeURL(m_info->m_projectURL);
delete m_info; m_info = 0;
saveSettings();
TopLevel::getInstance()->main()->menuBar()->setEnabled( true );
kapp->restoreOverrideCursor();
return;
}
getGeneralInfo();
if( !loadLanguageSupport(m_info->m_language) ) {
delete m_info; m_info = 0;
TopLevel::getInstance()->main()->menuBar()->setEnabled( true );
kapp->restoreOverrideCursor();
return;
}
if( !loadProjectPart() ) {
unloadLanguageSupport();
delete m_info; m_info = 0;
API::getInstance()->setProjectDom( 0 );
TopLevel::getInstance()->main()->menuBar()->setEnabled( true );
kapp->restoreOverrideCursor();
return;
}
TopLevel::getInstance()->statusBar()->message( i18n("Changing plugin profile...") );
m_oldProfileName = PluginController::getInstance()->changeProfile(m_info->m_profileName);
TopLevel::getInstance()->statusBar()->message( i18n("Loading project plugins...") );
loadLocalParts();
// shall we try to load a session file from network?? Probably not.
if (m_info->m_projectURL.isLocalFile())
{
// first restore the project session stored in a .kdevses file
if (!m_pProjectSession->restoreFromFile(m_info->sessionFile(), PluginController::getInstance()->loadedPlugins() ))
{
kdWarning() << i18n("error during restoring of the TDevelop session !") << endl;
}
}
#if KDE_IS_VERSION(3,5,0)
m_openRecentProjectAction->addURL(projectFile(), projectName()); // KDE >= 3.5.x
#else
m_openRecentProjectAction->addURL(projectFile()); // KDE 3.4.x
#endif
m_closeProjectAction->setEnabled(true);
m_projectOptionsAction->setEnabled(true);
Core::getInstance()->doEmitProjectOpened();
TopLevel::getInstance()->main()->menuBar()->setEnabled( true );
kapp->restoreOverrideCursor();
TopLevel::getInstance()->statusBar()->message( i18n("Project loaded."), 3000 );
return;
}
bool ProjectManager::closeProject( bool exiting )
{
if( !projectLoaded() )
return true;
// save the session if it is a local file
if (m_info->m_projectURL.isLocalFile())
{
m_pProjectSession->saveToFile(m_info->sessionFile(), PluginController::getInstance()->loadedPlugins() );
}
if ( !PartController::getInstance()->querySaveFiles() )
return false;
Core::getInstance()->doEmitProjectClosed();
PluginController::getInstance()->unloadProjectPlugins();
PluginController::getInstance()->changeProfile(m_oldProfileName);
unloadLanguageSupport();
unloadProjectPart();
/// @todo if this fails, user is screwed
saveProjectFile();
API::getInstance()->setProjectDom(0);
API::getInstance()->codeModel()->wipeout();
delete m_info;
m_info = 0;
m_closeProjectAction->setEnabled(false);
m_projectOptionsAction->setEnabled(false);
if ( !exiting )
{
PartController::getInstance()->slotCloseAllWindows();
}
return true;
}
bool ProjectManager::loadProjectFile()
{
TQString path;
if (!KIO::NetAccess::download(m_info->m_projectURL, path, 0)) {
KMessageBox::sorry(TopLevel::getInstance()->main(),
i18n("Could not read project file: %1").arg(m_info->m_projectURL.prettyURL()));
return false;
}
TQFile fin(path);
if (!fin.open(IO_ReadOnly))
{
KMessageBox::sorry(TopLevel::getInstance()->main(),
i18n("Could not read project file: %1").arg(m_info->m_projectURL.prettyURL()));
return false;
}
int errorLine, errorCol;
TQString errorMsg;
if (!m_info->m_document.setContent(&fin, &errorMsg, &errorLine, &errorCol))
{
KMessageBox::sorry(TopLevel::getInstance()->main(),
i18n("This is not a valid project file.\n"
"XML error in line %1, column %2:\n%3")
.arg(errorLine).arg(errorCol).arg(errorMsg));
fin.close();
KIO::NetAccess::removeTempFile(path);
return false;
}
if (m_info->m_document.documentElement().nodeName() != "tdevelop")
{
KMessageBox::sorry(TopLevel::getInstance()->main(),
i18n("This is not a valid project file."));
fin.close();
KIO::NetAccess::removeTempFile(path);
return false;
}
fin.close();
KIO::NetAccess::removeTempFile(path);
API::getInstance()->setProjectDom(&m_info->m_document);
return true;
}
bool ProjectManager::saveProjectFile()
{
Q_ASSERT( API::getInstance()->projectDom() );
if (m_info->m_projectURL.isLocalFile()) {
TQFile fout(m_info->m_projectURL.path());
if( !fout.open(IO_WriteOnly) ) {
KMessageBox::sorry(TopLevel::getInstance()->main(), i18n("Could not write the project file."));
return false;
}
TQTextStream stream(&fout);
API::getInstance()->projectDom()->save(stream, 2);
fout.close();
} else {
KTempFile fout(TQString::fromLatin1("tdevelop3"));
fout.setAutoDelete(true);
if (fout.status() != 0) {
KMessageBox::sorry(TopLevel::getInstance()->main(), i18n("Could not write the project file."));
return false;
}
API::getInstance()->projectDom()->save(*(fout.textStream()), 2);
fout.close();
KIO::NetAccess::upload(fout.name(), m_info->m_projectURL, 0);
}
return true;
}
static TQString getAttribute(TQDomElement elem, TQString attr)
{
TQDomElement el = elem.namedItem(attr).toElement();
return el.firstChild().toText().data();
}
static void getAttributeList(TQDomElement elem, TQString attr, TQString tag, TQStringList &list)
{
list.clear();
TQDomElement el = elem.namedItem(attr).toElement();
TQDomElement item = el.firstChild().toElement();
while (!item.isNull())
{
if (item.tagName() == tag)
list << item.firstChild().toText().data();
item = item.nextSibling().toElement();
}
}
void ProjectManager::getGeneralInfo()
{
TQDomElement docEl = m_info->m_document.documentElement();
TQDomElement generalEl = docEl.namedItem("general").toElement();
m_info->m_projectPlugin = getAttribute(generalEl, "projectmanagement");
m_info->m_vcsPlugin = getAttribute(generalEl, "versioncontrol");
m_info->m_language = getAttribute(generalEl, "primarylanguage");
m_info->m_projectName = getAttribute(generalEl, "projectname");
if( m_info->m_projectName.isEmpty() )
{
m_info->m_projectName = m_info->m_projectURL.filename();
m_info->m_projectName = m_info->m_projectName.left(m_info->m_projectName.length()-TQString(".tdevelop").length());
TQDomElement prjname = m_info->m_document.createElement("projectname");
prjname.appendChild( m_info->m_document.createTextNode( m_info->m_projectName) );
generalEl.appendChild( prjname );
}
getAttributeList(generalEl, "ignoreparts", "part", m_info->m_ignoreParts);
getAttributeList(generalEl, "keywords", "keyword", m_info->m_keywords);
//FIXME: adymo: workaround for those project templates without "profile" element
// m_info->m_profileName = getAttribute(generalEl, "profile");
TQDomElement el = generalEl.namedItem("profile").toElement();
if (el.isNull())
// m_info->m_profileName = profileByAttributes(m_info->m_language, m_info->m_keywords);
m_info->m_profileName = Settings::profileByAttributes(m_info->m_language, m_info->m_keywords);
else
m_info->m_profileName = el.firstChild().toText().data();
}
bool ProjectManager::loadProjectPart()
{
KService::Ptr projectService = KService::serviceByDesktopName(m_info->m_projectPlugin);
if (!projectService) {
// this is for backwards compatibility with pre-alpha6 projects
projectService = KService::serviceByDesktopName(m_info->m_projectPlugin.lower());
}
if (!projectService) {
KMessageBox::sorry(TopLevel::getInstance()->main(),
i18n("No project management plugin %1 found.")
.arg(m_info->m_projectPlugin));
return false;
}
KDevProject *projectPart = KParts::ComponentFactory
::createInstanceFromService< KDevProject >( projectService, API::getInstance(), 0,
PluginController::argumentsFromService( projectService ) );
if ( !projectPart ) {
KMessageBox::sorry(TopLevel::getInstance()->main(),
i18n("Could not create project management plugin %1.")
.arg(m_info->m_projectPlugin));
return false;
}
API::getInstance()->setProject( projectPart );
TQDomDocument& dom = *API::getInstance()->projectDom();
TQString path = DomUtil::readEntry(dom,"/general/projectdirectory", ".");
bool absolute = DomUtil::readBoolEntry(dom,"/general/absoluteprojectpath",false);
TQString projectDir = projectDirectory( path, absolute );
kdDebug(9000) << "projectDir: " << projectDir << " projectName: " << m_info->m_projectName << endl;
projectPart->openProject(projectDir, m_info->m_projectName);
PluginController::getInstance()->integratePart( projectPart );
return true;
}
void ProjectManager::unloadProjectPart()
{
KDevProject *projectPart = API::getInstance()->project();
if( !projectPart ) return;
PluginController::getInstance()->removePart( projectPart );
projectPart->closeProject();
delete projectPart;
API::getInstance()->setProject(0);
}
bool ProjectManager::loadLanguageSupport(const TQString& lang)
{
kdDebug(9000) << "Looking for language support for " << lang << endl;
if (lang == m_info->m_activeLanguage)
{
kdDebug(9000) << "Language support already loaded" << endl;
return true;
}
KTrader::OfferList languageSupportOffers =
KTrader::self()->query(TQString::fromLatin1("TDevelop/LanguageSupport"),
TQString::fromLatin1("[X-TDevelop-Language] == '%1' and [X-TDevelop-Version] == %2").arg(lang).arg(TDEVELOP_PLUGIN_VERSION));
if (languageSupportOffers.isEmpty()) {
KMessageBox::sorry(TopLevel::getInstance()->main(),
i18n("No language plugin for %1 found.")
.arg(lang));
return false;
}
KService::Ptr languageSupportService = *languageSupportOffers.begin();
KDevLanguageSupport *langSupport = KParts::ComponentFactory
::createInstanceFromService<KDevLanguageSupport>( languageSupportService,
API::getInstance(),
0,
PluginController::argumentsFromService( languageSupportService ) );
if ( !langSupport ) {
KMessageBox::sorry(TopLevel::getInstance()->main(),
i18n("Could not create language plugin for %1.")
.arg(lang));
return false;
}
API::getInstance()->setLanguageSupport( langSupport );
PluginController::getInstance()->integratePart( langSupport );
m_info->m_activeLanguage = lang;
kdDebug(9000) << "Language support for " << lang << " successfully loaded." << endl;
return true;
}
void ProjectManager::unloadLanguageSupport()
{
KDevLanguageSupport *langSupport = API::getInstance()->languageSupport();
if( !langSupport ) return;
kdDebug(9000) << "Language support for " << langSupport->name() << " unloading..." << endl;
PluginController::getInstance()->removePart( langSupport );
delete langSupport;
API::getInstance()->setLanguageSupport(0);
}
void ProjectManager::loadLocalParts()
{
// Make sure to refresh load/ignore lists
getGeneralInfo();
PluginController::getInstance()->unloadPlugins( m_info->m_ignoreParts );
PluginController::getInstance()->loadProjectPlugins( m_info->m_ignoreParts );
PluginController::getInstance()->loadGlobalPlugins( m_info->m_ignoreParts );
}
KURL ProjectManager::projectFile() const
{
if (!m_info)
return KURL();
return m_info->m_projectURL;
}
TQString ProjectManager::projectName() const
{
if (!m_info) return TQString();
return m_info->m_projectName;
}
bool ProjectManager::projectLoaded() const
{
return m_info != 0;
}
ProjectSession* ProjectManager::projectSession() const
{
return m_pProjectSession;
}
bool ProjectManager::loadKDevelop2Project( const KURL & url )
{
if( !url.isValid() || !url.isLocalFile() ){
KMessageBox::sorry(0, i18n("Invalid URL."));
return false;
}
TQString cmd = KGlobal::dirs()->findExe( "kdevprj2tdevelop" );
if (cmd.isEmpty()) {
KMessageBox::sorry(0, i18n("You do not have 'kdevprj2tdevelop' installed."));
return false;
}
TQFileInfo fileInfo( url.path() );
KShellProcess proc( "/bin/sh" );
proc.setWorkingDirectory( fileInfo.dirPath(true) );
proc << "perl" << cmd << KShellProcess::quote( url.path() );
proc.start( KProcess::Block );
TQString projectFile = fileInfo.dirPath( true ) + "/" + fileInfo.baseName() + ".tdevelop";
return loadProject( KURL(projectFile) );
}
// TQString ProjectManager::profileByAttributes(const TQString &language, const TQStringList &keywords)
// {
// KConfig config(locate("data", "tdevelop/profiles/projectprofiles"));
// config.setGroup(language);
//
// TQStringList profileKeywords = TQStringList::split("/", "Empty");
// if (config.hasKey("Keywords"))
// profileKeywords = config.readListEntry("Keywords");
//
// int idx = 0;
// for (TQStringList::const_iterator it = profileKeywords.constBegin();
// it != profileKeywords.constEnd(); ++it)
// {
// if (keywords.contains(*it))
// {
// idx = profileKeywords.findIndex(*it);
// break;
// }
// }
//
// TQStringList profiles;
// if (config.hasKey("Profiles"))
// {
// profiles = config.readListEntry("Profiles");
// kdDebug() << "IDX: " << idx << " PROFILE: " << profiles[idx] << endl;
// return profiles[idx];
// }
// return "TDevelop";
// }
#include "projectmanager.moc"