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.
586 lines
20 KiB
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 <tqtimer.h>
|
|
#include <tqpopupmenu.h>
|
|
#include <tqwhatsthis.h>
|
|
#include <tqvaluelist.h>
|
|
#include <tqmap.h>
|
|
#include <tqdir.h>
|
|
#include <tqdom.h>
|
|
#include <tqvbox.h>
|
|
#include <tqhbox.h>
|
|
#include <tqpoint.h>
|
|
#include <tqlayout.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(TQObject *parent, const char *name, const TQStringList &/*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, TQT_SIGNAL(insertConfigWidget(const KDialogBase*, TQWidget*, unsigned int)),
|
|
this, TQT_SLOT(insertConfigWidget(const KDialogBase*, TQWidget*, unsigned int)));
|
|
|
|
connect(core(), TQT_SIGNAL(contextMenu(TQPopupMenu *, const Context *)),
|
|
this, TQT_SLOT(contextMenu(TQPopupMenu *, const Context *)));
|
|
|
|
connect(core(), TQT_SIGNAL(projectOpened()), this, TQT_SLOT(projectOpened()));
|
|
connect(core(), TQT_SIGNAL(projectClosed()), this, TQT_SLOT(projectClosed()));
|
|
|
|
KConfig * config = kapp->config();
|
|
config->setGroup("File List Plugin");
|
|
if (config->readBoolEntry("ToolbarInToolview", true))
|
|
{
|
|
m_toolbarWidget = new TQVBox(0, "toolbarContainer");
|
|
m_toolbarWidget->setHidden(true);
|
|
m_guibuilder = new ToolbarGUIBuilder(m_toolbarWidget, mainWindow()->main());
|
|
setClientBuilder(m_guibuilder);
|
|
}
|
|
m_restored = false;
|
|
TQTimer::singleShot(0, this, TQT_SLOT(init()));
|
|
}
|
|
|
|
ProjectviewPart::~ProjectviewPart()
|
|
{
|
|
delete m_configProxy;
|
|
delete m_guibuilder;
|
|
if ( m_widget ) {
|
|
mainWindow()->removeView( m_widget );
|
|
delete m_widget; // deletes the tqchildren as well
|
|
}
|
|
}
|
|
|
|
void ProjectviewPart::restorePartialProjectSession(const TQDomElement * el)
|
|
{
|
|
m_projectViews.clear(); // remove the global views
|
|
m_restored = true;
|
|
if (!el)
|
|
{
|
|
return;
|
|
}
|
|
// get the base of the project
|
|
TQString 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
|
|
TQDomNodeList domList = el->elementsByTagName("projectview");
|
|
|
|
uint len = domList.length();
|
|
for (uint i = 0; i < len; ++i)
|
|
{
|
|
const TQDomElement viewEl = domList.item(i).toElement();
|
|
if (viewEl.isNull())
|
|
{
|
|
continue;
|
|
}
|
|
FileInfoList urlList;
|
|
TQDomNodeList fileList = viewEl.elementsByTagName("file");
|
|
|
|
uint len2 = fileList.length();
|
|
for (uint i2 = 0; i2 < len2; ++i2)
|
|
{
|
|
const TQDomElement fileEl = fileList.item(i2).toElement();
|
|
if (!fileEl.isNull())
|
|
{
|
|
bool ok;
|
|
int line = -1;
|
|
TQString 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;
|
|
}
|
|
TQString encoding = "";
|
|
attr = fileEl.attribute("encoding");
|
|
if (! attr.isNull())
|
|
{
|
|
encoding = attr;
|
|
}
|
|
TQString 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(TQDomElement * el)
|
|
{
|
|
if (!el || m_projectViews.empty())
|
|
{
|
|
return;
|
|
}
|
|
TQDomDocument 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
|
|
TQDomElement viewEl = domDoc.createElement("projectview");
|
|
viewEl.setAttribute("name", it.key());
|
|
el->appendChild(viewEl);
|
|
for (FileInfoList::ConstIterator it2 = it.data().constBegin(); it2 != it.data().constEnd(); ++it2)
|
|
{
|
|
TQDomElement 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
|
|
TQDomElement 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 TQWidget(0, "filelist widget");
|
|
m_widget->setIcon( SmallIcon(info()->icon()) );
|
|
m_widget->setCaption(i18n("File List"));
|
|
|
|
TQBoxLayout * l = new TQVBoxLayout(m_widget);
|
|
|
|
// create the toolbar if needed
|
|
if (m_guibuilder)
|
|
{
|
|
m_toolbarWidget->reparent(m_widget, TQPoint(0, 0), true);
|
|
l->addWidget(m_toolbarWidget);
|
|
TQWhatsThis::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
|
|
TQWidget * fileListWidget = new FileListWidget(this, m_widget);
|
|
fileListWidget->setCaption(i18n("File List"));
|
|
TQWhatsThis::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, TQT_SIGNAL(activated(const TQString &)), this, TQT_SLOT(slotOpenProjectView(const TQString &)));
|
|
|
|
m_openPrjViewAction->setToolTip(i18n("Open Session"));
|
|
|
|
m_savePrjViewAction = new KAction(i18n("Save Session"), "filesave", 0, this, TQT_SLOT(slotSaveProjectView()), actionCollection(), "viewsession_save");
|
|
|
|
m_newPrjViewAction = new KAction(i18n("New Session..."), "filenew", 0, this, TQT_SLOT(slotSaveAsProjectView()), actionCollection(), "viewsession_new");
|
|
|
|
m_deletePrjViewAction = new KSelectAction(i18n("Delete Session"), "editdelete", 0, actionCollection(), "viewsession_delete");
|
|
|
|
connect(m_deletePrjViewAction, TQT_SIGNAL(activated(const TQString &)), this, TQT_SLOT(slotDeleteProjectView(const TQString &)));
|
|
|
|
m_deletePrjViewAction->setToolTip(i18n("Delete Session"));
|
|
|
|
m_deleteCurrentPrjViewAction = new KAction(i18n("Delete Session"), "editdelete", 0, this, TQT_SLOT(slotDeleteProjectViewCurent()), actionCollection(), "viewsession_deletecurrent");
|
|
|
|
m_deleteCurrentPrjViewAction->setToolTip(i18n("Delete Session"));
|
|
|
|
adjustViewActions();
|
|
}
|
|
|
|
void ProjectviewPart::insertConfigWidget(const KDialogBase *dlg, TQWidget *page, unsigned int pageNo)
|
|
{
|
|
// create configuraton dialogs here
|
|
switch (pageNo)
|
|
{
|
|
case PROJECTDOC_OPTIONS:
|
|
{
|
|
ProjectviewProjectConfig *w = new ProjectviewProjectConfig(this, page, "project config");
|
|
connect(dlg, TQT_SIGNAL(okClicked()), w, TQT_SLOT(accept()));
|
|
break;
|
|
}
|
|
case GLOBALDOC_OPTIONS:
|
|
{
|
|
ProjectviewConfig *w = new ProjectviewConfig(page, "global config");
|
|
connect(dlg, TQT_SIGNAL(okClicked()), w, TQT_SLOT(accept()));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ProjectviewPart::contextMenu(TQPopupMenu */*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, TQT_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 TQString &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()
|
|
{
|
|
TQStringList 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 TQString& 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;
|
|
TQString 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>").tqarg(newProjectView), TQString(), 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)
|
|
{
|
|
TQString encoding;
|
|
if ( KTextEditor::EncodingInterface * ei = dynamic_cast<KTextEditor::EncodingInterface*>( ro_part ) )
|
|
{
|
|
TQString 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
|
|
TQStringList 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();
|
|
TQMap<TQString, TQString> entries = config->entryMap("ProjectViews");
|
|
|
|
m_projectViews.clear();
|
|
TQMap<TQString, TQString>::ConstIterator it;
|
|
for (it = entries.constBegin(); it != entries.constEnd(); ++it)
|
|
{
|
|
FileInfoList urlList;
|
|
TQStringList urls = TQStringList::split(",", it.data());
|
|
for (TQStringList::Iterator it2 = urls.begin(); it2 != urls.end(); ++it2 )
|
|
{
|
|
// search the encoding. The entry can be like: fileURL;encoding
|
|
TQStringList file = TQStringList::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"
|