/* This file is part of the KDE project Copyright (C) 2004 by Alexander Dymo 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 #include #include #include #include #include #include #include #include #include #include //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 itemProtos = m_listbox->items[text()]; for (TQValueList::const_iterator it = itemProtos.begin(); it != itemProtos.end(); ++it) urlList.append(qMakePair((*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().drawPrimitive(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::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::iterator it = namedCatalogs.begin(); it != namedCatalogs.end(); ++it) { if (it.data() == item) { namedCatalogs.remove(it); break; } } //clear indexes for catalog TQValueList idx = indexes[item]; for (TQValueList::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::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 catalogIndexes = indexes[item]; for (TQValueList::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(); } 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 idx = indexes[item]; for (TQValueList::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 entryMap = config->entryMap("Locations"); for (TQMap::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 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::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 entryMap = config->entryMap("Locations"); for (TQMap::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(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 >::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, TQ_SIGNAL(dirty(const TQString&)), this, TQ_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"