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.
889 lines
30 KiB
889 lines
30 KiB
/*********
|
|
*
|
|
* This file is part of BibleTime's source code, http://www.bibletime.info/.
|
|
*
|
|
* Copyright 1999-2006 by the BibleTime developers.
|
|
* The BibleTime source code is licensed under the GNU General Public License version 2.0.
|
|
*
|
|
**********/
|
|
|
|
//BibleTime includes
|
|
#include "cswordmoduleinfo.h"
|
|
#include "cswordbackend.h"
|
|
#include "cswordmodulesearch.h"
|
|
#include "cswordkey.h"
|
|
#include "centrydisplay.h"
|
|
#include "clanguagemgr.h"
|
|
|
|
#include "util/scoped_resource.h"
|
|
#include "util/directoryutil.h"
|
|
#include "util/cpointers.h"
|
|
#include "frontend/cbtconfig.h"
|
|
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
#include <stddef.h>
|
|
#include <dirent.h>
|
|
#include <regex.h>
|
|
|
|
//TQt includes
|
|
#include <tqregexp.h>
|
|
#include <tqdir.h>
|
|
#include <tqvariant.h>
|
|
#include <tqfileinfo.h>
|
|
|
|
//KDE includes
|
|
#include <klocale.h>
|
|
#include <kglobal.h>
|
|
#include <kstandarddirs.h>
|
|
#include <kconfig.h>
|
|
|
|
//Sword includes
|
|
#include <swbuf.h>
|
|
#include <swkey.h>
|
|
#include <listkey.h>
|
|
#include <versekey.h>
|
|
#include <swconfig.h>
|
|
#include <rtfhtml.h>
|
|
|
|
//Lucence includes
|
|
#include <CLucene.h>
|
|
#include <CLucene/util/Reader.h>
|
|
#include <CLucene/util/Misc.h>
|
|
#include <CLucene/util/dirent.h>
|
|
|
|
//Increment this, if the index format changes
|
|
//Then indices on the user's systems will be rebuilt
|
|
const unsigned int INDEX_VERSION = 6;
|
|
|
|
//Maximum index entry size, 1MiB for now
|
|
//Lucene default is too small
|
|
const unsigned long BT_MAX_LUCENE_FIELD_LENGTH = 1024*1024;
|
|
|
|
CSwordModuleInfo::CSwordModuleInfo(sword::SWModule * module, CSwordBackend * const usedBackend) {
|
|
m_module = module;
|
|
Q_ASSERT(module);
|
|
|
|
m_searchResult.ClearList();
|
|
m_backend = usedBackend ? usedBackend : CPointers::backend();
|
|
m_dataCache.name = module ? TQString(module->Name()) : TQString();
|
|
m_dataCache.isUnicode = module ? module->isUnicode() : false;
|
|
m_dataCache.category = UnknownCategory;
|
|
m_dataCache.language = 0;
|
|
m_dataCache.hasVersion = !TQString((*m_backend->getConfig())[module->Name()]["Version"]).isEmpty();
|
|
|
|
if (backend()) {
|
|
if (hasVersion() && (minimumSwordVersion() > sword::SWVersion::currentVersion)) {
|
|
qWarning("The module \"%s\" requires a newer Sword library. Please update to \"Sword %s\".",
|
|
name().latin1(), (const char *)minimumSwordVersion());
|
|
}
|
|
}
|
|
}
|
|
|
|
CSwordModuleInfo::CSwordModuleInfo(const CSwordModuleInfo & m) {
|
|
m_module = m.m_module;
|
|
m_backend = m.m_backend;
|
|
m_dataCache = m.m_dataCache;
|
|
m_searchResult = m.m_searchResult;
|
|
}
|
|
|
|
/** No descriptions */
|
|
CSwordModuleInfo *CSwordModuleInfo::clone() {
|
|
return new CSwordModuleInfo(*this);
|
|
}
|
|
|
|
CSwordModuleInfo::~CSwordModuleInfo() {
|
|
m_searchResult.ClearList();
|
|
m_module = 0; //the Sword module object is deleted by the backend
|
|
}
|
|
|
|
/** Sets the unlock key of the modules and writes the key into the cofig file.*/
|
|
const bool CSwordModuleInfo::unlock(const TQString & unlockKey) {
|
|
if (!isEncrypted()) {
|
|
return false;
|
|
}
|
|
|
|
CBTConfig::setModuleEncryptionKey(name(), unlockKey);
|
|
backend()->setCipherKey(m_module->Name(), unlockKey.latin1());
|
|
//TODO: write to Sword config as well
|
|
|
|
return true;
|
|
}
|
|
|
|
/** This function returns true if this module is locked, otherwise return false. */
|
|
const bool CSwordModuleInfo::isLocked() {
|
|
//still works, but the cipherkey is stored in CBTConfig.
|
|
//Works because it is set in sword on program startup.
|
|
|
|
if (isEncrypted()) {
|
|
if (unlockKeyIsValid()) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/** This functions returns true if this module is encrypted (locked or unlocked). */
|
|
const bool CSwordModuleInfo::isEncrypted() const {
|
|
/**
|
|
* If we have the CipherKey entry the module
|
|
* is encrypted but not necessarily locked
|
|
*/
|
|
|
|
//This code is still right, though we do no longer write to the module config files any more
|
|
sword::ConfigEntMap config = backend()->getConfig()->Sections.find(name().latin1())->second;
|
|
sword::ConfigEntMap::iterator it = config.find("CipherKey");
|
|
|
|
if (it != config.end()) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/** This function makes an estimate if a module was properly unlocked.
|
|
* It returns true if the first entry of the module (parsed as Latin1
|
|
* byte sequence) is not empty and contains only printable characters.
|
|
* If that is the case, we can safely assume that a) the module was properly
|
|
* unlocked and b) no buffer overflows will occur, which can happen when
|
|
* Sword filters process garbage text which was not properly decrypted.
|
|
*/
|
|
const bool CSwordModuleInfo::unlockKeyIsValid() {
|
|
|
|
(*m_module) = sword::TOP;
|
|
|
|
// This needs to use ::tqfromLatin1 because if the text is still locked,
|
|
// a lot of garbage will show up. It will also work with properly decrypted
|
|
// Unicode text, because all non-ASCII Unicode chars consist of bytes >127
|
|
// and therefore contain no control (nonprintable) characters, which are all <127.
|
|
TQString test = isUnicode()
|
|
? TQString::fromUtf8(m_module->getRawEntryBuf().c_str())
|
|
: TQString::tqfromLatin1( m_module->getRawEntryBuf().c_str() );
|
|
|
|
if (test.isEmpty()) {
|
|
qWarning("Unlock key of module %s is NOT valid", name().latin1());
|
|
return false;
|
|
}
|
|
|
|
for (unsigned int i = 0; i <= test.length(); i++) {
|
|
if ( !test[i].isPrint() && !test[i].isNull() ) {
|
|
qWarning("Unlock key of module %s is NOT valid", name().latin1());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
qDebug("Unlock key of module %s is valid", name().latin1());
|
|
return true;
|
|
}
|
|
|
|
const TQString CSwordModuleInfo::getGlobalBaseIndexLocation() {
|
|
return KGlobal::dirs()->saveLocation("data", "bibletime/indices");
|
|
}
|
|
|
|
const TQString CSwordModuleInfo::getModuleBaseIndexLocation() const {
|
|
return getGlobalBaseIndexLocation() + TQString("/") + name().ascii();
|
|
}
|
|
|
|
const TQString CSwordModuleInfo::getModuleStandardIndexLocation() const { //this for now returns the location of the main index
|
|
return getModuleBaseIndexLocation() + TQString("/standard");
|
|
}
|
|
|
|
const bool CSwordModuleInfo::hasIndex() { //this will return true only
|
|
//if the index exists and has correct version information for both index and module
|
|
|
|
TQDir d;
|
|
if (!d.exists( getModuleStandardIndexLocation() )) {
|
|
return false;
|
|
}
|
|
|
|
//first check if the index version and module version are ok
|
|
util::scoped_ptr<KConfig> indexconfig(
|
|
new KConfig( getModuleBaseIndexLocation() + TQString("/bibletime-index.conf") )
|
|
);
|
|
|
|
if (hasVersion()) {
|
|
if (indexconfig->readEntry("module-version") != TQString(config(CSwordModuleInfo::ModuleVersion)) ) {
|
|
return false;
|
|
}
|
|
}
|
|
if (indexconfig->readEntry("index-version") != TQString::number( INDEX_VERSION )) {
|
|
qDebug("%s: INDEX_VERSION is not compatible with this version of BibleTime.", name().latin1());
|
|
return false;
|
|
}
|
|
|
|
//then check if the index is there
|
|
return lucene::index::IndexReader::indexExists(getModuleStandardIndexLocation().ascii());
|
|
}
|
|
|
|
|
|
void CSwordModuleInfo::buildIndex() {
|
|
wchar_t wcharBuffer[BT_MAX_LUCENE_FIELD_LENGTH + 1];
|
|
|
|
//we don't want the linked entries indexed again
|
|
module()->setSkipConsecutiveLinks(true);
|
|
|
|
//Without this we don't get strongs, lemmas, etc
|
|
backend()->setFilterOptions ( CBTConfig::getFilterOptionDefaults() );
|
|
//make sure we reset all important filter options which influcence the plain filters.
|
|
backend()->setOption( CSwordModuleInfo::strongNumbers, false );
|
|
backend()->setOption( CSwordModuleInfo::morphTags, false );
|
|
backend()->setOption( CSwordModuleInfo::morphSegmentation, false );
|
|
backend()->setOption( CSwordModuleInfo::footnotes, false );
|
|
backend()->setOption( CSwordModuleInfo::headings, false );
|
|
backend()->setOption( CSwordModuleInfo::scriptureReferences, false );
|
|
backend()->setOption( CSwordModuleInfo::redLetterWords, false );
|
|
|
|
// do not use any stop words
|
|
const TCHAR* stop_words[] = {
|
|
NULL
|
|
};
|
|
lucene::analysis::standard::StandardAnalyzer an( stop_words );
|
|
TQString index = getModuleStandardIndexLocation();
|
|
|
|
TQDir dir;
|
|
dir.mkdir( getGlobalBaseIndexLocation(), true );
|
|
dir.mkdir( getModuleBaseIndexLocation(), true );
|
|
dir.mkdir( getModuleStandardIndexLocation(), true );
|
|
|
|
|
|
if (lucene::index::IndexReader::indexExists(index.ascii())) {
|
|
if (lucene::index::IndexReader::isLocked(index.ascii()) ) {
|
|
lucene::index::IndexReader::unlock(index.ascii());
|
|
}
|
|
}
|
|
|
|
util::scoped_ptr<lucene::index::IndexWriter> writer( new lucene::index::IndexWriter(index.ascii(), &an, true) ); //always create a new index
|
|
writer->setMaxFieldLength(BT_MAX_LUCENE_FIELD_LENGTH);
|
|
writer->setUseCompoundFile(true); //merge segments into a single file
|
|
writer->setMinMergeDocs(1000);
|
|
|
|
*m_module = sword::TOP;
|
|
unsigned long verseLowIndex = m_module->Index();
|
|
*m_module = sword::BOTTOM;
|
|
unsigned long verseHighIndex = m_module->Index();
|
|
|
|
//verseLowIndex is not 0 in all cases (i.e. NT-only modules)
|
|
unsigned long verseIndex = verseLowIndex + 1;
|
|
|
|
|
|
const bool isVerseModule = (type() == CSwordModuleInfo::Bible) || (type() == CSwordModuleInfo::Commentary);
|
|
|
|
m_indexingProgress.setValue( TQVariant((int)0) );
|
|
m_indexingProgress.activate();
|
|
|
|
SWKey* key = m_module->getKey();
|
|
//VerseKey for bibles
|
|
VerseKey* vk = dynamic_cast<VerseKey*>(key);
|
|
|
|
if (vk) {
|
|
// we have to be sure to insert the english key into the index, otherwise we'd be in trouble if the language changes
|
|
vk->setLocale("en_US");
|
|
//If we have a verse based module, we want to include the pre-chapter etc. headings in the search
|
|
vk->Headings(1);
|
|
}
|
|
|
|
//holds UTF-8 data and is faster than TQString
|
|
TQCString textBuffer;
|
|
|
|
// we start with the first module entry, key is automatically updated
|
|
// because key is a pointer to the modules key
|
|
for (*m_module = sword::TOP; !(key->Error()); (*key)++) {
|
|
|
|
//If it is a sword-heading, store in buffer and index later in Verse X:1
|
|
if (vk) {
|
|
if (vk->Verse() == 0) {
|
|
//qWarning("key is %s", key->getText());
|
|
//qWarning("text is %s", m_module->StripText());
|
|
textBuffer.append( m_module->StripText() );
|
|
continue;
|
|
}
|
|
}
|
|
|
|
util::scoped_ptr<lucene::document::Document> doc(new lucene::document::Document());
|
|
|
|
//index the key
|
|
lucene_utf8towcs(wcharBuffer, key->getText(), BT_MAX_LUCENE_FIELD_LENGTH);
|
|
doc->add(*lucene::document::Field::UnIndexed(_T("key"), wcharBuffer));
|
|
|
|
// index the main text
|
|
//at this point we have to make sure we disabled the strongs and the other options
|
|
//so the plain filters won't include the numbers somehow.
|
|
lucene_utf8towcs(wcharBuffer, (const char*) textBuffer.append(m_module->StripText()), BT_MAX_LUCENE_FIELD_LENGTH);
|
|
doc->add(*lucene::document::Field::UnStored(_T("content"), wcharBuffer));
|
|
textBuffer.resize(0); //clean up
|
|
|
|
// index attributes
|
|
AttributeList::iterator attListI;
|
|
AttributeValue::iterator attValueI;
|
|
// Footnotes
|
|
for (attListI = m_module->getEntryAttributes()["Footnote"].begin();
|
|
attListI != m_module->getEntryAttributes()["Footnote"].end();
|
|
attListI++) {
|
|
lucene_utf8towcs(wcharBuffer, attListI->second["body"], BT_MAX_LUCENE_FIELD_LENGTH);
|
|
doc->add(*lucene::document::Field::UnStored(_T("footnote"), wcharBuffer));
|
|
} // for attListI
|
|
|
|
// Headings
|
|
for (attValueI = m_module->getEntryAttributes()["Heading"]["Preverse"].begin();
|
|
attValueI != m_module->getEntryAttributes()["Heading"]["Preverse"].end();
|
|
attValueI++) {
|
|
lucene_utf8towcs(wcharBuffer, attValueI->second, BT_MAX_LUCENE_FIELD_LENGTH);
|
|
doc->add(*lucene::document::Field::UnStored(_T("heading"), wcharBuffer));
|
|
} // for attValueI
|
|
|
|
// Strongs/Morphs
|
|
for (attListI = m_module->getEntryAttributes()["Word"].begin();
|
|
attListI != m_module->getEntryAttributes()["Word"].end();
|
|
attListI++) {
|
|
// for each attribute
|
|
if (attListI->second["LemmaClass"] == "strong") {
|
|
lucene_utf8towcs(wcharBuffer, attListI->second["Lemma"], BT_MAX_LUCENE_FIELD_LENGTH);
|
|
doc->add(*lucene::document::Field::UnStored(_T("strong"), wcharBuffer));
|
|
//qWarning("Adding strong %s", attListI->second["Lemma"].c_str());
|
|
}
|
|
if (attListI->second.find("Morph") != attListI->second.end()) {
|
|
lucene_utf8towcs(wcharBuffer, attListI->second["Morph"], BT_MAX_LUCENE_FIELD_LENGTH);
|
|
doc->add(*lucene::document::Field::UnStored(_T("morph"), wcharBuffer));
|
|
}
|
|
} // for attListI
|
|
|
|
writer->addDocument(doc);
|
|
verseIndex = m_module->Index();
|
|
|
|
if (verseIndex % 200 == 0) {
|
|
if (verseHighIndex == verseLowIndex) { //prevent division by zero
|
|
m_indexingProgress.setValue( TQVariant(0) );
|
|
} else {
|
|
m_indexingProgress.setValue( TQVariant((int)((100*(verseIndex-verseLowIndex))/(verseHighIndex-verseLowIndex))) );
|
|
}
|
|
m_indexingProgress.activate();
|
|
}
|
|
}
|
|
|
|
writer->optimize();
|
|
writer->close();
|
|
|
|
TQString configFilename = getModuleStandardIndexLocation() + TQString("/../bibletime-index.conf");
|
|
util::scoped_ptr<KConfig> indexconfig( new KConfig( configFilename ) );
|
|
if (hasVersion()) {
|
|
indexconfig->writeEntry("module-version", config(CSwordModuleInfo::ModuleVersion) );
|
|
}
|
|
indexconfig->writeEntry("index-version", INDEX_VERSION);
|
|
}
|
|
|
|
void CSwordModuleInfo::deleteIndexForModule( TQString name ) {
|
|
util::filesystem::DirectoryUtil::removeRecursive( getGlobalBaseIndexLocation() + "/" + name );
|
|
}
|
|
|
|
unsigned long CSwordModuleInfo::indexSize() const {
|
|
return util::filesystem::DirectoryUtil::getDirSizeRecursive( getModuleBaseIndexLocation() );
|
|
}
|
|
|
|
|
|
const bool CSwordModuleInfo::searchIndexed(const TQString& searchedText, sword::ListKey& scope) {
|
|
char utfBuffer[BT_MAX_LUCENE_FIELD_LENGTH + 1];
|
|
wchar_t wcharBuffer[BT_MAX_LUCENE_FIELD_LENGTH + 1];
|
|
|
|
// work around Swords thread insafety for Bibles and Commentaries
|
|
util::scoped_ptr < CSwordKey > key(CSwordKey::createInstance(this));
|
|
sword::SWKey* s = dynamic_cast < sword::SWKey * >(key.get());
|
|
TQPtrList<VerseKey> list;
|
|
list.setAutoDelete( true ); // the list owns the objects
|
|
|
|
const bool isVerseModule = (type() == CSwordModuleInfo::Bible) || (type() == CSwordModuleInfo::Commentary);
|
|
|
|
if (s) {
|
|
m_module->SetKey(*s);
|
|
}
|
|
|
|
m_searchResult.ClearList();
|
|
|
|
try {
|
|
// do not use any stop words
|
|
const TCHAR* stop_words[] = {
|
|
NULL
|
|
};
|
|
lucene::analysis::standard::StandardAnalyzer analyzer( stop_words );
|
|
lucene::search::IndexSearcher searcher(getModuleStandardIndexLocation().ascii());
|
|
lucene_utf8towcs(wcharBuffer, searchedText.utf8(), BT_MAX_LUCENE_FIELD_LENGTH);
|
|
util::scoped_ptr<lucene::search::Query> q( lucene::queryParser::QueryParser::parse(wcharBuffer, _T("content"), &analyzer) );
|
|
|
|
util::scoped_ptr<lucene::search::Hits> h( searcher.search(q, lucene::search::Sort::INDEXORDER) );
|
|
|
|
const bool useScope = (scope.Count() > 0);
|
|
// const bool isVerseModule = (type() == CSwordModuleInfo::Bible) || (type() == CSwordModuleInfo::Commentary);
|
|
|
|
lucene::document::Document* doc = 0;
|
|
util::scoped_ptr<SWKey> swKey( module()->CreateKey() );
|
|
|
|
|
|
for (int i = 0; i < h->length(); ++i) {
|
|
doc = &h->doc(i);
|
|
lucene_wcstoutf8(utfBuffer, doc->get(_T("key")), BT_MAX_LUCENE_FIELD_LENGTH);
|
|
|
|
swKey->setText(utfBuffer);
|
|
|
|
// limit results based on scope
|
|
//if (searchOptions & CSwordModuleSearch::useScope && scope.Count() > 0){
|
|
if (useScope) {
|
|
for (int j = 0; j < scope.Count(); j++) {
|
|
VerseKey* vkey = dynamic_cast<VerseKey*>(scope.getElement(j));
|
|
if (vkey->LowerBound().compare(*swKey) <= 0 && vkey->UpperBound().compare(*swKey) >= 0) {
|
|
m_searchResult.add(*swKey);
|
|
}
|
|
}
|
|
} else { // no scope, give me all buffers
|
|
m_searchResult.add(*swKey);
|
|
}
|
|
}
|
|
} catch (...) {
|
|
qWarning("CLucene exception");
|
|
return false;
|
|
}
|
|
|
|
list.clear();
|
|
|
|
return (m_searchResult.Count() > 0);
|
|
}
|
|
|
|
void CSwordModuleInfo::connectIndexingFinished(TQObject* receiver, const char* slot) {
|
|
m_indexingFinished.connect(receiver, slot);
|
|
}
|
|
|
|
void CSwordModuleInfo::connectIndexingProgress(TQObject* receiver, const char* slot) {
|
|
m_indexingProgress.connect(receiver, slot);
|
|
}
|
|
|
|
void CSwordModuleInfo::disconnectIndexingSignals(TQObject* receiver) {
|
|
m_indexingProgress.disconnect(receiver);
|
|
m_indexingFinished.disconnect(receiver);
|
|
}
|
|
|
|
/** Returns the last search result for this module. */
|
|
sword::ListKey & CSwordModuleInfo::searchResult(const sword::ListKey * newResult) {
|
|
if (newResult) {
|
|
m_searchResult.copyFrom(*newResult);
|
|
}
|
|
|
|
return m_searchResult;
|
|
}
|
|
|
|
/** Clears the last search result. */
|
|
void CSwordModuleInfo::clearSearchResult() {
|
|
m_searchResult.ClearList();
|
|
}
|
|
|
|
/** Returns the required Sword version for this module. Returns -1 if no special Sword version is required. */
|
|
const sword::SWVersion CSwordModuleInfo::minimumSwordVersion() {
|
|
return sword::SWVersion(config(CSwordModuleInfo::MinimumSwordVersion).latin1());
|
|
}
|
|
|
|
const TQString CSwordModuleInfo::config(const CSwordModuleInfo::ConfigEntry entry) const {
|
|
switch (entry) {
|
|
|
|
case AboutInformation: {
|
|
return getFormattedConfigEntry("About");
|
|
}
|
|
|
|
case CipherKey: {
|
|
if (CBTConfig::getModuleEncryptionKey(name()).isNull()) { //fall back!
|
|
return TQString(m_module->getConfigEntry("CipherKey"));
|
|
} else {
|
|
return CBTConfig::getModuleEncryptionKey(name());
|
|
};
|
|
}
|
|
|
|
case AbsoluteDataPath: {
|
|
TQString path( getSimpleConfigEntry("AbsoluteDataPath") );
|
|
path.tqreplace(TQRegExp("/./"), "/"); // make /abs/path/./modules/ looking better
|
|
//make sure we have a trailing slash!
|
|
|
|
if (path.right(1) != "/") {
|
|
path.append('/');
|
|
}
|
|
|
|
return path;
|
|
}
|
|
|
|
case DataPath: { //make sure we remove the dataFile part if it's a Lexicon
|
|
TQString path(getSimpleConfigEntry("DataPath"));
|
|
|
|
if ((type() == CSwordModuleInfo::GenericBook) || (type() == CSwordModuleInfo::Lexicon)) {
|
|
int pos = path.tqfindRev("/"); //last slash in the string
|
|
|
|
if (pos != -1) {
|
|
path = path.left(pos + 1); //include the slash
|
|
}
|
|
}
|
|
|
|
return path;
|
|
}
|
|
|
|
case Description:
|
|
return getFormattedConfigEntry("Description");
|
|
|
|
case ModuleVersion: {
|
|
TQString version(getSimpleConfigEntry("Version"));
|
|
|
|
if (version.isEmpty()) {
|
|
version = "1.0";
|
|
}
|
|
|
|
return version;
|
|
}
|
|
|
|
case MinimumSwordVersion: {
|
|
const TQString minimumVersion(getSimpleConfigEntry("MinimumVersion"));
|
|
return !minimumVersion.isEmpty()? minimumVersion : TQString("0.0");
|
|
}
|
|
|
|
case TextDir: {
|
|
const TQString dir(getSimpleConfigEntry("Direction"));
|
|
return !dir.isEmpty()? dir : TQString("LtoR");
|
|
}
|
|
|
|
case DisplayLevel: {
|
|
const TQString level(getSimpleConfigEntry("DisplayLevel"));
|
|
return !level.isEmpty()? level : TQString("1");
|
|
}
|
|
|
|
case GlossaryFrom: {
|
|
if (!category() == Glossary) {
|
|
return TQString();
|
|
};
|
|
|
|
const TQString lang(getSimpleConfigEntry("GlossaryFrom"));
|
|
|
|
return !lang.isEmpty()? lang : TQString();
|
|
}
|
|
|
|
case GlossaryTo: {
|
|
if (!category() == Glossary) {
|
|
return TQString();
|
|
};
|
|
|
|
const TQString lang(getSimpleConfigEntry("GlossaryTo"));
|
|
|
|
return !lang.isEmpty()? lang : TQString();
|
|
}
|
|
|
|
case Markup: {
|
|
const TQString markup(getSimpleConfigEntry("SourceType"));
|
|
return !markup.isEmpty()? markup : TQString("Unknown");
|
|
}
|
|
|
|
case DistributionLicense:
|
|
return getSimpleConfigEntry("DistributionLicense");
|
|
|
|
case DistributionSource:
|
|
return getSimpleConfigEntry("DistributionSource");
|
|
|
|
case DistributionNotes:
|
|
return getSimpleConfigEntry("DistributionNotes");
|
|
|
|
case TextSource:
|
|
return getSimpleConfigEntry("TextSource");
|
|
|
|
case CopyrightNotes:
|
|
return getSimpleConfigEntry("CopyrightNotes");
|
|
|
|
case CopyrightHolder:
|
|
return getSimpleConfigEntry("CopyrightHolder");
|
|
|
|
case CopyrightDate:
|
|
return getSimpleConfigEntry("CopyrightDate");
|
|
|
|
case CopyrightContactName:
|
|
return getSimpleConfigEntry("CopyrightContactName");
|
|
|
|
case CopyrightContactAddress:
|
|
return getSimpleConfigEntry("CopyrightContactAddress");
|
|
|
|
case CopyrightContactEmail:
|
|
return getSimpleConfigEntry("CopyrightContactEmail");
|
|
|
|
default:
|
|
return TQString();
|
|
}
|
|
}
|
|
|
|
/** Returns true if the module supports the feature given as parameter. */
|
|
const bool CSwordModuleInfo::has(const CSwordModuleInfo::Feature feature) const {
|
|
switch (feature) {
|
|
|
|
// case StrongsNumbers:
|
|
// return m_module->getConfig().has("Feature", "StrongsNumber");
|
|
|
|
case GreekDef:
|
|
return m_module->getConfig().has("Feature", "GreekDef");
|
|
|
|
case HebrewDef:
|
|
return m_module->getConfig().has("Feature", "HebrewDef");
|
|
|
|
case GreekParse:
|
|
return m_module->getConfig().has("Feature", "GreekParse");
|
|
|
|
case HebrewParse:
|
|
return m_module->getConfig().has("Feature", "HebrewParse");
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
const bool CSwordModuleInfo::has(const CSwordModuleInfo::FilterTypes option) const {
|
|
//BAD workaround to see if the filter is GBF, OSIS or ThML!
|
|
const TQString name = backend()->configOptionName(option);
|
|
|
|
if (m_module->getConfig().has("GlobalOptionFilter", TQString(TQString("OSIS").append(name)).latin1())) {
|
|
return true;
|
|
}
|
|
|
|
if (m_module->getConfig().has("GlobalOptionFilter", TQString(TQString("GBF").append(name)).latin1())) {
|
|
return true;
|
|
}
|
|
|
|
if (m_module->getConfig().has("GlobalOptionFilter", TQString(TQString("ThML").append(name)).latin1())) {
|
|
return true;
|
|
}
|
|
|
|
if (m_module->getConfig().has("GlobalOptionFilter", TQString(TQString("UTF8").append(name)).latin1())) {
|
|
return true;
|
|
}
|
|
|
|
if (m_module->getConfig().has("GlobalOptionFilter", name.latin1())) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/** Returns the text direction of the module's text., */
|
|
const CSwordModuleInfo::TextDirection CSwordModuleInfo::textDirection() {
|
|
if (config(TextDir) == "RtoL") {
|
|
return CSwordModuleInfo::RightToLeft;
|
|
} else {
|
|
return CSwordModuleInfo::LeftToRight;
|
|
}
|
|
}
|
|
|
|
/** Writes the new text at the given position into the module. This does only work for writable modules. */
|
|
void CSwordModuleInfo::write(CSwordKey * key, const TQString & newText) {
|
|
module()->KeyText((const char *)key->key().utf8());
|
|
|
|
//don't store a pointer to the const char* value somewhere because TQCString doesn't keep the value of it
|
|
module()->setEntry(isUnicode()? (const char *)newText.utf8() : (const char *)newText.local8Bit());
|
|
}
|
|
|
|
/** Deletes the current entry and removes it from the module. */
|
|
const bool CSwordModuleInfo::deleteEntry(CSwordKey * const key) {
|
|
module()->KeyText(isUnicode()? (const char *)key->key().utf8() : (const char *)key->key().local8Bit());
|
|
|
|
if (module()) {
|
|
module()->deleteEntry();
|
|
return true;
|
|
};
|
|
|
|
return false;
|
|
}
|
|
|
|
/** Returns the category of this module. See CSwordModuleInfo::Category for possible values. */
|
|
const CSwordModuleInfo::Category CSwordModuleInfo::category() const {
|
|
if (m_dataCache.category == CSwordModuleInfo::UnknownCategory) {
|
|
const TQString cat(m_module->getConfigEntry("Category"));
|
|
|
|
if (cat == "Cults / Unorthodox / Questionable Material") {
|
|
m_dataCache.category = Cult;
|
|
} else if (cat == "Daily Devotional" || m_module->getConfig().has("Feature", "DailyDevotion")) {
|
|
m_dataCache.category = DailyDevotional;
|
|
} else if (cat == "Glossaries" || m_module->getConfig().has("Feature", "Glossary")) { //alow both
|
|
m_dataCache.category = Glossary;
|
|
};
|
|
}
|
|
|
|
return m_dataCache.category;
|
|
}
|
|
|
|
/** Returns the display object for this module. */
|
|
Rendering::CEntryDisplay * const CSwordModuleInfo::getDisplay() const {
|
|
return dynamic_cast < Rendering::CEntryDisplay * >(m_module->Disp());
|
|
}
|
|
|
|
TQString CSwordModuleInfo::aboutText() const {
|
|
TQString text;
|
|
text += "<font size=\"-1\"><table>";
|
|
|
|
text += TQString("<tr><td><b>%1</b></td><td>%2</td><tr>")
|
|
.tqarg(i18n("Version"))
|
|
.tqarg(hasVersion()? config(CSwordModuleInfo::ModuleVersion) : i18n("unknown"));
|
|
|
|
text += TQString("<tr><td><b>%1</b></td><td>%2</td></tr>")
|
|
.tqarg(i18n("Markup"))
|
|
.tqarg(!TQString(m_module->getConfigEntry("SourceType")).isEmpty()? m_module->
|
|
getConfigEntry("SourceType") : i18n("unknown").ascii());
|
|
|
|
text += TQString("<tr><td><b>%1</b></td><td>%2</td></tr>")
|
|
.tqarg(i18n("Location"))
|
|
.tqarg(config(CSwordModuleInfo::AbsoluteDataPath));
|
|
|
|
text += TQString("<tr><td><b>%1</b></td><td>%2</td></tr>")
|
|
.tqarg(i18n("Language"))
|
|
.tqarg(language()->translatedName());
|
|
|
|
if (m_module->getConfigEntry("Category"))
|
|
text += TQString("<tr><td><b>%1</b></td><td>%2</td></tr>")
|
|
.tqarg(i18n("Category"))
|
|
.tqarg(m_module->getConfigEntry("Category"));
|
|
|
|
if (m_module->getConfigEntry("LCSH"))
|
|
text += TQString("<tr><td><b>%1</b></td><td>%2</td></tr>")
|
|
.tqarg(i18n("LCSH"))
|
|
.tqarg(m_module->getConfigEntry("LCSH"));
|
|
|
|
text += TQString("<tr><td><b>%1</b></td><td>%2</td></tr>")
|
|
.tqarg(i18n("Writable"))
|
|
.tqarg(isWritable()? i18n("yes") : i18n("no"));
|
|
|
|
if (isEncrypted())
|
|
text += TQString("<tr><td><b>%1</b></td><td>%2</td></tr>")
|
|
.tqarg(i18n("Unlock key"))
|
|
.tqarg(config(CSwordModuleInfo::CipherKey));
|
|
|
|
TQString options;
|
|
|
|
unsigned int opts;
|
|
|
|
for (opts = CSwordModuleInfo::filterTypesMIN; opts <= CSwordModuleInfo::filterTypesMAX; ++opts) {
|
|
if (has(static_cast < CSwordModuleInfo::FilterTypes > (opts))) {
|
|
if (!options.isEmpty()) {
|
|
options += TQString::tqfromLatin1(", ");
|
|
}
|
|
|
|
options += CSwordBackend::translatedOptionName(static_cast < CSwordModuleInfo::FilterTypes > (opts));
|
|
}
|
|
}
|
|
|
|
if (!options.isEmpty()) {
|
|
text += TQString("<tr><td><b>%1</b></td><td>%2</td></tr>")
|
|
.tqarg(i18n("Features"))
|
|
.tqarg(options);
|
|
}
|
|
|
|
text += "</table><hr>";
|
|
|
|
if (category() == Cult) //clearly say the module contains cult/questionable materials
|
|
text += TQString("<br/><b>%1</b><br/><br/>")
|
|
.tqarg(i18n("Take care, this work contains cult / questionable material!"));
|
|
|
|
text += TQString("<b>%1:</b><br><font size=\"-1\">%2</font>")
|
|
.tqarg(i18n("About"))
|
|
.tqarg(config(AboutInformation));
|
|
|
|
typedef TQValueList<CSwordModuleInfo::ConfigEntry> ListConfigEntry;
|
|
|
|
ListConfigEntry entries;
|
|
|
|
entries.append(DistributionLicense);
|
|
|
|
entries.append(DistributionSource);
|
|
|
|
entries.append(DistributionNotes);
|
|
|
|
entries.append(TextSource);
|
|
|
|
entries.append(CopyrightNotes);
|
|
|
|
entries.append(CopyrightHolder);
|
|
|
|
entries.append(CopyrightDate);
|
|
|
|
entries.append(CopyrightContactName);
|
|
|
|
entries.append(CopyrightContactAddress);
|
|
|
|
entries.append(CopyrightContactEmail);
|
|
|
|
typedef TQMap<CSwordModuleInfo::ConfigEntry, TQString> MapConfigEntry;
|
|
|
|
MapConfigEntry entryMap;
|
|
|
|
entryMap[DistributionLicense] = i18n("Distribution license");
|
|
|
|
entryMap[DistributionSource] = i18n("Distribution source");
|
|
|
|
entryMap[DistributionNotes] = i18n("Distribution notes");
|
|
|
|
entryMap[TextSource] = i18n("Text source");
|
|
|
|
entryMap[CopyrightNotes] = i18n("Copyright notes");
|
|
|
|
entryMap[CopyrightHolder] = i18n("Copyright holder");
|
|
|
|
entryMap[CopyrightDate] = i18n("Copyright date");
|
|
|
|
entryMap[CopyrightContactName] = i18n("Copyright contact name");
|
|
|
|
entryMap[CopyrightContactAddress] = i18n("Copyright contact address");
|
|
|
|
entryMap[CopyrightContactEmail] = i18n("Copyright contact email");
|
|
|
|
text += ("<hr><table>");
|
|
|
|
for (ListConfigEntry::iterator it(entries.begin()); it != entries.end(); ++it) {
|
|
TQString t( config(*it) );
|
|
|
|
if (!t.isEmpty()) {
|
|
text += TQString("<tr><td><b>%1</b></td><td>%2</td></tr>")
|
|
.tqarg(entryMap[*it])
|
|
.tqarg(config(*it));
|
|
}
|
|
|
|
}
|
|
|
|
|
|
text += "</table></font>";
|
|
|
|
return text;
|
|
}
|
|
|
|
/** Returns the language of the module. */
|
|
const CLanguageMgr::Language * const CSwordModuleInfo::language() const {
|
|
if (!m_dataCache.language) {
|
|
if (module()) {
|
|
if (category() == Glossary) {
|
|
//special handling for glossaries, we use the "from language" as language for the module
|
|
m_dataCache.language = (CPointers::languageMgr())->languageForAbbrev(config(GlossaryFrom));
|
|
} else {
|
|
m_dataCache.language = (CPointers::languageMgr())->languageForAbbrev(module()->Lang());
|
|
}
|
|
} else {
|
|
m_dataCache.language = (CPointers::languageMgr())->defaultLanguage(); //default language
|
|
}
|
|
}
|
|
|
|
return m_dataCache.language;
|
|
}
|
|
|
|
|
|
/*!
|
|
\fn CSwordModuleInfo::getSimpleConfigEntry(char* name)
|
|
*/
|
|
TQString CSwordModuleInfo::getSimpleConfigEntry(const TQString& name) const {
|
|
TQString ret = isUnicode()
|
|
? TQString::fromUtf8(m_module->getConfigEntry(name.latin1()))
|
|
: TQString::tqfromLatin1(m_module->getConfigEntry(name.latin1()));
|
|
|
|
return ret.isEmpty() ? TQString() : ret;
|
|
}
|
|
|
|
TQString CSwordModuleInfo::getFormattedConfigEntry(const TQString& name) const {
|
|
SWBuf RTF_Buffer(m_module->getConfigEntry(name.latin1()));
|
|
sword::RTFHTML RTF_Filter;
|
|
RTF_Filter.processText(RTF_Buffer, 0, 0);
|
|
TQString ret = isUnicode()
|
|
? TQString::fromUtf8(RTF_Buffer.c_str())
|
|
: TQString::tqfromLatin1(RTF_Buffer.c_str());
|
|
|
|
return ret.isEmpty() ? TQString() : ret;
|
|
}
|