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/parts/filelist/projectviewpart.cpp

586 lines
20 KiB

/***************************************************************************
* Copyright (C) 2005 by Jens Herden *
* jens@kdewebdev.org *
* *
* 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 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 "projectviewprojectconfig.h"
#include "projectviewconfig.h"
#include "projectviewpart.h"
#include "filelist_widget.h"
#include "toolbarguibuilder.h"
#include <qtimer.h>
#include <qpopupmenu.h>
#include <qwhatsthis.h>
#include <qvaluelist.h>
#include <qmap.h>
#include <qdir.h>
#include <qdom.h>
#include <qvbox.h>
#include <qhbox.h>
#include <qpoint.h>
#include <qlayout.h>
#include <kmainwindow.h>
#include <kinputdialog.h>
#include <klocale.h>
#include <kaction.h>
#include <kdialogbase.h>
#include <kiconloader.h>
#include <kmessagebox.h>
#include <ktexteditor/viewcursorinterface.h>
#include <ktexteditor/encodinginterface.h>
#include <kparts/part.h>
#include <kxmlguibuilder.h>
#include <kdebug.h>
#include <kconfig.h>
#include <kapplication.h>
#include <kdevplugininfo.h>
#include <kdevgenericfactory.h>
#include <kdevpartcontroller.h>
#include <kdevproject.h>
#include <kdevcore.h>
#include <kdevmainwindow.h>
#include <configwidgetproxy.h>
typedef KDevGenericFactory<ProjectviewPart> projectviewFactory;
KDevPluginInfo data("kdevfilelist");
K_EXPORT_COMPONENT_FACTORY(libkdevfilelist, projectviewFactory(data))
#define GLOBALDOC_OPTIONS 1
#define PROJECTDOC_OPTIONS 2
ProjectviewPart::ProjectviewPart(QObject *parent, const char *name, const QStringList &/*args*/)
: KDevPlugin(&data, parent, name ? name : "FileListPart")
{
setInstance(projectviewFactory::instance());
setXMLFile("kdevfilelist.rc");
setupActions();
m_configProxy = new ConfigWidgetProxy(core());
m_configProxy->createGlobalConfigPage(i18n("File List"), GLOBALDOC_OPTIONS, info()->icon());
m_configProxy->createProjectConfigPage(i18n("File List"), PROJECTDOC_OPTIONS, info()->icon());
connect(m_configProxy, SIGNAL(insertConfigWidget(const KDialogBase*, QWidget*, unsigned int)),
this, SLOT(insertConfigWidget(const KDialogBase*, QWidget*, unsigned int)));
connect(core(), SIGNAL(contextMenu(QPopupMenu *, const Context *)),
this, SLOT(contextMenu(QPopupMenu *, const Context *)));
connect(core(), SIGNAL(projectOpened()), this, SLOT(projectOpened()));
connect(core(), SIGNAL(projectClosed()), this, SLOT(projectClosed()));
KConfig * config = kapp->config();
config->setGroup("File List Plugin");
if (config->readBoolEntry("ToolbarInToolview", true))
{
m_toolbarWidget = new QVBox(0, "toolbarContainer");
m_toolbarWidget->setHidden(true);
m_guibuilder = new ToolbarGUIBuilder(m_toolbarWidget, mainWindow()->main());
setClientBuilder(m_guibuilder);
}
m_restored = false;
QTimer::singleShot(0, this, SLOT(init()));
}
ProjectviewPart::~ProjectviewPart()
{
delete m_configProxy;
delete m_guibuilder;
if ( m_widget ) {
mainWindow()->removeView( m_widget );
delete m_widget; // deletes the children as well
}
}
void ProjectviewPart::restorePartialProjectSession(const QDomElement * el)
{
m_projectViews.clear(); // remove the global views
m_restored = true;
if (!el)
{
return;
}
// get the base of the project
QString urlStr = project()->projectDirectory();
if (KURL::isRelativeURL(urlStr))
{
m_projectBase.setProtocol("file");
m_projectBase.setPath(urlStr);
} else
{
m_projectBase = KURL::fromPathOrURL(urlStr);
}
m_projectBase.adjustPath(+1); // just in case
// read all the views
QDomNodeList domList = el->elementsByTagName("projectview");
uint len = domList.length();
for (uint i = 0; i < len; ++i)
{
const QDomElement viewEl = domList.item(i).toElement();
if (viewEl.isNull())
{
continue;
}
FileInfoList urlList;
QDomNodeList fileList = viewEl.elementsByTagName("file");
uint len2 = fileList.length();
for (uint i2 = 0; i2 < len2; ++i2)
{
const QDomElement fileEl = fileList.item(i2).toElement();
if (!fileEl.isNull())
{
bool ok;
int line = -1;
QString attr = fileEl.attribute("line");
if (! attr.isNull())
{
line = attr.toInt(&ok);
if (!ok)
line = -1;
}
int col = -1;
attr = fileEl.attribute("col");
if (! attr.isNull())
{
col = attr.toInt(&ok);
if (!ok)
col = -1;
}
QString encoding = "";
attr = fileEl.attribute("encoding");
if (! attr.isNull())
{
encoding = attr;
}
QString urlStr = fileEl.attribute("url");
if (KURL::isRelativeURL(urlStr))
{
KURL url = m_projectBase;
url.addPath(urlStr);
urlList.append(FileInfo(url, line, col, encoding));
} else
urlList.append(FileInfo(KURL::fromPathOrURL(urlStr), line, col, encoding));
}
}
m_projectViews.insert(viewEl.attribute("name"), urlList);
}
// read default view
domList = el->elementsByTagName("defaultview");
if (domList.length() > 0)
{
m_defaultProjectView = domList.item(0).toElement().attribute("name");
if (!m_defaultProjectView.isEmpty())
{
slotOpenProjectView(m_defaultProjectView);
}
} else
{
m_defaultProjectView = "";
}
}
void ProjectviewPart::savePartialProjectSession(QDomElement * el)
{
if (!el || m_projectViews.empty())
{
return;
}
QDomDocument domDoc = el->ownerDocument();
if (domDoc.isNull())
{
return;
}
// write all views
ViewMap::ConstIterator it;
for (it = m_projectViews.constBegin(); it != m_projectViews.constEnd(); ++it)
{
// we create the view even if there is no file inside, might be wanted by the user
QDomElement viewEl = domDoc.createElement("projectview");
viewEl.setAttribute("name", it.key());
el->appendChild(viewEl);
for (FileInfoList::ConstIterator it2 = it.data().constBegin(); it2 != it.data().constEnd(); ++it2)
{
QDomElement urlEl = domDoc.createElement("file");
if (m_projectBase.isParentOf((*it2).url))
urlEl.setAttribute("url", KURL::relativeURL(m_projectBase, (*it2).url));
else
urlEl.setAttribute("url", (*it2).url.url());
urlEl.setAttribute("line", (*it2).line);
urlEl.setAttribute("col", (*it2).col);
urlEl.setAttribute("encoding", (*it2).encoding);
viewEl.appendChild(urlEl);
}
}
// write the default view
QDomElement defaultEl = domDoc.createElement("defaultview");
defaultEl.setAttribute("name", m_defaultProjectView);
el->appendChild(defaultEl);
}
void ProjectviewPart::init()
{
// delayed initialization stuff goes here
// create the toolview
m_widget = new QWidget(0, "filelist widget");
m_widget->setIcon( SmallIcon(info()->icon()) );
m_widget->setCaption(i18n("File List"));
QBoxLayout * l = new QVBoxLayout(m_widget);
// create the toolbar if needed
if (m_guibuilder)
{
m_toolbarWidget->reparent(m_widget, QPoint(0, 0), true);
l->addWidget(m_toolbarWidget);
QWhatsThis::add(m_toolbarWidget, i18n("<b>View Session Toolbar</b><p>This allows to create and work with view sessions. A view session is a set of open documents.</p>"));
}
// create the listview
QWidget * fileListWidget = new FileListWidget(this, m_widget);
fileListWidget->setCaption(i18n("File List"));
QWhatsThis::add(fileListWidget, i18n("<b>File List</b><p>This is the list of opened files.</p>"));
l->addWidget(fileListWidget);
m_widget->setFocusProxy(fileListWidget);
mainWindow()->embedSelectView(m_widget, i18n("File List"), i18n("Open files"));
if (!project())
readConfig();
}
void ProjectviewPart::setupActions()
{
m_openPrjViewAction = new KSelectAction(i18n("Open Session..."), 0, actionCollection(), "viewsession_open");
connect(m_openPrjViewAction, SIGNAL(activated(const QString &)), this, SLOT(slotOpenProjectView(const QString &)));
m_openPrjViewAction->setToolTip(i18n("Open Session"));
m_savePrjViewAction = new KAction(i18n("Save Session"), "filesave", 0, this, SLOT(slotSaveProjectView()), actionCollection(), "viewsession_save");
m_newPrjViewAction = new KAction(i18n("New Session..."), "filenew", 0, this, SLOT(slotSaveAsProjectView()), actionCollection(), "viewsession_new");
m_deletePrjViewAction = new KSelectAction(i18n("Delete Session"), "editdelete", 0, actionCollection(), "viewsession_delete");
connect(m_deletePrjViewAction, SIGNAL(activated(const QString &)), this, SLOT(slotDeleteProjectView(const QString &)));
m_deletePrjViewAction->setToolTip(i18n("Delete Session"));
m_deleteCurrentPrjViewAction = new KAction(i18n("Delete Session"), "editdelete", 0, this, SLOT(slotDeleteProjectViewCurent()), actionCollection(), "viewsession_deletecurrent");
m_deleteCurrentPrjViewAction->setToolTip(i18n("Delete Session"));
adjustViewActions();
}
void ProjectviewPart::insertConfigWidget(const KDialogBase *dlg, QWidget *page, unsigned int pageNo)
{
// create configuraton dialogs here
switch (pageNo)
{
case PROJECTDOC_OPTIONS:
{
ProjectviewProjectConfig *w = new ProjectviewProjectConfig(this, page, "project config");
connect(dlg, SIGNAL(okClicked()), w, SLOT(accept()));
break;
}
case GLOBALDOC_OPTIONS:
{
ProjectviewConfig *w = new ProjectviewConfig(page, "global config");
connect(dlg, SIGNAL(okClicked()), w, SLOT(accept()));
break;
}
}
}
void ProjectviewPart::contextMenu(QPopupMenu */*popup*/, const Context */*context*/)
{
// put actions into the context menu here
// if (context->hasType(Context::EditorContext))
// {
// // editor context menu
// const EditorContext *econtext = static_cast<const EditorContext*>(context);
//
// // use context and plug actions here
// // action->plug(popup);
//
// // or create menu items on the fly
// // int id = -1;
// // id = popup->insertItem(i18n("Do Something Here"),
// // this, SLOT(doSomething()));
// // popup->setWhatsThis(id, i18n("<b>Do something here</b><p>Describe here what does this action do."
// }
// else if (context->hasType(Context::FileContext))
// {
// // file context menu
// const FileContext *fcontext = static_cast<const FileContext*>(context);
//
// //use context and plug actions here
// }
// else if (context->hasType(Context::ProjectModelItemContext))
// {
// // project tree context menu
// const ProjectModelItemContext *pcontext = static_cast<const ProjectModelItemContext*>(context);
//
// // use context and plug actions here
// }
// else if (context->hasType(Context::CodeModelItemContext))
// {
// // class tree context menu
// const CodeModelItemContext *mcontext = static_cast<const CodeModelItemContext*>(context);
//
// // use context and plug actions here
// }
// else if (context->hasType(Context::DocumentationContext))
// {
// // documentation viewer context menu
// const DocumentationContext *dcontext = static_cast<const DocumentationContext*>(context);
//
// // use context and plug actions here
// }
}
void ProjectviewPart::projectOpened()
{
if ( !m_restored )
m_projectViews.clear(); // remove the global views
adjustViewActions();
m_restored = false;
}
void ProjectviewPart::projectClosed()
{
m_projectBase = KURL();
m_defaultProjectView = "";
readConfig(); // read the global project views
}
void ProjectviewPart::slotOpenProjectView(const QString &view)
{
KConfig * config = kapp->config();
config->setGroup("File List Plugin");
bool onlyProject = config->readBoolEntry("OnlyProjectFiles", false);
bool closeOpenFiles = config->readBoolEntry("CloseOpenFiles", true);
m_currentProjectView = view;
if (m_projectViews.contains(view) > 0)
{
FileInfoList viewUrls = m_projectViews[view];
if (closeOpenFiles)
{
// we close everything that is not part of the project view
KURL::List urlsToClose = partController()->openURLs();
for (KURL::List::Iterator it = urlsToClose.begin(); it != urlsToClose.end(); ++it)
{
// it is in the list of wanted files and do we want it at all
if ((viewUrls.contains(*it) > 0) && (!onlyProject || !project() || project()->isProjectFile((*it).path()) ))
{
viewUrls.remove(*it); // don't open if it is open already
it = urlsToClose.remove(it);
--it; // do not skip one
}
}
if (!urlsToClose.empty())
{
partController()->closeFiles(urlsToClose);
}
}
// we open what still needs to get opened
FileInfoList::const_iterator viewIt;
for (viewIt = viewUrls.begin(); viewIt != viewUrls.end(); ++viewIt)
{
if (!onlyProject || !project() || project()->isProjectFile((*viewIt).url.path()))
{
partController()->setEncoding( (*viewIt).encoding );
partController()->editDocument((*viewIt).url, (*viewIt).line, (*viewIt).col);
}
}
}
adjustViewActions();
}
void ProjectviewPart::slotSaveProjectView()
{
slotSaveAsProjectView(m_currentProjectView.isEmpty());
}
void ProjectviewPart::adjustViewActions()
{
QStringList viewList = getViewList();
m_openPrjViewAction->clear();
m_openPrjViewAction->setItems(viewList);
int i = viewList.findIndex(m_currentProjectView);
if (i > -1)
{
m_openPrjViewAction->setCurrentItem(i);
}
m_deletePrjViewAction->clear();
m_deletePrjViewAction->setItems(viewList);
m_currentProjectView = m_openPrjViewAction->currentText();
if (m_currentProjectView.isEmpty() && !viewList.empty())
{
m_currentProjectView = viewList.front();
}
bool haveView = !m_currentProjectView.isEmpty();
m_savePrjViewAction->setEnabled(haveView);
m_deleteCurrentPrjViewAction->setEnabled(haveView);
}
void ProjectviewPart::slotDeleteProjectViewCurent()
{
slotDeleteProjectView(m_currentProjectView);
}
void ProjectviewPart::slotDeleteProjectView(const QString& view)
{
m_projectViews.remove(view);
if (m_currentProjectView == view)
m_currentProjectView = "";
if (m_defaultProjectView == view)
m_defaultProjectView = "";
if (! project())
writeConfig();
adjustViewActions();
}
void ProjectviewPart::slotSaveAsProjectView(bool askForName)
{
if (askForName)
{
bool ok;
QString newProjectView = KInputDialog::getText(i18n("Save View Session As"), i18n("Enter the name of the session:"), "", &ok, mainWindow()->main());
if (!ok)
{
return;
}
newProjectView = newProjectView.remove("="); // we use this string in config files and = would confuse it
if (m_projectViews.contains(newProjectView) > 0 &&
KMessageBox::warningContinueCancel(mainWindow()->main(), i18n("<qt>A view session named <b>%1</b> already exists.<br>Do you want to overwrite it?</qt>").arg(newProjectView), QString::null, i18n("Overwrite")) != KMessageBox::Continue)
{
return;
}
m_currentProjectView = newProjectView;
}
FileInfoList viewUrls;
KURL::List openURLs = partController()->openURLs();
for (KURL::List::Iterator it = openURLs.begin(); it != openURLs.end(); ++it)
{
// test if we have an editor
// FIXME this can fail if there are two parts with the same URL
KParts::ReadOnlyPart *ro_part = partController()->partForURL(*it);
KTextEditor::ViewCursorInterface* cursorIf = dynamic_cast<KTextEditor::ViewCursorInterface*>(ro_part->widget());
if (cursorIf)
{
QString encoding;
if ( KTextEditor::EncodingInterface * ei = dynamic_cast<KTextEditor::EncodingInterface*>( ro_part ) )
{
QString temp = ei->encoding();
if ( !temp.isNull() )
{
encoding = temp;
}
}
unsigned int line, col;
cursorIf->cursorPositionReal(&line, &col);
viewUrls.append(FileInfo(*it, line, col, encoding));
}
}
// add or overwrite the values
m_projectViews.insert(m_currentProjectView, viewUrls, true);
if (! project())
writeConfig();
adjustViewActions();
}
void ProjectviewPart::writeConfig()
{
KConfig * config = kapp->config();
config->deleteGroup("ProjectViews", true);
config->setGroup("ProjectViews");
// write all views
ViewMap::ConstIterator it;
for (it = m_projectViews.constBegin(); it != m_projectViews.constEnd(); ++it)
{
// we create the view even if there is no file inside, might be wanted by the user
QStringList urls;
for (FileInfoList::ConstIterator it2 = it.data().constBegin(); it2 != it.data().constEnd(); ++it2)
{
if ((*it2).encoding.isEmpty())
urls.append((*it2).url.url());
else
urls.append((*it2).url.url() + ";" + (*it2).encoding);
}
config->writeEntry(it.key(), urls);
}
}
void ProjectviewPart::readConfig()
{
KConfig * config = kapp->config();
QMap<QString, QString> entries = config->entryMap("ProjectViews");
m_projectViews.clear();
QMap<QString, QString>::ConstIterator it;
for (it = entries.constBegin(); it != entries.constEnd(); ++it)
{
FileInfoList urlList;
QStringList urls = QStringList::split(",", it.data());
for (QStringList::Iterator it2 = urls.begin(); it2 != urls.end(); ++it2 )
{
// search the encoding. The entry can be like: fileURL;encoding
QStringList file = QStringList::split(";", *it2);
if (file.count() == 1)
urlList.append(FileInfo(KURL::fromPathOrURL(*it2)));
else
urlList.append(FileInfo(KURL::fromPathOrURL(file.first()), -1, -1, file.last()));
}
m_projectViews.insert(it.key(), urlList);
}
adjustViewActions();
}
#include "projectviewpart.moc"