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/documentation/interfaces/kdevdocumentationplugin.cpp

759 lines
23 KiB

/* This file is part of the KDE project
Copyright (C) 2004 by Alexander Dymo <cloudtemple@mksat.net>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "kdevdocumentationplugin.h"
#include <tqfile.h>
#include <tqpainter.h>
#include <tqstyle.h>
#include <tqheader.h>
#include <tqtextstream.h>
#include <kstandarddirs.h>
#include <kiconloader.h>
#include <tdeconfig.h>
#include <kdebug.h>
#include <kdirwatch.h>
#include <tdelocale.h>
//class DocumentationItem
DocumentationItem::DocumentationItem(DocumentationItem::Type type, TDEListView *parent,
const TQString &name)
:TDEListViewItem(parent, name), m_type(type)
{
init();
}
DocumentationItem::DocumentationItem(DocumentationItem::Type type, TDEListViewItem *parent,
const TQString &name)
:TDEListViewItem(parent, name), m_type(type)
{
init();
}
DocumentationItem::DocumentationItem(DocumentationItem::Type type, TDEListView *parent,
TDEListViewItem *after, const TQString &name)
:TDEListViewItem(parent, after, name), m_type(type)
{
init();
}
DocumentationItem::DocumentationItem(DocumentationItem::Type type, TDEListViewItem * parent,
TDEListViewItem * after, const TQString & name )
:TDEListViewItem(parent, after, name), m_type(type)
{
init();
}
void DocumentationItem::init( )
{
TQString icon;
switch (m_type)
{
case Collection:
case Catalog:
icon = "folder";
break;
case Book:
icon = "contents";
break;
default:
icon = "text-x-generic";
}
setPixmap(0, SmallIcon(icon));
}
//----------------------------------------------------
//class DocumentationCatalogItem
DocumentationCatalogItem::DocumentationCatalogItem(DocumentationPlugin* plugin,
TDEListView *parent, TDEListViewItem *after, const TQString &name)
:DocumentationItem(DocumentationItem::Catalog, parent, after, name), m_plugin(plugin),
isLoaded(false), isActivated(false), m_isProjectDocumentationItem(false)
{
setExpandable(true);
m_plugin->addCatalog(this);
}
DocumentationCatalogItem::DocumentationCatalogItem(DocumentationPlugin* plugin,
DocumentationItem *parent, const TQString &name)
:DocumentationItem(DocumentationItem::Catalog, parent, name), m_plugin(plugin),
isLoaded(false), isActivated(false), m_isProjectDocumentationItem(false)
{
setExpandable(true);
m_plugin->addCatalog(this);
}
DocumentationCatalogItem::~DocumentationCatalogItem()
{
m_plugin->clearCatalog(this);
}
void DocumentationCatalogItem::setOpen(bool o)
{
if (o)
{
load();
}
DocumentationItem::setOpen(o);
}
void DocumentationCatalogItem::load()
{
if(isLoaded)
return;
plugin()->createTOC(this);
isLoaded = true;
}
void DocumentationCatalogItem::activate()
{
if (!isActivated)
{
plugin()->setCatalogURL(this);
isActivated = true;
}
DocumentationItem::activate();
}
//----------------------------------------------------
//class IndexItemProto
IndexItemProto::IndexItemProto(DocumentationPlugin *plugin, DocumentationCatalogItem *catalog,
IndexBox *listbox, const TQString &text, const TQString &description)
: m_listbox(listbox), m_text(text), m_description(description)
{
plugin->indexes[catalog].append(this);
m_listbox->addIndexItem(this);
}
IndexItemProto::~IndexItemProto()
{
m_listbox->removeIndexItem(this);
}
//----------------------------------------------------
//class IndexItem
IndexItem::IndexItem(IndexBox *listbox, const TQString &text)
:TQListBoxText(listbox, text), m_listbox(listbox)
{
}
IndexItem::List IndexItem::urls() const
{
List urlList;
TQValueList<IndexItemProto*> itemProtos = m_listbox->items[text()];
for (TQValueList<IndexItemProto*>::const_iterator it = itemProtos.begin();
it != itemProtos.end(); ++it)
urlList.append(tqMakePair((*it)->description(), (*it)->url()));
return urlList;
}
//----------------------------------------------------
//class ConfigurationItem
ConfigurationItem::ConfigurationItem(TQListView *parent, DocumentationPlugin * plugin, const TQString &title, const TQString &url,
bool indexPossible, bool fullTextSearchPossible)
:TQCheckListItem(parent, "", TQCheckListItem::CheckBox), m_title(title), m_url(url),
m_origTitle(title), m_contents(true), m_index(false), m_fullTextSearch(false),
m_indexPossible(indexPossible), m_fullTextSearchPossible(fullTextSearchPossible),
m_docPlugin( plugin )
{
setText(3, m_title);
setText(4, m_url);
}
void ConfigurationItem::paintCell(TQPainter *p, const TQColorGroup &cg, int column,
int width, int align)
{
if ( (column == 0) || (column == 1) || (column == 2) )
{
if ( !p )
return;
TQListView *lv = listView();
if ( !lv )
return;
const BackgroundMode bgmode = lv->viewport()->backgroundMode();
const TQColorGroup::ColorRole crole = TQPalette::backgroundRoleFromMode( bgmode );
p->fillRect(0, 0, width, height(), cg.brush(crole));
TQFontMetrics fm(lv->fontMetrics());
int boxsize = lv->style().pixelMetric(TQStyle::PM_CheckListButtonSize, lv);
int marg = lv->itemMargin();
int styleflags = TQStyle::Style_Default;
if (((column == 0) && m_contents) || ((column == 1) && m_index) || ((column == 2) && m_fullTextSearch))
styleflags |= TQStyle::Style_On;
else
styleflags |= TQStyle::Style_Off;
if ((column == 0) || ((column == 1) && m_indexPossible) || ((column == 2) && m_fullTextSearchPossible))
styleflags |= TQStyle::Style_Enabled;
int x = 0;
int y = 0;
x += 3;
if (align & AlignVCenter)
y = ((height() - boxsize) / 2) + marg;
else
y = (fm.height() + 2 + marg - boxsize) / 2;
TQStyleOption opt(this);
lv->style().tqdrawPrimitive(TQStyle::PE_CheckListIndicator, p,
TQRect(x, y, boxsize, fm.height() + 2 + marg), cg, styleflags, opt);
return;
}
TQListViewItem::paintCell(p, cg, column, width, align);
}
int ConfigurationItem::width(const TQFontMetrics &fm, const TQListView *lv, int c) const
{
if ((c == 0) || (c == 1) || (c == 2))
return lv->style().pixelMetric(TQStyle::PM_CheckListButtonSize, lv) + 4;
return TQListViewItem::width(fm, lv, c);
}
//----------------------------------------------------
//class DocumentationPlugin
DocumentationPlugin::DocumentationPlugin(TDEConfig *pluginConfig, TQObject *parent, const char *name)
:TQObject(parent, name), config(pluginConfig), m_indexCreated(false)
{
}
DocumentationPlugin::~DocumentationPlugin()
{
}
void DocumentationPlugin::autoSetup()
{
config->setGroup("General");
if ( ! config->readBoolEntry("Autosetup", false) )
{
autoSetupPlugin();
config->setGroup("General");
config->writeEntry("Autosetup", true);
config->sync();
}
}
void DocumentationPlugin::clear()
{
// Do not clear the project documentation catalogs, since those are handled by
// the ProjectDocumentationPlugin class
for (TQValueList<DocumentationCatalogItem *>::iterator it = catalogs.begin();
it != catalogs.end(); /*none*/)
{
if (!(*it)->isProjectDocumentationItem())
{
DocumentationCatalogItem *curr = *it;
++it; // Need to advance before destroying the catalog, otherwise it could crash
//clearCatalog(curr); -- not necessary, already invoked by ~DocumentationCatalogItem()
delete curr; // Destroy and removes the item from the TDEListView
}
else
++it;
}
}
void DocumentationPlugin::clearCatalog(DocumentationCatalogItem *item)
{
//clear named catalog map
for (TQMap<TQString, DocumentationCatalogItem*>::iterator it = namedCatalogs.begin();
it != namedCatalogs.end(); ++it)
{
if (it.data() == item)
{
namedCatalogs.remove(it);
break;
}
}
//clear indexes for catalog
TQValueList<IndexItemProto *> idx = indexes[item];
for (TQValueList<IndexItemProto *>::iterator it = idx.begin(); it != idx.end(); ++it)
{
delete *it;
}
indexes.remove(item);
//clear the catalog
catalogs.remove(item); // Removes the item from the catalog list
}
void DocumentationPlugin::createIndex(IndexBox *index)
{
if (m_indexCreated)
return;
for (TQValueList<DocumentationCatalogItem *>::iterator it = catalogs.begin();
it != catalogs.end(); ++it)
{
loadIndex(index, *it);
}
m_indexCreated = true;
}
void DocumentationPlugin::cacheIndex(DocumentationCatalogItem *item)
{
kdDebug() << "Creating index cache for " << item->text(0) << endl;
TQString cacheName = locateLocal("data", TQString("kdevdocumentation/index/cache_") + item->text(0));
TQFile cacheFile(cacheName);
if (!cacheFile.open(IO_WriteOnly))
return;
TQTextStream str(&cacheFile);
str.setEncoding(TQTextStream::Unicode);
str << CACHE_VERSION << endl;
TQValueList<IndexItemProto*> catalogIndexes = indexes[item];
for (TQValueList<IndexItemProto*>::const_iterator it = catalogIndexes.constBegin();
it != catalogIndexes.constEnd(); ++it)
{
str << (*it)->text() << endl;
str << (*it)->description() << endl;
str << (*it)->url().url() << endl;
}
cacheFile.close();
}
bool DocumentationPlugin::loadCachedIndex(IndexBox *index, DocumentationCatalogItem *item)
{
TQString cacheName = locateLocal("data", TQString("kdevdocumentation/index/cache_") + item->cacheVersion() + item->text(0));
TQFile cacheFile(cacheName);
if (!cacheFile.open(IO_ReadOnly))
return false;
kdDebug() << "Using cached index for item: " << item->text(0) << endl;
TQTextStream str(&cacheFile);
str.setEncoding(TQTextStream::Unicode);
TQString cache = str.read();
TQStringList cacheList = TQStringList::split("\n", cache, true);
TQString ver = cacheList.first();
if (ver != CACHE_VERSION)
{
kdDebug() << "Wrong cache version: " << ver << endl;
return false;
}
TQStringList::const_iterator it = cacheList.begin();
it++;
TQString s[3]; int c = 0;
for (; it != cacheList.end(); ++it)
{
s[c] = *it;
if (c == 2)
{
IndexItemProto *ii = new IndexItemProto(this, item, index, s[0], s[1]);
ii->addURL(KURL(s[2]));
c = 0;
}
else c++;
}
cacheFile.close();
return true;
}
void DocumentationPlugin::addCatalog(DocumentationCatalogItem *item)
{
catalogs.append(item);
namedCatalogs[item->text(0)] = item;
// indexes[item] = TQValueList<IndexItem*>();
}
void DocumentationPlugin::addCatalogConfiguration(TDEListView *configurationView,
const TQString &title, const TQString &url)
{
new ConfigurationItem(configurationView, this, title, url,
hasCapability(Index), hasCapability(FullTextSearch));
}
void DocumentationPlugin::editCatalogConfiguration(ConfigurationItem *configurationItem,
const TQString &title, const TQString &url)
{
configurationItem->setTitle(title);
configurationItem->setURL(url);
}
void DocumentationPlugin::deleteCatalogConfiguration(const ConfigurationItem *const configurationItem)
{
deletedConfigurationItems << configurationItem->title();
}
void DocumentationPlugin::clearCatalogIndex(DocumentationCatalogItem *item)
{
//clear indexes for catalog
TQValueList<IndexItemProto *> idx = indexes[item];
for (TQValueList<IndexItemProto *>::iterator it = idx.begin(); it != idx.end(); ++it)
{
delete *it;
}
indexes.remove(item);
}
void DocumentationPlugin::loadIndex(IndexBox *index, DocumentationCatalogItem *item)
{
if (!indexEnabled(item))
return;
if (!needRefreshIndex(item) && loadCachedIndex(index, item))
return;
createIndex(index, item);
cacheIndex(item);
}
TDEListViewItem* DocumentationPlugin::findCatalogPosition(const TQString &key, const TDEListView *contents) const
{
TDEListViewItem *curr_item = (TDEListViewItem*)(contents->firstChild());
if (!curr_item)
return NULL; // Empty listview
TDEListViewItem *prev_item = NULL;
while (curr_item && curr_item->text(0) <= key)
{
prev_item = curr_item;
curr_item = (TDEListViewItem*)(curr_item->nextSibling());
}
return prev_item;
}
void DocumentationPlugin::init(TDEListView *contents)
{
config->setGroup("Locations");
TQMap<TQString, TQString> entryMap = config->entryMap("Locations");
for (TQMap<TQString, TQString>::const_iterator it = entryMap.begin();
it != entryMap.end(); ++it)
{
TQString cat_key = it.key();
if (catalogEnabled(cat_key))
{
// Keep the list in increasing sorted order. Given that the list is
// reasonably short, we can affort to scan through the list each time
// a new item is created
TDEListViewItem *afterItem = findCatalogPosition(cat_key, contents);
createCatalog(contents, afterItem, cat_key, config->readPathEntry(cat_key));
}
}
}
void DocumentationPlugin::reinit(TDEListView *contents, IndexBox *index, TQStringList restrictions)
{
config->setGroup("Locations");
TQMap<TQString, TQString> entryMap = config->entryMap("Locations");
//remove deleted in configuration catalogs
for (TQStringList::const_iterator it = deletedConfigurationItems.constBegin();
it != deletedConfigurationItems.constEnd(); ++it)
{
if (namedCatalogs.contains(*it))
delete namedCatalogs[*it];
}
deletedConfigurationItems.clear();
//update configuration
for (TQMap<TQString, TQString>::const_iterator it = entryMap.begin();
it != entryMap.end(); ++it)
{
config->setGroup("Locations");
TQString cat_key = it.key();
if (restrictions.contains(cat_key) || (!catalogEnabled(cat_key)))
{
if (namedCatalogs.contains(cat_key))
delete namedCatalogs[cat_key];
}
else
{
kdDebug() << "updating 1" << endl;
if (!namedCatalogs.contains(cat_key)) //create catalog if it does not exist
{
TDEListViewItem *afterItem = findCatalogPosition(cat_key, contents);
DocumentationCatalogItem *item = createCatalog(contents, afterItem, cat_key, config->readPathEntry(cat_key));
loadIndex(index, item);
index->setDirty(true);
// index->refill(indexes[item]);
}
else if (!indexEnabled(namedCatalogs[cat_key])) //clear index if it is disabled in configuration
{
kdDebug() << " updating: clearCatalogIndex" << endl;
clearCatalogIndex(namedCatalogs[cat_key]);
}
else if ( (indexEnabled(namedCatalogs[cat_key])) //index is requested in configuration but does not yet exist
&& (!indexes.contains(namedCatalogs[cat_key])) )
{
kdDebug() << " index requested " << endl;
loadIndex(index, namedCatalogs[cat_key]);
index->setDirty(true);
}
m_indexCreated = true;
}
}
}
void DocumentationPlugin::loadCatalogConfiguration(TDEListView *configurationView)
{
config->setGroup("Locations");
TQMap<TQString, TQString> entryMap = config->entryMap("Locations");
for (TQMap<TQString, TQString>::const_iterator it = entryMap.begin();
it != entryMap.end(); ++it)
{
if (namedCatalogs.contains(it.key())
&& namedCatalogs[it.key()]->isProjectDocumentationItem())
continue;
config->setGroup("Locations");
ConfigurationItem *item = new ConfigurationItem(configurationView, this, it.key(),
config->readPathEntry(it.key()),
hasCapability(Index), hasCapability(FullTextSearch));
config->setGroup("TOC Settings");
item->setContents(config->readBoolEntry(item->title(), true));
config->setGroup("Index Settings");
item->setIndex(config->readBoolEntry(item->title(), false));
config->setGroup("Search Settings");
item->setFullTextSearch(config->readBoolEntry(item->title(), false));
}
}
void DocumentationPlugin::saveCatalogConfiguration(TDEListView *configurationView)
{
for (TQStringList::const_iterator it = deletedConfigurationItems.constBegin();
it != deletedConfigurationItems.constEnd(); ++it)
{
config->setGroup("Locations");
config->deleteEntry(*it);
config->setGroup("TOC Settings");
config->deleteEntry(*it);
config->setGroup("Index Settings");
config->deleteEntry(*it);
config->setGroup("Search Settings");
config->deleteEntry(*it);
}
TQListViewItemIterator it(configurationView);
while (it.current())
{
ConfigurationItem *confItem = dynamic_cast<ConfigurationItem*>(it.current());
if ( confItem->docPlugin() != this )
{
++it;
continue;
}
config->setGroup("Locations");
if (confItem->isChanged())
config->deleteEntry(confItem->origTitle());
config->writePathEntry(confItem->title(), confItem->url());
config->setGroup("TOC Settings");
if (confItem->isChanged())
config->deleteEntry(confItem->origTitle());
config->writeEntry(confItem->title(), confItem->contents());
config->setGroup("Index Settings");
if (confItem->isChanged())
config->deleteEntry(confItem->origTitle());
config->writeEntry(confItem->title(), confItem->index());
config->setGroup("Search Settings");
if (confItem->isChanged())
config->deleteEntry(confItem->origTitle());
config->writeEntry(confItem->title(), confItem->fullTextSearch());
++it;
}
config->sync();
}
void DocumentationPlugin::setIndexEnabled( DocumentationCatalogItem * item, bool e )
{
TQString group = config->group();
config->setGroup("Index Settings");
config->writeEntry(item->text(0), e);
config->setGroup(group);
}
bool DocumentationPlugin::indexEnabled( DocumentationCatalogItem * item ) const
{
TQString group = config->group();
config->setGroup("Index Settings");
bool b = config->readBoolEntry(item->text(0), false);
config->setGroup(group);
return b;
}
bool DocumentationPlugin::catalogEnabled(const TQString &name) const
{
TQString group = config->group();
config->setGroup("TOC Settings");
bool b = config->readBoolEntry(name, true);
config->setGroup(group);
return b;
}
void DocumentationPlugin::setCatalogEnabled(const TQString &name, bool e)
{
TQString group = config->group();
config->setGroup("TOC Settings");
config->writeEntry(name, e);
config->setGroup(group);
}
//----------------------------------------------------
//class IndexBox
IndexBox::IndexBox(TQWidget *parent, const char *name)
:TDEListBox(parent, name), m_dirty(false)
{
}
void IndexBox::addIndexItem(IndexItemProto *item)
{
items[item->text()].append(item);
}
void IndexBox::removeIndexItem(IndexItemProto *item)
{
TQString text = item->text();
items[text].remove(item);
if (items[text].count() == 0)
{
items.remove(text);
TQListBoxItem *item = findItem(text, TQt::CaseSensitive | TQt::ExactMatch);
if (item)
delete item;
}
}
void IndexBox::fill()
{
for (TQMap<TQString, TQValueList<IndexItemProto*> >::const_iterator it = items.begin();
it != items.end(); ++it)
{
new IndexItem(this, it.key());
}
}
void IndexBox::setDirty(bool dirty)
{
m_dirty = dirty;
}
void IndexBox::refill()
{
if (m_dirty)
{
clear();
fill();
setDirty(false);
}
}
ProjectDocumentationPlugin::ProjectDocumentationPlugin(DocumentationPlugin *docPlugin, DocumentationPlugin::ProjectDocType type)
:TQObject(0, 0), m_docPlugin(docPlugin), m_catalog(0), m_type(type), m_contents(0), m_index(0)
{
kdDebug() << "ProjectDocumentationPlugin::ProjectDocumentationPlugin for type " << type << endl;
m_watch = new KDirWatch(this);
connect(m_watch, TQT_SIGNAL(dirty(const TQString&)), this, TQT_SLOT(reinit()));
m_watch->startScan();
}
ProjectDocumentationPlugin::~ProjectDocumentationPlugin()
{
deinit();
}
TDEListViewItem* ProjectDocumentationPlugin::findCatalogPosition(const TQString &key, const TDEListView *contents) const
{
TDEListViewItem *curr_item = (TDEListViewItem*)(contents->firstChild());
if (!curr_item)
return NULL; // Empty listview
TDEListViewItem *prev_item = NULL;
while (curr_item && curr_item->text(0) <= key)
{
prev_item = curr_item;
curr_item = (TDEListViewItem*)(curr_item->nextSibling());
}
return prev_item;
}
void ProjectDocumentationPlugin::init(TDEListView *contents, IndexBox *index, const TQString &url)
{
m_contents = contents;
m_index = index;
m_url = url;
if (m_catalog)
deinit();
TQString cat_key = (m_type == DocumentationPlugin::APIDocs ? i18n("Project API Documentation")
: i18n("Project User Manual"));
TDEListViewItem *afterItem = findCatalogPosition(cat_key, contents);
m_catalog = m_docPlugin->createCatalog(contents, afterItem, cat_key, url);
if (m_catalog)
{
m_catalog->setProjectDocumentationItem(true);
m_watch->addFile(url);
}
}
void ProjectDocumentationPlugin::reinit()
{
deinit();
if (m_contents != 0 && m_index != 0 && m_url != 0)
init(m_contents, m_index, m_url);
}
void ProjectDocumentationPlugin::deinit()
{
m_watch->removeFile(m_url);
if (m_catalog)
{
delete m_catalog;
m_catalog = 0;
}
}
TQString ProjectDocumentationPlugin::pluginName() const
{
return m_docPlugin->pluginName();
}
TQString ProjectDocumentationPlugin::catalogURL() const
{
return m_url;
}
#include "kdevdocumentationplugin.moc"