/*************************************************************************** * Copyright (C) 2004 by Alexander Dymo * * cloudtemple@mksat.net * * * * 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., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include "documentation_part.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kdevplugininfo.h" #include "kdevcore.h" #include "kdevproject.h" #include "kdevmainwindow.h" #include "kdevgenericfactory.h" #include "kdevdocumentationplugin.h" #include "configwidgetproxy.h" #include "kdevpartcontroller.h" #include "domutil.h" #include "urlutil.h" #include "kdeveditorutil.h" #include "documentation_widget.h" #include "docglobalconfigwidget.h" #include "docprojectconfigwidget.h" #include "contentsview.h" #include "find_documentation.h" #include "KDevDocumentationIface.h" #define GLOBALDOC_OPTIONS 1 #define PROJECTDOC_OPTIONS 2 static const KDevPluginInfo data("kdevdocumentation"); typedef KDevGenericFactory DocumentationFactory; K_EXPORT_COMPONENT_FACTORY( libkdevdocumentation, DocumentationFactory( data ) ) DocumentationPart::DocumentationPart(QObject *parent, const char *name, const QStringList& ) :KDevPlugin(&data, parent, name ? name : "DocumentationPart" ), m_projectDocumentationPlugin(0), m_userManualPlugin(0), m_hasIndex(false) { setInstance(DocumentationFactory::instance()); setXMLFile("kdevpart_documentation.rc"); m_configProxy = new ConfigWidgetProxy(core()); m_configProxy->createGlobalConfigPage(i18n("Documentation"), GLOBALDOC_OPTIONS, info()->icon() ); m_configProxy->createProjectConfigPage(i18n("Project Documentation"), 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())); m_widget = new DocumentationWidget(this); m_widget->setIcon(SmallIcon( info()->icon() )); m_widget->setCaption(i18n("Documentation")); QWhatsThis::add(m_widget, i18n("Documentation browser

" "The documentation browser gives access to various " "documentation sources (Qt DCF, Doxygen, KDoc, KDevelopTOC and DevHelp " "documentation) and the KDevelop manuals. It also provides documentation index " "and full text search capabilities.")); mainWindow()->embedSelectViewRight(m_widget, i18n("Documentation"), i18n("Documentation browser")); setupActions(); QTimer::singleShot(0, this, SLOT(init())); new KDevDocumentationIface(this); } DocumentationPart::~DocumentationPart() { if ( m_widget ) { mainWindow()->removeView( m_widget ); } delete m_widget; delete m_configProxy; } void DocumentationPart::loadDocumentationPlugins() { KTrader::OfferList docPluginOffers = KTrader::self()->query(QString::fromLatin1("KDevelop/DocumentationPlugins"), QString("[X-KDevelop-Version] == %1").arg(KDEVELOP_PLUGIN_VERSION)); KTrader::OfferList::ConstIterator serviceIt = docPluginOffers.begin(); for ( ; serviceIt != docPluginOffers.end(); ++serviceIt ) { KService::Ptr docPluginService; docPluginService = *serviceIt; kdDebug() << "DocumentationPart::loadDocumentationPlugins: creating plugin" << docPluginService->name() << endl; int error; DocumentationPlugin *docPlugin = KParts::ComponentFactory ::createInstanceFromService(docPluginService, 0, docPluginService->name().latin1(), QStringList(), &error); if (!docPlugin) kdDebug() << " failed to create doc plugin " << docPluginService->name() << endl; else { kdDebug() << " success" << endl; docPlugin->init(m_widget->contents()); connect(this, SIGNAL(indexSelected(IndexBox* )), docPlugin, SLOT(createIndex(IndexBox* ))); m_plugins.append(docPlugin); } } } void DocumentationPart::emitIndexSelected(IndexBox *indexBox) { if (!m_hasIndex) { QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); emit indexSelected(indexBox); indexBox->fill(); m_hasIndex = true; QApplication::restoreOverrideCursor(); } } void DocumentationPart::insertConfigWidget(const KDialogBase *dlg, QWidget *page, unsigned int pageNo) { switch (pageNo) { case GLOBALDOC_OPTIONS: { DocGlobalConfigWidget *w1 = new DocGlobalConfigWidget(this, m_widget, page, "doc config widget"); connect(dlg, SIGNAL(okClicked()), w1, SLOT(accept())); break; } case PROJECTDOC_OPTIONS: { DocProjectConfigWidget *w1 = new DocProjectConfigWidget(this, page, "doc project config"); connect(dlg, SIGNAL(okClicked()), w1, SLOT(accept())); break; } } } KConfig *DocumentationPart::config() { return DocumentationFactory::instance()->config(); } bool DocumentationPart::configure(int page) { KDialogBase dlg(KDialogBase::Plain, i18n("Documentation Settings"), KDialogBase::Ok|KDialogBase::Cancel, KDialogBase::Ok, m_widget, "docsettings dialog"); QVBoxLayout *l = new QVBoxLayout(dlg.plainPage(), 0, 0); DocGlobalConfigWidget *w1 = new DocGlobalConfigWidget(this, m_widget, dlg.plainPage()); l->addWidget(w1); w1->docTab->setCurrentPage(page); connect(&dlg, SIGNAL(okClicked()), w1, SLOT(accept())); return (dlg.exec() == QDialog::Accepted); } void DocumentationPart::setupActions() { /* KActionMenu *menu = new KActionMenu(i18n("&Bookmarks"), actionCollection(), "doc_bookmarks"); KBookmarkMenu *bm = new KBookmarkMenu(new DocBookmarkManager(this), new DocBookmarkOwner(this), menu->popupMenu(), actionCollection(), true);*/ KAction *action; action = new KAction(i18n("&Search in Documentation..."), CTRL+ALT+Key_S, this, SLOT(searchInDocumentation()), actionCollection(), "help_search_in_doc" ); action->setToolTip(i18n("Full text search in the documentation")); action->setWhatsThis(i18n("Search in documentation

" "Opens the Search in documentation tab. It allows " "a search term to be entered which will be searched for in " "the documentation. For this to work, a " "full text index must be created first, which can be done in the " "configuration dialog of the documentation plugin.")); action = new KAction(i18n("&Look in Documentation Index..."), CTRL+ALT+Key_I, this, SLOT(lookInDocumentationIndex()), actionCollection(), "help_look_in_index" ); action->setToolTip(i18n("Look in the documentation index")); action->setWhatsThis(i18n("Look in documentation index

" "Opens the documentation index tab. It allows " "a term to be entered which will be looked for in " "the documentation index.")); action = new KAction(i18n("Man Page..."), 0, this, SLOT(manPage()), actionCollection(), "help_manpage" ); action->setToolTip(i18n("Show a manpage")); action->setWhatsThis(i18n("Show a manpage

Opens a man page using embedded viewer.")); action = new KAction(i18n("Info Page..."), 0, this, SLOT(infoPage()), actionCollection(), "help_infopage"); action->setToolTip(i18n("Show an infopage")); action->setWhatsThis(i18n("Show an infopage

Opens an info page using embedded viewer.")); action = new KAction(i18n("Find Documentation..."), 0, this, SLOT(findInDocumentation()), actionCollection(), "help_find_documentation"); action->setToolTip(i18n("Find Documentation")); action->setWhatsThis(i18n("Find documentation

" "Opens the documentation finder tab and searches " "all possible sources of documentation like " "table of contents, index, man and info databases, " "Google, etc.")); } void DocumentationPart::emitBookmarkLocation(const QString &title, const KURL &url) { emit bookmarkLocation(title, url); } void DocumentationPart::searchInDocumentation() { QString word = KDevEditorUtil::currentWord( dynamic_cast( partController()->activePart() ) ); if ( word.isEmpty() ) { if ( isAssistantUsed() ) callAssistant ( "KDevDocumentation", "searchInDocumentation()" ); else { mainWindow()->raiseView ( m_widget ); m_widget->searchInDocumentation(); } } else { if ( isAssistantUsed() ) callAssistant ( "KDevDocumentation", "searchInDocumentation(QString)", word ); else { mainWindow()->raiseView ( m_widget ); m_widget->searchInDocumentation ( word ); } } } void DocumentationPart::searchInDocumentation(const QString &term) { mainWindow()->raiseView(m_widget); m_widget->searchInDocumentation(term); } void DocumentationPart::contextSearchInDocumentation() { if (isAssistantUsed()) callAssistant("KDevDocumentation", "searchInDocumentation(QString)", m_contextStr); else searchInDocumentation(m_contextStr); } void DocumentationPart::manPage() { QString word = KDevEditorUtil::currentWord( dynamic_cast( partController()->activePart() ) ); if ( isAssistantUsed() ) { if ( word.isEmpty() ) { callAssistant ( "KDevDocumentation", "manPage()" ); } else { callAssistant ( "KDevDocumentation", "manPage(QString)", word ); } } else { bool ok; QString manpage = KInputDialog::getText ( i18n ( "Show Manual Page" ), i18n ( "Show manpage on:" ), word, &ok, 0 ); if ( ok && !manpage.isEmpty() ) manPage ( manpage ); } } void DocumentationPart::infoPage() { QString word = KDevEditorUtil::currentWord( dynamic_cast( partController()->activePart() ) ); if ( isAssistantUsed() ) { if ( word.isEmpty() ) { callAssistant ( "KDevDocumentation", "infoPage()" ); } else { callAssistant ( "KDevDocumentation", "infoPage(QString)", word ); } } else { bool ok; QString infopage = KInputDialog::getText ( i18n ( "Show Info Page" ), i18n ( "Show infopage on:" ), word, &ok, 0 ); if ( ok && !infopage.isEmpty() ) infoPage ( infopage ); } } void DocumentationPart::manPage(const QString &term) { QString url = QString::fromLatin1("man:/%1").arg(term); partController()->showDocument(KURL(url)); } void DocumentationPart::infoPage(const QString &term) { QString url = QString::fromLatin1("info:/%1").arg(term); partController()->showDocument(KURL(url)); } void DocumentationPart::contextManPage() { if (isAssistantUsed()) callAssistant("KDevDocumentation", "manPage(QString)", m_contextStr); else manPage(m_contextStr); } void DocumentationPart::contextInfoPage() { if (isAssistantUsed()) callAssistant("KDevDocumentation", "infoPage(QString)", m_contextStr); else infoPage(m_contextStr); } void DocumentationPart::contextFindDocumentation() { if (isAssistantUsed()) callAssistant("KDevDocumentation", "findInFinder(QString)", m_contextStr); else findInDocumentation(m_contextStr); } void DocumentationPart::findInDocumentation() { QString word = KDevEditorUtil::currentWord( dynamic_cast( partController()->activePart() ) ); if ( word.isEmpty() ) { if ( isAssistantUsed() ) callAssistant ( "KDevDocumentation", "findInFinder()" ); else { mainWindow()->raiseView ( m_widget ); m_widget->findInDocumentation(); } } else { if ( isAssistantUsed() ) callAssistant ( "KDevDocumentation", "findInFinder(QString)", word ); else { mainWindow()->raiseView ( m_widget ); m_widget->findInDocumentation ( word ); } } } void DocumentationPart::findInDocumentation(const QString &term) { mainWindow()->raiseView(m_widget); m_widget->findInDocumentation(term); } void DocumentationPart::lookInDocumentationIndex() { QString word = KDevEditorUtil::currentWord( dynamic_cast( partController()->activePart() ) ); if ( word.isEmpty() ) { if ( isAssistantUsed() ) callAssistant ( "KDevDocumentation", "lookupInIndex()" ); else { mainWindow()->raiseView ( m_widget ); m_widget->lookInDocumentationIndex(); } } else { if ( isAssistantUsed() ) callAssistant ( "KDevDocumentation", "lookupInIndex(QString)", word ); else { mainWindow()->raiseView ( m_widget ); m_widget->lookInDocumentationIndex ( word ); } } } void DocumentationPart::lookInDocumentationIndex(const QString &term) { mainWindow()->raiseView(m_widget); m_widget->lookInDocumentationIndex(term); } void DocumentationPart::contextLookInDocumentationIndex() { if (isAssistantUsed()) callAssistant("KDevDocumentation", "lookupInIndex(QString)", m_contextStr); else lookInDocumentationIndex(m_contextStr); } void DocumentationPart::contextMenu(QPopupMenu *popup, const Context *context) { if (context->hasType(Context::EditorContext)) { const EditorContext *econtext = static_cast(context); QString ident = econtext->currentWord(); if (!ident.isEmpty()) { m_contextStr = ident; QString squeezed = KStringHandler::csqueeze(m_contextStr, 30); int id = -1; if (hasContextFeature(Finder)) { id = popup->insertItem(i18n("Find Documentation: %1").arg(squeezed), this, SLOT(contextFindDocumentation())); popup->setWhatsThis(id, i18n("Find documentation

" "Opens the documentation finder tab and searches " "all possible sources of documentation like " "table of contents, index, man and info databases, " "Google, etc.")); } if (hasContextFeature(IndexLookup)) { id = popup->insertItem(i18n("Look in Documentation Index: %1").arg(squeezed), this, SLOT(contextLookInDocumentationIndex())); popup->setWhatsThis(id, i18n("Look in documentation index

" "Opens the documentation index tab. It allows " "a term to be entered which will be looked for in " "the documentation index.")); } if (hasContextFeature(FullTextSearch)) { id = popup->insertItem(i18n("Search in Documentation: %1").arg(squeezed), this, SLOT(contextSearchInDocumentation())); popup->setWhatsThis(id, i18n("Search in documentation

Searches " "for a term under the cursor in " "the documentation. For this to work, " "a full text index must be created first, which can be done in the " "configuration dialog of the documentation plugin.")); } if (hasContextFeature(GotoMan)) { id = popup->insertItem(i18n("Goto Manpage: %1").arg(squeezed), this, SLOT(contextManPage())); popup->setWhatsThis(id, i18n("Goto manpage

Tries to open a man page for the term under the cursor.")); } if (hasContextFeature(GotoInfo)) { id = popup->insertItem( i18n("Goto Infopage: %1").arg(squeezed), this, SLOT(contextInfoPage()) ); popup->setWhatsThis(id, i18n("Goto infopage

Tries to open an info page for the term under the cursor.")); } if (id != -1) popup->insertSeparator(); } } } bool DocumentationPart::hasContextFeature(ContextFeature feature) { KConfig *config = DocumentationFactory::instance()->config(); QString group = config->group(); config->setGroup("Context Features"); switch (feature) { case Finder: return config->readBoolEntry("Finder", true); break; case IndexLookup: return config->readBoolEntry("IndexLookup", false); break; case FullTextSearch: return config->readBoolEntry("FullTextSearch", true); break; case GotoMan: return config->readBoolEntry("GotoMan", false); break; case GotoInfo: return config->readBoolEntry("GotoInfo", false); break; } config->setGroup(group); return false; } void DocumentationPart::setContextFeature(ContextFeature feature, bool b) { KConfig *config = DocumentationFactory::instance()->config(); QString group = config->group(); config->setGroup("Context Features"); QString key; switch (feature) { case Finder: key = "Finder"; break; case IndexLookup: key = "IndexLookup"; break; case FullTextSearch: key = "FullTextSearch"; break; case GotoMan: key = "GotoMan"; break; case GotoInfo: key = "GotoInfo"; break; } if (!key.isEmpty()) config->writeEntry(key, b); config->setGroup(group); } void DocumentationPart::projectOpened() { QString projectDocSystem = DomUtil::readEntry(*(projectDom()), "/kdevdocumentation/projectdoc/docsystem"); QString projectDocURL = DomUtil::readEntry(*(projectDom()), "/kdevdocumentation/projectdoc/docurl"); if (!projectDocURL.isEmpty()) projectDocURL = QDir::cleanDirPath(project()->projectDirectory() + "/" + projectDocURL); QString userManualURL = DomUtil::readEntry(*(projectDom()), "/kdevdocumentation/projectdoc/usermanualurl"); for (QValueList::const_iterator it = m_plugins.constBegin(); it != m_plugins.constEnd(); ++it) { if ((*it)->hasCapability(DocumentationPlugin::ProjectDocumentation) && ((*it)->pluginName() == projectDocSystem)) m_projectDocumentationPlugin = (*it)->projectDocumentationPlugin(DocumentationPlugin::APIDocs); if ((*it)->hasCapability(DocumentationPlugin::ProjectUserManual)) { kdDebug() << "creating user manual for type: " << DocumentationPlugin::UserManual << endl; m_userManualPlugin = (*it)->projectDocumentationPlugin(DocumentationPlugin::UserManual); } } if (m_projectDocumentationPlugin) m_projectDocumentationPlugin->init(m_widget->contents(), m_widget->index(), projectDocURL); if (m_userManualPlugin && !userManualURL.isEmpty()) m_userManualPlugin->init(m_widget->contents(), m_widget->index(), userManualURL); } void DocumentationPart::projectClosed() { // saveProjectDocumentationInfo(); delete m_projectDocumentationPlugin; m_projectDocumentationPlugin = 0; delete m_userManualPlugin; m_userManualPlugin = 0; } void DocumentationPart::saveProjectDocumentationInfo() { if (m_projectDocumentationPlugin) { DomUtil::writeEntry(*(projectDom()), "/kdevdocumentation/projectdoc/docsystem", m_projectDocumentationPlugin->pluginName()); QString relPath = URLUtil::extractPathNameRelative(project()->projectDirectory(), m_projectDocumentationPlugin->catalogURL()); DomUtil::writeEntry(*(projectDom()), "/kdevdocumentation/projectdoc/docurl", relPath); } else { DomUtil::writeEntry(*(projectDom()), "/kdevdocumentation/projectdoc/docsystem", ""); DomUtil::writeEntry(*(projectDom()), "/kdevdocumentation/projectdoc/docurl", ""); } if (m_userManualPlugin) DomUtil::writeEntry(*(projectDom()), "/kdevdocumentation/projectdoc/usermanualurl", m_userManualPlugin->catalogURL()); else DomUtil::writeEntry(*(projectDom()), "/kdevdocumentation/projectdoc/usermanualurl", ""); } QCString DocumentationPart::startAssistant() { static QCString lastAssistant = ""; if (!lastAssistant.isEmpty() && KApplication::dcopClient()->isApplicationRegistered(lastAssistant)) return lastAssistant; const char *function = 0; QString app = "kdevassistant"; function = "start_service_by_desktop_name(QString,QStringList)"; QStringList URLs; QByteArray data, replyData; QCString replyType; QDataStream arg(data, IO_WriteOnly); arg << app << URLs; if (!KApplication::dcopClient()->call("klauncher", "klauncher", function, data, replyType, replyData)) { kdDebug() << "call failed" << endl; lastAssistant = ""; } else { QDataStream reply(replyData, IO_ReadOnly); if ( replyType != "serviceResult" ) { kdDebug() << "unexpected result: " << replyType.data() << endl; lastAssistant = ""; } int result; QCString dcopName; QString error; reply >> result >> dcopName >> error; if (result != 0) { kdDebug() << "Error: " << error << endl; lastAssistant = ""; } if (!dcopName.isEmpty()) { lastAssistant = dcopName; kdDebug() << dcopName.data() << endl; //@fixme: is there another way to wait for the remote object to be loaded while (!KApplication::dcopClient()->remoteObjects(dcopName).contains("KDevDocumentation")) usleep(500); } } return lastAssistant; } bool DocumentationPart::isAssistantUsed() const { // hack to solve BR #90334 - don't call kdevassistant via DCOP if we ARE kdevassistant if ( kapp->instanceName().find("kdevassistant") != -1 ) { return false; } return m_assistantUsed; } void DocumentationPart::setAssistantUsed(bool b) { m_assistantUsed = b; //use global config to store different settings for kdevassistant and kdevelop KConfig *config = kapp->config(); config->setGroup("Documentation"); config->writeEntry("UseAssistant", isAssistantUsed()); } void DocumentationPart::activateAssistantWindow(const QCString &ref) { kdDebug() << "DocumentationPart::activateAssistantWindow" << endl; QByteArray data, replyData; QCString replyType; if (KApplication::dcopClient()->call(ref, "MainWindow", "getWinID()", data, replyType, replyData)) { kdDebug() << " call successful " << endl; QDataStream reply(replyData, IO_ReadOnly); int winId; reply >> winId; kdDebug() << "Win ID: " << winId << endl; KWin::forceActiveWindow(winId); KApplication::dcopClient()->send(ref, "MainWindow", "show()", QByteArray()); } } void DocumentationPart::callAssistant(const QCString &interface, const QCString &method) { QCString ref = startAssistant(); QByteArray data; if (KApplication::dcopClient()->send(ref, interface, method, data)) activateAssistantWindow(ref); else kdDebug() << "problem communicating with: " << ref; } void DocumentationPart::callAssistant(const QCString &interface, const QCString &method, const QString &dataStr) { QCString ref = startAssistant(); QByteArray data; QDataStream arg(data, IO_WriteOnly); arg << dataStr; if (KApplication::dcopClient()->send(ref, interface, method, data)) activateAssistantWindow(ref); else kdDebug() << "problem communicating with: " << ref; } void DocumentationPart::loadSettings() { KConfig *config = kapp->config(); config->setGroup("Documentation"); m_assistantUsed = config->readBoolEntry("UseAssistant", false); if (QString(KGlobal::instance()->aboutData()->appName()) == "kdevassistant") { int page = config->readNumEntry("LastPage", 0); switch (page) { case 1: lookInDocumentationIndex(); break; case 2: findInDocumentation(); break; case 3: searchInDocumentation(); break; } } } void DocumentationPart::init( ) { loadDocumentationPlugins(); loadSettings(); } #include "documentation_part.moc"