void BtInstallThread::removeModule()
{
	qDebug() << "BtInstallThread::removeModule start";
	CSwordModuleInfo* m;
	m = CPointers::backend()->findModuleByName(m_module);
	if (!m) {
		m = instbackend::backend(instbackend::source(m_destination.toLatin1()))->findModuleByName(m_module);
	}
	if (m) { //module found?
		qDebug() << "BtInstallThread::removeModule, module" << m_module << "found";
		QString prefixPath = m->config(CSwordModuleInfo::AbsoluteDataPath) + "/";
		QString dataPath = m->config(CSwordModuleInfo::DataPath);
		if (dataPath.left(2) == "./") {
			dataPath = dataPath.mid(2);
		}

		if (prefixPath.contains(dataPath)) {
			prefixPath.remove( prefixPath.indexOf(dataPath), dataPath.length() );
		}
		else {
			prefixPath = QString::fromLatin1(CPointers::backend()->prefixPath);
		}

		sword::SWMgr mgr(prefixPath.toLatin1());
		BtInstallMgr iMgr;
		iMgr.removeModule(&mgr, m->name().toLatin1());
	} else {
		qDebug() << "BtInstallThread::removeModule, module" << m_module << "not found";
	}
}
void CInfoDisplay::lookupInfo(const QString &mod_name, const QString &key_text) {
	qDebug("CInfoDisplay::lookup");
	qDebug() <<  mod_name <<  key_text;
	CSwordModuleInfo* m = CPointers::backend()->findModuleByName(mod_name);
	Q_ASSERT(m);
	if (!m)
		return;
	boost::scoped_ptr<CSwordKey> key( CSwordKey::createInstance(m) );
	key->key( key_text ); 

	CDisplayTemplateMgr* mgr = CPointers::displayTemplateManager();
	CDisplayTemplateMgr::Settings settings;
	settings.pageCSS_ID = "infodisplay";

    // lookup text and wrap in a "div" with language set to module language
    QString lang = m->language()->abbrev();
    QString renderedText = key->renderedText();
    QString divText = "<div class=\"infodisplay\" lang=\"";
        divText.append(lang);
        divText.append("\">");
        divText.append(renderedText);
        divText.append("</div>");

	QString content = mgr->fillTemplate(CBTConfig::get
				(CBTConfig::displayStyle), divText, settings);

	m_htmlPart->setText(content);
}
Exemplo n.º 3
0
QString ModuleInterface::module(int index) {
    if (index < 0 || index >= m_modules.count())
        return "";
    CSwordModuleInfo* module = m_modules.at(index);
    if (module == 0)
        return "";
    return module->name();
}
Exemplo n.º 4
0
void BtBookshelfDockWidget::slotPrepareItemContextMenu() {
    void *v = m_itemContextMenu->property("BtModule").value<void*>();
    CSwordModuleInfo *module = static_cast<CSwordModuleInfo*>(v);
    m_itemOpenAction->setEnabled(!module->isLocked());
    m_itemSearchAction->setText(tr("&Search in %1...").arg(module->name()));
    m_itemSearchAction->setEnabled(!module->isLocked());
    m_itemUnlockAction->setEnabled(module->isLocked());
}
Exemplo n.º 5
0
QString ModuleInterface::englishCategory(int index) {
    if (index < 0 || index >= m_modules.count())
        return "";
    CSwordModuleInfo* module = m_modules.at(index);
    if (module == 0)
        return "";
    CSwordModuleInfo::Category category = module->category();
    if (category == 0)
        return "";
    return module->englishCategoryName(category);
}
Exemplo n.º 6
0
QString ModuleInterface::language(int index) {
    if (index < 0 || index >= m_modules.count())
        return "";
    CSwordModuleInfo* module = m_modules.at(index);
    if (module == 0)
        return "";
    const CLanguageMgr::Language* language = module->language();
    if (language == 0)
        return "";
    return language->translatedName();
}
Exemplo n.º 7
0
void ModuleInterface::unlock(const QString& moduleName, const QString& unlockKey) {
    CSwordModuleInfo* module = CSwordBackend::instance()->findModuleByName(moduleName);
    if (module) {
        module->unlock(unlockKey);

        // Re-initialize module pointers:
        CSwordBackend *backend = CSwordBackend::instance();
        backend->reloadModules(CSwordBackend::OtherChange);
        module = CSwordBackend::instance()->findModuleByName(moduleName);
        updateWorksModel();
    }
}
/** Prints a key using the hyperlink created by CReferenceManager. */
bool CExportManager::printByHyperlink( const QString& hyperlink, CSwordBackend::DisplayOptions displayOptions, CSwordBackend::FilterOptions filterOptions ) {
    QString moduleName;
    QString keyName;
    CReferenceManager::Type type;

    CReferenceManager::decodeHyperlink(hyperlink, moduleName, keyName, type);
    if (moduleName.isEmpty()) {
        moduleName = CReferenceManager::preferredModule(type);
    }

    CPrinter::KeyTree tree;
    CPrinter::KeyTreeItem::Settings settings;
    settings.keyRenderingFace =
        displayOptions.verseNumbers
        ? CPrinter::KeyTreeItem::Settings::SimpleKey
        : CPrinter::KeyTreeItem::Settings::NoKey;

    CSwordModuleInfo* module = backend()->findModuleByName(moduleName);
    Q_ASSERT(module);

    if (module) {
        //check if we have a range of entries or a single one
        if ((module->type() == CSwordModuleInfo::Bible) || (module->type() == CSwordModuleInfo::Commentary)) {
            sword::ListKey verses = sword::VerseKey().ParseVerseList((const char*)keyName.toUtf8(), "Genesis 1:1", true);

            for (int i = 0; i < verses.Count(); ++i) {
                sword::VerseKey* element = dynamic_cast<sword::VerseKey*>(verses.GetElement(i));
                if (element) {
                    const QString startKey = QString::fromUtf8(element->LowerBound().getText());
                    const QString stopKey =  QString::fromUtf8(element->UpperBound().getText());

                    tree.append( new CPrinter::KeyTreeItem(startKey, stopKey, module, settings) );
                }
                else if (verses.GetElement(i)) {
                    const QString key =  QString::fromUtf8(verses.GetElement(i)->getText());

                    tree.append( new CPrinter::KeyTreeItem(key, module, settings) );
                }
            }
        }
        else {
            tree.append( new CPrinter::KeyTreeItem(keyName, module, settings) );
        }
    }

    boost::scoped_ptr<CPrinter> printer(new CPrinter(0, displayOptions, filterOptions));
    printer->printKeyTree(tree);
    return true;
}
Exemplo n.º 9
0
bool ModuleInterface::isLocked(const QString& moduleName) {
    CSwordModuleInfo* module = CSwordBackend::instance()->findModuleByName(moduleName);
    if (module) {

        // Verse intros must be false for checking lock
        if (module->type() == CSwordModuleInfo::Bible ||
                module->type() == CSwordModuleInfo::Commentary) {
                ((sword::VerseKey*)(module->module()->getKey()))->setIntros(false);
        }

        bool locked = module->isLocked();
        return locked;
    }
    return false;
}
void BtInstallThread::removeTempFiles()
{
	qDebug("BtInstallThread::removeTempFiles start");

	// (take the remote conf file for this module, take DataPath,
	// take the absolute path of the InstallMgr)

	//sword::InstallSource is = instbackend::source(m_source);
    if (instbackend::isRemote(*m_installSource)) {
		// get the path for the module temp files
		CSwordModuleInfo* mInfo = m_backendForSource->findModuleByName(m_module);
		QString dataPath = mInfo->config(CSwordModuleInfo::AbsoluteDataPath);
		qDebug() << "Delete path:" << dataPath;
		// it's easier to use sword than qt
		sword::FileMgr::removeDir(dataPath.toLatin1().data());
	}
}
const QString Rendering::CChapterDisplay::text( const QList<CSwordModuleInfo*>& modules, const QString& keyName, const CSwordBackend::DisplayOptions displayOptions, const CSwordBackend::FilterOptions filterOptions ) {
	Q_ASSERT( modules.count() >= 1 );
	Q_ASSERT( !keyName.isEmpty() );

	CSwordModuleInfo* module = modules.first();

	if (modules.count() == 1) module->module()->setSkipConsecutiveLinks( true ); //skip empty, linked verses

	CTextRendering::KeyTreeItem::Settings settings;
	settings.keyRenderingFace =
		displayOptions.verseNumbers
		? CTextRendering::KeyTreeItem::Settings::SimpleKey
		: CTextRendering::KeyTreeItem::Settings::NoKey;

	QString startKey = keyName;
	QString endKey = startKey;

	//check whether there's an intro we have to include
	Q_ASSERT((module->type() == CSwordModuleInfo::Bible));

	if (module->type() == CSwordModuleInfo::Bible) {
		((sword::VerseKey*)(module->module()->getKey()))->Headings(1); //HACK: enable headings for VerseKeys

		CSwordBibleModuleInfo* bible = dynamic_cast<CSwordBibleModuleInfo*>(module);
		Q_ASSERT(bible);

		CSwordVerseKey k1(module);
		k1.Headings(1);
		k1.key(keyName);

		if (k1.Chapter() == 1)	k1.Chapter(0); //Chapter 1, start with 0:0, otherwise X:0
		
		k1.Verse(0);

		startKey = k1.key();
		
		if (k1.Chapter() == 0) k1.Chapter(1);
		k1.Verse(bible->verseCount(k1.book(), k1.Chapter()));
		endKey = k1.key();
	}

	CDisplayRendering render(displayOptions, filterOptions);
	return render.renderKeyRange( startKey, endKey, modules, keyName, settings );
}
/** Deletes indices for selected modules */
void BtIndexPage::deleteIndices()
{
	bool indicesDeleted = false;
	
	for (int i = 0; i < m_modsWithIndices->childCount(); i++) {
		if (m_modsWithIndices->child(i)->checkState(0) == Qt::Checked) {
			CSwordModuleInfo* module = CPointers::backend()->findModuleByName(m_modsWithIndices->child(i)->text(0).toUtf8());
			if (module) {
				CSwordModuleInfo::deleteIndexForModule( module->name() );
				indicesDeleted = true;
			}
		}
	}

	// repopulate the list if an action was taken
	if (indicesDeleted) {
		populateModuleList();
	}
}
Exemplo n.º 13
0
	static void searchProgress(char percent, void *data)
	{
		typedef QPair<BtMiniMenu*, CSwordModuleInfo*> Pair;
		
		Pair *p = reinterpret_cast<Pair*>(data);

		if(!p)
			return;

		BtMiniMenu *dialog = p->first;
		CSwordModuleInfo *m = p->second;

		if(dialog && m)
		{
			if(dialog->wasCanceled())
				m->module()->terminateSearch = true;
			dialog->setValue(percent);
		}
	}
void BtIndexPage::deleteOrphanedIndices()
{
	QDir dir(CSwordModuleInfo::getGlobalBaseIndexLocation());
	dir.setFilter(QDir::Dirs);
	CSwordModuleInfo* module;
	
	for (unsigned int i = 0; i < dir.count(); i++) {
		if (dir[i] != "." && dir[i] != "..") {
			if ( (module = CPointers::backend()->findModuleByName(dir[i])) ) { //mod exists
				if (!module->hasIndex()){ //index files found, but wrong version etc.
					CSwordModuleInfo::deleteIndexForModule( dir[i] );
				}
			}
			else{ //no module exists
				if (CBTConfig::get( CBTConfig::autoDeleteOrphanedIndices ) ){
					CSwordModuleInfo::deleteIndexForModule( dir[i] );
				}
			}
		}
	}
}
/** Returns the rendered text using the modules in the list and using the key parameter.
 * The displayoptions and filter options are used, too.
 */
const QString CEntryDisplay::text( const QList<CSwordModuleInfo*>& modules, const QString& keyName, const CSwordBackend::DisplayOptions displayOptions, const CSwordBackend::FilterOptions filterOptions ) {
    CDisplayRendering render(displayOptions, filterOptions);

    //no highlighted key and no extra key link in the text
    CTextRendering::KeyTreeItem::Settings normal_settings(false, CTextRendering::KeyTreeItem::Settings::CompleteShort);
    CSwordModuleInfo* module = modules.first();

    Rendering::CTextRendering::KeyTree tree;

    //in Bibles and Commentaries we need to check if 0:0 and X:0 contain something
    if (module->type() == CSwordModuleInfo::Bible || module->type() == CSwordModuleInfo::Commentary) {
        ((sword::VerseKey*)(module->module()->getKey()))->Headings(1); //HACK: enable headings for VerseKeys

        CSwordVerseKey k1(module);
        k1.Headings(1);
        k1.key(keyName);

        // don't print the key
        CTextRendering::KeyTreeItem::Settings preverse_settings(false, CTextRendering::KeyTreeItem::Settings::NoKey);

        if (k1.Verse() == 1) { //X:1, prepend X:0
            if (k1.Chapter() == 1) { //1:1, also prepend 0:0 before that
                k1.Chapter(0);
                k1.Verse(0);
                if ( k1.rawText().length() > 0 ) {
                    tree.append( new Rendering::CTextRendering::KeyTreeItem(k1.key(), modules, preverse_settings) );
                }
                k1.Chapter(1);
            }
            k1.Verse(0);
            if ( k1.rawText().length() > 0 ) {
				tree.append( new Rendering::CTextRendering::KeyTreeItem(k1.key(), modules, preverse_settings) );
            }
        }
    }
	tree.append( new Rendering::CTextRendering::KeyTreeItem(keyName, modules, normal_settings) );
    QString result(render.renderKeyTree(tree));
    qDeleteAll(tree);
	return result;
}
void BtInstallThread::removeModule()
{
	CSwordModuleInfo* m = CPointers::backend()->findModuleByName(m_module);
	if (m) { //module found?
		QString prefixPath = m->config(CSwordModuleInfo::AbsoluteDataPath) + "/";
		QString dataPath = m->config(CSwordModuleInfo::DataPath);
		if (dataPath.left(2) == "./") {
			dataPath = dataPath.mid(2);
		}

		if (prefixPath.contains(dataPath)) {
			prefixPath.remove( prefixPath.indexOf(dataPath), dataPath.length() );
		}
		else {
			prefixPath = QString::fromLatin1(CPointers::backend()->prefixPath);
		}

		sword::SWMgr mgr(prefixPath.toLatin1());
		//BtInstallMgr iMgr;
		//TODO: use SWModule name, see also removepage
		m_iMgr.removeModule(&mgr, m->name().toLatin1());
	}
}
Exemplo n.º 17
0
void ModuleInterface::updateWorksModel() {
    m_worksModel.clear();
    m_modules.clear();

    QString currentLang = currentLanguage();
    QString currentCat = currentCategory();

    QHash<int, QByteArray> roleNames;
    roleNames[TextRole] =  "modelText";
    m_worksModel.setRoleNames(roleNames);

    BtBookshelfModel* bookshelfModel = CSwordBackend::instance()->model();
    if (bookshelfModel == 0)
        return;
    int count = bookshelfModel->rowCount();
    for (int row=0; row<count; ++row) {
        QModelIndex index = bookshelfModel->index(row);
        CSwordModuleInfo* module = getModule(bookshelfModel, index);
        CSwordModuleInfo::Category category = module->category();
        QString categoryName = module->categoryName(category);
        const CLanguageMgr::Language* language = module->language();
        QString languageName = language->translatedName();
        if (languageName == currentLang &&
                categoryName == currentCat) {
            m_modules << module;
            QString moduleName = module->name();
            QStandardItem* item = new QStandardItem();
            item->setData(moduleName, TextRole);
            m_worksModel.appendRow(item);
        }
    }

    QQuickItem* object = findQmlObject("moduleChooser");
    if (object == 0)
        return;
    object->setProperty("worksModel", QVariant::fromValue(&m_worksModel));
}
Exemplo n.º 18
0
void ModuleInterface::getCategoriesAndLanguages() {

    m_categories.clear();
    m_languages.clear();

    QQuickItem* object = findQmlObject("moduleChooser");
    if (object == 0)
        return;

    BtBookshelfModel* bookshelfModel = CSwordBackend::instance()->model();
    if (bookshelfModel == 0)
        return;
    int count = bookshelfModel->rowCount();
    for (int row=0; row<count; ++row) {
        QModelIndex index = bookshelfModel->index(row);
        CSwordModuleInfo* module = getModule(bookshelfModel, index);
        CSwordModuleInfo::Category category = module->category();
        QString categoryName = module->categoryName(category);
        const CLanguageMgr::Language* language = module->language();
        QString languageName = language->translatedName();
        m_categories.insert(categoryName);
        m_languages.insert(languageName);
    }
}
void Filters::BT_OSISHTML::renderReference(const char *osisRef, sword::SWBuf &buf, sword::SWModule *myModule, BT_UserData *myUserData) {
	QString ref( osisRef );
	QString hrefRef( ref );
	//Q_ASSERT(!ref.isEmpty()); checked later

	if (!ref.isEmpty()) {
		//find out the mod, using the current module makes sense if it's a bible or commentary because the refs link into a bible by default.
		//If the osisRef is something like "ModuleID:key comes here" then the
		// modulename is given, so we'll use that one

		CSwordModuleInfo* mod = CPointers::backend()->findSwordModuleByPointer(myModule);
		//Q_ASSERT(mod); checked later
		if (!mod || (mod->type() != CSwordModuleInfo::Bible
				&& mod->type() != CSwordModuleInfo::Commentary)) {

			mod = CBTConfig::get( CBTConfig::standardBible );
		}

		// Q_ASSERT(mod); There's no necessarily a module or standard Bible

		//if the osisRef like "GerLut:key" contains a module, use that
		int pos = ref.indexOf(":");

		if ((pos >= 0) && ref.at(pos-1).isLetter() && ref.at(pos+1).isLetter()) {
			QString newModuleName = ref.left(pos);
			hrefRef = ref.mid(pos+1);

			if (CPointers::backend()->findModuleByName(newModuleName)) {
				mod = CPointers::backend()->findModuleByName(newModuleName);
			}
		}

		if (mod) {
			CReferenceManager::ParseOptions options;
			options.refBase = QString::fromUtf8(myUserData->key->getText());
			options.refDestinationModule = QString(mod->name());
			options.sourceLanguage = QString(myModule->Lang());
			options.destinationLanguage = QString("en");

			buf.append("<a href=\"");
			buf.append( //create the hyperlink with key and mod
				CReferenceManager::encodeHyperlink(
					mod->name(),
					CReferenceManager::parseVerseReference(hrefRef, options),
					CReferenceManager::typeFromModule(mod->type())
				).toUtf8().constData()
			);
			buf.append("\" crossrefs=\"");
			buf.append((const char*)CReferenceManager::parseVerseReference(ref, options).toUtf8().constData()); //ref must contain the osisRef module marker if there was any
			buf.append("\">");
		}
		// should we add something if there were no referenced module available?
	}
}
Exemplo n.º 20
0
QString remoteModuleToolTip(const CSwordModuleInfo & module,
                            const QString & localVer)
{
    QString text = "<p style='white-space:pre'><b>";
    text += module.name();
    text += "</b> ";

    if (module.category() == CSwordModuleInfo::Cult) {
        text += "<small><b>";
        text += QObject::tr("Take care, this work contains cult / questionable "
                            "material!");
        text += "</b></small><br/>";
    }

    text += "<small>(";
    text += module.config(CSwordModuleInfo::Description);
    text += ")</small><hr/>";

    if (module.isEncrypted()) {
        text += QObject::tr("Encrypted - needs unlock key");
        text += "<br/>";
    }

    if (!localVer.isEmpty()) {
        text += "<b>";
        text += QObject::tr("Updated version available!");
        text += "</b><br/>";
    }

    if (module.hasVersion()) {
        text += QObject::tr("Version");
        text += ": ";
        text += module.config(CSwordModuleInfo::ModuleVersion);
    }

    // if installed already
    if (!localVer.isEmpty()) {
        text += "  ";
        text += QObject::tr("Installed version");
        text += ": ";
        text += localVer;
    }
    text += "<br/><small>(";
    text += QObject::tr("Double click for more information");
    text += ")</small></p>";

    return text;
}
/*!
	\fn CInfoDisplay::decodeFootnote( const QString& data )
	*/
const QString CInfoDisplay::decodeFootnote( const QString& data ) {
	QStringList list = data.split("/");
	Q_ASSERT(list.count() >= 3);
	if (!list.count()) {
		return QString::null;
	}

	const QString modulename = list.first();
	const QString swordFootnote = list.last();

	// remove the first and the last and then rejoin it to get a key
	list.pop_back(); list.pop_front();
	const QString keyname = list.join("/");

	CSwordModuleInfo* module = CPointers::backend()->findModuleByName(modulename);
	if (!module) {
		return QString::null;
	}

	boost::scoped_ptr<CSwordKey> key( CSwordKey::createInstance(module) );
	key->key(keyname);
	key->renderedText(); //force entryAttributes

	const char* note =
		module->module()->getEntryAttributes()
			["Footnote"][swordFootnote.toLatin1().data()]["body"].c_str();

	QString text = module->isUnicode() ? QString::fromUtf8(note) : QString(note);
	text = QString::fromUtf8(module->module()->RenderText(
									module->isUnicode()
									? (const char*)text.toUtf8()
									: (const char*)text.toLatin1()
								));

	return QString("<div class=\"footnoteinfo\" lang=\"%1\"><h3>%2</h3><p>%3</p></div>")
            .arg(module->language()->abbrev())
			.arg(tr("Footnote"))
			.arg(text);
}
Exemplo n.º 22
0
void BtSearchResultArea::updatePreview(const QString& key) {
    using namespace Rendering;

    CSwordModuleInfo* module = m_moduleListBox->activeModule();
    if ( module ) {
        const QString searchedText = CSearchDialog::getSearchDialog()->searchText();

        QString text;
        CDisplayRendering render;

        BtConstModuleList modules;
        modules.append(module);

        CTextRendering::KeyTreeItem::Settings settings;

        //for bibles render 5 context verses
        if (module->type() == CSwordModuleInfo::Bible) {
            CSwordVerseKey vk(module);
            vk.setIntros(true);
            vk.setKey(key);

            // HACK: enable headings for VerseKeys:
            static_cast<sword::VerseKey *>(module->module()->getKey())
                    ->setIntros(true);

            //first go back and then go forward the keys to be in context
            vk.previous(CSwordVerseKey::UseVerse);
            vk.previous(CSwordVerseKey::UseVerse);

            //include Headings in display, they are indexed and searched too
            if (vk.getVerse() == 1) {
                if (vk.getChapter() == 1) {
                    vk.setChapter(0);
                }
                vk.setVerse(0);
            }

            const QString startKey = vk.key();

            vk.setKey(key);

            vk.next(CSwordVerseKey::UseVerse);
            vk.next(CSwordVerseKey::UseVerse);
            const QString endKey = vk.key();

            settings.keyRenderingFace = CTextRendering::KeyTreeItem::Settings::CompleteShort;
            text = render.renderKeyRange(startKey, endKey, modules, key, settings);
        }
        //for commentaries only one verse, but with heading
        else if (module->type() == CSwordModuleInfo::Commentary) {
            CSwordVerseKey vk(module);
            vk.setIntros(true);
            vk.setKey(key);

            // HACK: enable headings for VerseKeys:
            static_cast<sword::VerseKey *>(module->module()->getKey())
                    ->setIntros(true);

            //include Headings in display, they are indexed and searched too
            if (vk.getVerse() == 1) {
                if (vk.getChapter() == 1) {
                    vk.setChapter(0);
                }
                vk.setVerse(0);
            }
            const QString startKey = vk.key();

            vk.setKey(key);
            const QString endKey = vk.key();

            settings.keyRenderingFace = CTextRendering::KeyTreeItem::Settings::NoKey;
            text = render.renderKeyRange(startKey, endKey, modules, key, settings);
        }
        else {
            text = render.renderSingleKey(key, modules, settings);
        }

        m_previewDisplay->setText( CSwordModuleSearch::highlightSearchedText(text, searchedText) );
        m_previewDisplay->moveToAnchor( CDisplayRendering::keyToHTMLAnchor(key) );
    }
}
char Filters::BT_ThMLHTML::processText(sword::SWBuf& buf, const sword::SWKey* key, const sword::SWModule* module) {
	sword::ThMLHTML::processText(buf, key, module);

	CSwordModuleInfo* m = CPointers::backend()->findModuleByName( module->Name() );

	if (m && !(m->has(CSwordModuleInfo::lemmas) || m->has(CSwordModuleInfo::strongNumbers))) { //only parse if the module has strongs or lemmas
		return 1;
	}

	QString result;

	QString t = QString::fromUtf8(buf.c_str());
	QRegExp tag("([.,;]?<sync[^>]+(type|value)=\"([^\"]+)\"[^>]+(type|value)=\"([^\"]+)\"([^<]*)>)+");

	QStringList list;
	int lastMatchEnd = 0;
	int pos = tag.indexIn(t,0);

	if (pos == -1) { //no strong or morph code found in this text
		return 1; //WARNING: Return alread here
	}

	while (pos != -1) {
		list.append(t.mid(lastMatchEnd, pos+tag.matchedLength()-lastMatchEnd));

		lastMatchEnd = pos+tag.matchedLength();
		pos = tag.indexIn(t,pos+tag.matchedLength());
	}

	if (!t.right(t.length() - lastMatchEnd).isEmpty()) {
		list.append(t.right(t.length() - lastMatchEnd));
	}

	tag = QRegExp("<sync[^>]+(type|value|class)=\"([^\"]+)\"[^>]+(type|value|class)=\"([^\"]+)\"[^>]+((type|value|class)=\"([^\"]+)\")*([^<]*)>");

	for (QStringList::iterator it = list.begin(); it != list.end(); ++it) {
		QString e( *it );

		const bool textPresent = (e.trimmed().remove(QRegExp("[.,;:]")).left(1) != "<");

		if (!textPresent) {
			continue;
		}


		bool hasLemmaAttr = false;
		bool hasMorphAttr = false;

		int pos = tag.indexIn(e, 0);
		bool insertedTag = false;
		QString value;
		QString valueClass;

		while (pos != -1) {
			bool isMorph = false;
			bool isStrongs = false;
			value = QString::null;
			valueClass = QString::null;

			// check 3 attribute/value pairs

			for (int i = 1; i < 6; i += 2) {
				if (i > 4)
					i++;

				if (tag.cap(i) == "type") {
					isMorph   = (tag.cap(i+1) == "morph");
					isStrongs = (tag.cap(i+1) == "Strongs");
				}
				else if (tag.cap(i) == "value") {
					value = tag.cap(i+1);
				}
				else if (tag.cap(i) == "class") {
					valueClass = tag.cap(i+1);
				}
			}

			// prepend the class qualifier to the value
			if (!valueClass.isEmpty()) {
				value = valueClass + ":" + value;
				//     value.append(":").append(value);
			}

			if (value.isEmpty()) {
				break;
			}

			//insert the span
			if (!insertedTag) {
				e.replace(pos, tag.matchedLength(), "</span>");
				pos += 7;

				QString rep = QString("<span lemma=\"").append(value).append("\">");
				int startPos = 0;
				QChar c = e[startPos];

				while ((startPos < pos) && (c.isSpace() || c.isPunct())) {
					++startPos;
					c = e[startPos];
				}

				hasLemmaAttr = isStrongs;
				hasMorphAttr = isMorph;

				e.insert( startPos, rep );
				pos += rep.length();
			}
			else { //add the attribute to the existing tag
				e.remove(pos, tag.matchedLength());

				if ((!isMorph && hasLemmaAttr) || (isMorph && hasMorphAttr)) { //we append another attribute value, e.g. 3000 gets 3000|5000
					//search the existing attribute start
					QRegExp attrRegExp( isMorph ? "morph=\".+(?=\")" : "lemma=\".+(?=\")" );
					attrRegExp.setMinimal(true);
					const int foundAttrPos = e.indexOf(attrRegExp, pos);

					if (foundAttrPos != -1) {
						e.insert(foundAttrPos + attrRegExp.matchedLength(), QString("|").append(value));
						pos += value.length() + 1;

						hasLemmaAttr = !isMorph;
						hasMorphAttr = isMorph;
					}
				}
				else { //attribute was not yet inserted
					const int attrPos = e.indexOf(QRegExp("morph=|lemma="), 0);

					if (attrPos >= 0) {
						QString attr;
						attr.append(isMorph ? "morph" : "lemma").append("=\"").append(value).append("\" ");
						e.insert(attrPos, attr);

						hasMorphAttr = isMorph;
						hasLemmaAttr = !isMorph;

						pos += attr.length();
					}
				}
			}

			insertedTag = true;
			pos = tag.indexIn(e, pos);
		}

		result.append( e );
	}

	if (list.count()) {
		buf = (const char*)result.toUtf8();
	}

	return 1;
}
bool Filters::BT_ThMLHTML::handleToken(sword::SWBuf &buf, const char *token, sword::BasicFilterUserData *userData) {
	if (!substituteToken(buf, token) && !substituteEscapeString(buf, token)) {
		sword::XMLTag tag(token);
		BT_UserData* myUserData = dynamic_cast<BT_UserData*>(userData);
		sword::SWModule* myModule = const_cast<sword::SWModule*>(myUserData->module); //hack to be able to call stuff like Lang()

		if ( tag.getName() && !sword::stricmp(tag.getName(), "foreign") ) { // a text part in another language, we have to set the right font

			if (tag.getAttribute("lang")) {
				const char* abbrev = tag.getAttribute("lang");
				//const CLanguageMgr::Language* const language = CPointers::languageMgr()->languageForAbbrev( QString::fromLatin1(abbrev) );

				buf.append("<span class=\"foreign\" lang=\"");
				buf.append(abbrev);
				buf.append("\">");
			}
		}
		else if (tag.getName() && !sword::stricmp(tag.getName(), "sync")) { //lemmas, morph codes or strongs

			if (tag.getAttribute("type") && (!sword::stricmp(tag.getAttribute("type"), "morph") || !sword::stricmp(tag.getAttribute("type"), "Strongs") || !sword::stricmp(tag.getAttribute("type"), "lemma"))) { // Morph or Strong
				buf.append('<');
				buf.append(token);
				buf.append('>');
			}
		}
		else if (tag.getName() && !sword::stricmp(tag.getName(), "note")) { // <note> tag

			if (!tag.isEndTag() && !tag.isEmpty()) {
				//appending is faster than appendFormatted
				buf.append(" <span class=\"footnote\" note=\"");
				buf.append(myModule->Name());
				buf.append('/');
				buf.append(myUserData->key->getShortText());
				buf.append('/');
				buf.append( QString::number(myUserData->swordFootnote++).toUtf8().constData() );
				buf.append("\">*</span> ");

				myUserData->suspendTextPassThru = true;
				myUserData->inFootnoteTag = true;
			}
			else if (tag.isEndTag() && !tag.isEmpty()) { //end tag
				//buf += ")</span>";
				myUserData->suspendTextPassThru = false;
				myUserData->inFootnoteTag = false;
			}
		}
		else if (tag.getName() && !sword::stricmp(tag.getName(), "scripRef")) { // a scripRef
			//scrip refs which are embeded in footnotes may not be displayed!

			if (!myUserData->inFootnoteTag) {
				if (tag.isEndTag()) {
					if (myUserData->inscriptRef) { // like "<scripRef passage="John 3:16">See John 3:16</scripRef>"
						buf.append("</a></span>");

						myUserData->inscriptRef = false;
						myUserData->suspendTextPassThru = false;
					}
					else { // like "<scripRef>John 3:16</scripRef>"

						CSwordModuleInfo* mod = CBTConfig::get(CBTConfig::standardBible);
						//Q_ASSERT(mod); tested later
						if (mod) {
							CReferenceManager::ParseOptions options;
							options.refBase = QString::fromUtf8(myUserData->key->getText()); //current module key
							options.refDestinationModule = QString(mod->name());
							options.sourceLanguage = QString(myModule->Lang());
 							options.destinationLanguage = QString("en");

							//it's ok to split the reference, because to descriptive text is given
							bool insertSemicolon = false;
							buf.append("<span class=\"crossreference\">");
							QStringList refs = QString::fromUtf8(myUserData->lastTextNode.c_str()).split(";");
							QString oldRef; //the previous reference to use as a base for the next refs
							for (QStringList::iterator it(refs.begin()); it != refs.end(); ++it) {

								if (! oldRef.isEmpty() ){
									options.refBase = oldRef; //use the last ref as a base, e.g. Rom 1,2-3, when the next ref is only 3:3-10
								}
								const QString completeRef( CReferenceManager::parseVerseReference((*it), options) );

								oldRef = completeRef; //use the parsed result as the base for the next ref.

 								if (insertSemicolon) { //prepend a ref divider if we're after the first one
									buf.append("; ");
 								}

								buf.append("<a href=\"");
								buf.append(
									CReferenceManager::encodeHyperlink(
										mod->name(),
										completeRef,
										CReferenceManager::typeFromModule(mod->type())
									).toUtf8().constData()
								);

								buf.append("\" crossrefs=\"");
								buf.append((const char*)completeRef.toUtf8());
								buf.append("\">");

								buf.append((const char*)(*it).toUtf8());

								buf.append("</a>");

								insertSemicolon = true;
							}
							buf.append("</span>"); //crossref end
						}

						myUserData->suspendTextPassThru = false;
					}
				}
				else if (tag.getAttribute("passage") ) { //the passage was given as a parameter value
					myUserData->inscriptRef = true;
					myUserData->suspendTextPassThru = false;

					const char* ref = tag.getAttribute("passage");
					Q_ASSERT(ref);

					CSwordModuleInfo* mod = CBTConfig::get(CBTConfig::standardBible);
					//Q_ASSERT(mod); tested later

					CReferenceManager::ParseOptions options;
					options.refBase = QString::fromUtf8(myUserData->key->getText());
					options.refDestinationModule = QString(mod->name());
					options.sourceLanguage = myModule->Lang();
					options.destinationLanguage = QString("en");

					const QString completeRef = CReferenceManager::parseVerseReference(QString::fromUtf8(ref), options);

					if (mod) {
						buf.append("<span class=\"crossreference\">");
						buf.append("<a href=\"");
						buf.append(
 							CReferenceManager::encodeHyperlink(
 								mod->name(),
 								completeRef,
 								CReferenceManager::typeFromModule(mod->type())
							).toUtf8().constData()
						);
						buf.append("\" crossrefs=\"");
						buf.append((const char*)completeRef.toUtf8());
						buf.append("\">");
					}
					else {
						buf.append("<span><a>");
					}
				}
				else if ( !tag.getAttribute("passage") ) { // we're starting a scripRef like "<scripRef>John 3:16</scripRef>"
					myUserData->inscriptRef = false;

					// let's stop text from going to output, the text get's added in the -tag handler
					myUserData->suspendTextPassThru = true;
				}
			}
		}
		else if (tag.getName() && !sword::stricmp(tag.getName(), "div")) {
			if (tag.isEndTag()) {
				buf.append("</div>");
			}
			else if ( tag.getAttribute("class") && !sword::stricmp(tag.getAttribute("class"),"sechead") ) {
				buf.append("<div class=\"sectiontitle\">");
			}
			else if (tag.getAttribute("class") && !sword::stricmp(tag.getAttribute("class"), "title")) {
				buf.append("<div class=\"booktitle\">");
			}
		}
		else if (tag.getName() && !sword::stricmp(tag.getName(), "img") && tag.getAttribute("src")) {
			const char* value = tag.getAttribute("src");

			if (value[0] == '/') {
				value++; //strip the first /
			}

			buf.append("<img src=\"file:");
			buf.append(myUserData->module->getConfigEntry("AbsoluteDataPath"));
			buf.append('/');
			buf.append(value);
			buf.append("\" />");
		}
		else { // let unknown token pass thru
			return sword::ThMLHTML::handleToken(buf, token, userData);
		}
	}

	return true;
}
bool Filters::BT_OSISHTML::handleToken(sword::SWBuf &buf, const char *token, sword::BasicFilterUserData *userData) {
	// manually process if it wasn't a simple substitution

	if (!substituteToken(buf, token)) {
		BT_UserData* myUserData = dynamic_cast<BT_UserData*>(userData);
		sword::SWModule* myModule = const_cast<sword::SWModule*>(myUserData->module); //hack

		sword::XMLTag tag(token);
		//     qWarning("found %s", token);
		const bool osisQToTick = ((!userData->module->getConfigEntry("OSISqToTick")) || (strcmp(userData->module->getConfigEntry("OSISqToTick"), "false")));

		if (!tag.getName()) {
			return false;
		}

		// <div> tag
		if (!strcmp(tag.getName(), "div")) {
			//handle intro

			if ((!tag.isEmpty()) && (!tag.isEndTag())) { //start tag
				sword::SWBuf type( tag.getAttribute("type") );

				if (type == "introduction") {
					buf.append("<div class=\"introduction\">");
				}
				else if (type == "chapter") {
					buf.append("<div class=\"chapter\" />"); //don't open a div here, that would lead to a broken XML structure
				}
				else {
					buf.append("<div>");
				}
			}
			else if (tag.isEndTag()) { //end tag
				buf.append("</div>");
			}
		}
		else if (!strcmp(tag.getName(), "w")) {
			if ((!tag.isEmpty()) && (!tag.isEndTag())) { //start tag
				const char *attrib;
				const char *val;

				sword::XMLTag outTag("span");
				sword::SWBuf attrValue;

				if ((attrib = tag.getAttribute("xlit"))) {
					val = strchr(attrib, ':');
					val = (val) ? (val + 1) : attrib;
					outTag.setAttribute("xlit", val);
				}

				if ((attrib = tag.getAttribute("gloss"))) {
					val = strchr(attrib, ':');
					val = (val) ? (val + 1) : attrib;
					outTag.setAttribute("gloss", val);
				}

				if ((attrib = tag.getAttribute("lemma"))) {
 					char splitChar = '|';
					const int countSplit1 = tag.getAttributePartCount("lemma", '|');
					const int countSplit2 = tag.getAttributePartCount("lemma", ' '); //TODO: not allowed, remove soon
					int count = 0;
					
					if (countSplit1 > countSplit2) { //| split char
						splitChar = '|'; //TODO: not allowed, remove soon
						count = countSplit1;
					}
					else {
						splitChar = ' ';
						count = countSplit2;
					}
					
					int i = (count > 1) ? 0 : -1;  // -1 for whole value cuz it's faster, but does the same thing as 0
					attrValue = "";

					do {
						if (attrValue.length()) {
							attrValue.append( '|' );
						}

						attrib = tag.getAttribute("lemma", i, splitChar);

						if (i < 0) { // to handle our -1 condition
							i = 0;
						}

						val = strchr(attrib, ':');
						val = (val) ? (val + 1) : attrib;

						attrValue.append(val);
					}
					while (++i < count);

					if (attrValue.length()) {
						outTag.setAttribute("lemma", attrValue.c_str());
					}
				}

				if ((attrib = tag.getAttribute("morph"))) {
					char splitChar = '|';
					const int countSplit1 = tag.getAttributePartCount("morph", '|');
					const int countSplit2 = tag.getAttributePartCount("morph", ' '); //TODO: not allowed, remove soon
					int count = 0;
					
					if (countSplit1 > countSplit2) { //| split char
						splitChar = '|';
						count = countSplit1;
					}
					else {
						splitChar = ' ';
						count = countSplit2;
					}

					int i = (count > 1) ? 0 : -1;  // -1 for whole value cuz it's faster, but does the same thing as 0

					attrValue = "";

					do {
						if (attrValue.length()) {
							attrValue.append('|');
						}

						attrib = tag.getAttribute("morph", i, splitChar);

						if (i < 0) {
							i = 0; // to handle our -1 condition
						}

						val = strchr(attrib, ':');

						if (val) { //the prefix gives the modulename
							//check the prefix
							if (!strncmp("robinson:", attrib, 9)) { //robinson
								attrValue.append( "Robinson:" ); //work is not the same as Sword's module name
								attrValue.append( val+1 );
							}
							//strongs is handled by BibleTime
							/*else if (!strncmp("strongs", attrib, val-atrrib)) {
								attrValue.append( !strncmp(attrib, "x-", 2) ? attrib+2 : attrib );
							}*/
							else {
								attrValue.append( !strncmp(attrib, "x-", 2) ? attrib+2 : attrib );
							}
						}
						else { //no prefix given
							const bool skipFirst = ((val[0] == 'T') && ((val[1] == 'H') || (val[1] == 'H')));
							attrValue.append( skipFirst ? val+1 : val );
						}
					}
					while (++i < count);

					if (attrValue.length()) {
						outTag.setAttribute("morph", attrValue.c_str());
					}
				}

				if ((attrib = tag.getAttribute("POS"))) {
					val = strchr(attrib, ':');
					val = (val) ? (val + 1) : attrib;
					outTag.setAttribute("pos", val);
				}

				buf.append( outTag.toString() );
			}
			else if (tag.isEndTag()) { // end or empty <w> tag
				buf.append("</span>");
			}
		}

		// <note> tag
		else if (!strcmp(tag.getName(), "note")) {
			if (!tag.isEndTag()) { //start tag
				const sword::SWBuf type( tag.getAttribute("type") );

				if (type == "crossReference") { //note containing cross references
					myUserData->inCrossrefNote = true;
					myUserData->noteType = BT_UserData::CrossReference;
					myUserData->swordFootnote++; // cross refs count as notes, too

					/*     //get the refList value of the right entry attribute
					     AttributeList notes = myModule->getEntryAttributes()["Footnote"];
					     bool foundNote = false;

					     SWBuf id( tag.getAttribute("osisID") );
					     SWBuf refList;

					     for (AttributeList::iterator list_it = notes.begin(); (list_it != notes.end()) && !foundNote; ++list_it ) {
					      for (AttributeValue::iterator val_it = list_it->second.begin(); (val_it != list_it->second.end()) && !foundNote; ++val_it ) {
					       if ((val_it->first == "osisID") && (val_it->second == id)) {
					        foundNote = true; //this break the loop
					        refList = list_it->second["refList"];
					       }
					      }
					     }

					     if (refList.length()) {
					      buf.append(" <span class=\"crossreference\" crossrefs=\"");
					      buf.append(refList.c_str());
					      buf.append("\"> ");

					           myUserData->noteType = BT_UserData::CrossReference;
					     }
					     else {
					      myUserData->noteType = BT_UserData::Unknown;
					     }*/

					buf.append("<span class=\"crossreference\">");
				}

				/* else if (type == "explanation") {
				     }
				     */
				else if ((type == "strongsMarkup") || (type == "x-strongsMarkup")) {
					/**
					* leave strong's markup notes out, in the future we'll probably have
					* different option filters to turn different note types on or off
					*/

					myUserData->suspendTextPassThru = true;
					myUserData->noteType = BT_UserData::StrongsMarkup;
				}

				else {
					//           qWarning("found note in %s", myUserData->key->getShortText());
					buf.append(" <span class=\"footnote\" note=\"");
					buf.append(myModule->Name());
					buf.append('/');
					buf.append(myUserData->key->getShortText());
					buf.append('/');
					buf.append( QString::number(myUserData->swordFootnote++).toUtf8().constData() ); //inefficient

					const sword::SWBuf n = tag.getAttribute("n");
					
					buf.append("\">");
					buf.append( (n.length() > 0) ? n.c_str() : "*" );
					buf.append("</span> ");

					myUserData->noteType = BT_UserData::Footnote;
					myUserData->suspendTextPassThru = true;
				}
			}
			else { //if (tag.isEndTag()) {
				Q_ASSERT(myUserData->noteType != BT_UserData::Unknown);

				if (myUserData->noteType == BT_UserData::CrossReference) {
					buf.append("</span> ");
// 					myUserData->suspendTextPassThru = false;
					myUserData->inCrossrefNote = false;
				}

				myUserData->noteType = BT_UserData::Unknown;
				myUserData->suspendTextPassThru = false;
			}
		}
		// The <p> paragraph tag is handled by OSISHTMLHref
		else if (!strcmp(tag.getName(), "reference")) { // <reference> tag

			if (!tag.isEndTag() && !tag.isEmpty()) {
				QString ref( tag.getAttribute("osisRef") );
				QString hrefRef( ref );
				Q_ASSERT(!ref.isEmpty());

				if (!ref.isEmpty()) {
					//find out the mod, using the current module makes sense if it's a bible or commentary because the refs link into a bible by default.
					//If the osisRef is something like "ModuleID:key comes here" then the
					// modulename is given, so we'll use that one

					CSwordModuleInfo* mod = CPointers::backend()->findSwordModuleByPointer(myModule);
					Q_ASSERT(mod);
					if (!mod || (mod->type() != CSwordModuleInfo::Bible
							&& mod->type() != CSwordModuleInfo::Commentary)) {

						mod = CBTConfig::get( CBTConfig::standardBible );
					}

					Q_ASSERT(mod);

					//if the osisRef like "GerLut:key" contains a module, use that
					int pos = ref.indexOf(":");

					if ((pos >= 0) && ref.at(pos-1).isLetter() && ref.at(pos+1).isLetter()) {
						QString newModuleName = ref.left(pos);
						hrefRef = ref.mid(pos+1);

						if (CPointers::backend()->findModuleByName(newModuleName)) {
							mod = CPointers::backend()->findModuleByName(newModuleName);
						}
					}

					CReferenceManager::ParseOptions options;
					options.refBase = QString::fromUtf8(myUserData->key->getText());
					options.refDestinationModule = QString(mod->name());
					options.sourceLanguage = QString(myModule->Lang());
					options.destinationLanguage = QString("en");

					buf.append("<a href=\"");
					buf.append( //create the hyperlink with key and mod
						CReferenceManager::encodeHyperlink(
							mod->name(),
							CReferenceManager::parseVerseReference(hrefRef, options),
							CReferenceManager::typeFromModule(mod->type())
						).toUtf8().constData()
					);
					buf.append("\" crossrefs=\"");
					buf.append((const char*)CReferenceManager::parseVerseReference(ref, options).toUtf8().constData()); //ref must contain the osisRef module marker if there was any
					buf.append("\">");
				}
			}
			else if (tag.isEndTag()) {
				buf.append("</a>");
			}
			else { // empty reference marker
				// -- what should we do?  nothing for now.
			}
		}

		// <l> is handled by OSISHTMLHref
		// <title>
		else if (!strcmp(tag.getName(), "title")) {
			if (!tag.isEndTag() && !tag.isEmpty()) {
				buf.append("<div class=\"sectiontitle\">");
			}
			else if (tag.isEndTag()) {
				buf.append("</div>");
			}
			else { // empty title marker
				// what to do?  is this even valid?
				buf.append("<br/>");
			}
		}

		// <hi> highlighted text
		else if (!strcmp(tag.getName(), "hi")) {
			const sword::SWBuf type = tag.getAttribute("type");

			if ((!tag.isEndTag()) && (!tag.isEmpty())) {
				if (type == "bold") {
					buf.append("<span class=\"bold\">");
				}
				else if (type == "illuminated") {
					buf.append("<span class=\"illuminated\">");
				}
				else if (type == "italic") {
					buf.append("<span class=\"italic\">");
				}
				else if (type == "line-through") {
					buf.append("<span class=\"line-through\">");
				}
				else if (type == "normal") {
					buf.append("<span class=\"normal\">");
				}
				else if (type == "small-caps") {
					buf.append("<span class=\"small-caps\">");
				}
				else if (type == "underline") {
					buf.append("<span class=\"underline\">");
				}
				else {
					buf.append("<span>"); //don't break markup, </span> is inserted later
				}
			}
			else if (tag.isEndTag()) { //all hi replacements are html spans
				buf.append("</span>");
			}
		}

		//name
		else if (!strcmp(tag.getName(), "name")) {
			const sword::SWBuf type = tag.getAttribute("type");

			if ((!tag.isEndTag()) && (!tag.isEmpty())) {
				if (type == "geographic") {
					buf.append("<span class=\"name\"><span class=\"geographic\">");
				}
				else if (type == "holiday") {
					buf.append("<span class=\"name\"><span class=\"holiday\">");
				}
				else if (type == "nonhuman") {
					buf.append("<span class=\"name\"><span class=\"nonhuman\">");
				}
				else if (type == "person") {
					buf.append("<span class=\"name\"><span class=\"person\">");
				}
				else if (type == "ritual") {
					buf.append("<span class=\"name\"><span class=\"ritual\">");
				}
				else {
					buf.append("<span class=\"name\"><span>");
				}
			}
			else if (tag.isEndTag()) { //all hi replacements are html spans
				buf.append("</span></span> ");
			}
		}
		else if (!strcmp(tag.getName(), "transChange")) {
			sword::SWBuf type( tag.getAttribute("type") );

			if ( !type.length() ) {
				type = tag.getAttribute("changeType");
			}

			if ((!tag.isEndTag()) && (!tag.isEmpty())) {
				if (type == "added") {
					buf.append("<span class=\"transchange\" title=\"");
					buf.append(QObject::tr("Added text").toUtf8().constData());
					buf.append("\"><span class=\"added\">");
				}
				else if (type == "amplified") {
					buf.append("<span class=\"transchange\"><span class=\"amplified\">");
				}
				else if (type == "changed") {
					buf.append("<span class=\"transchange\"><span class=\"changed\">");
				}
				else if (type == "deleted") {
					buf.append("<span class=\"transchange\"><span class=\"deleted\">");
				}
				else if (type == "moved") {
					buf.append("<span class=\"transchange\"><span class=\"moved\">");
				}
				else if (type == "tenseChange") {
					buf.append("<span class=\"transchange\" title=\"");
					buf.append(QObject::tr("Verb tense changed").toUtf8().constData());
					buf.append("\"><span class=\"tenseChange\">");
				}
				else {
					buf.append("<span class=\"transchange\"><span>");
				}
			}
			else if (tag.isEndTag()) { //all hi replacements are html spans
				buf.append("</span></span>");
			}
		}
		else if (!strcmp(tag.getName(), "p")) {
			if (tag.isEmpty()) {
				buf.append("<p/>");
			}
		}

		// <q> quote
		else if (!strcmp(tag.getName(), "q")) {
			sword::SWBuf type = tag.getAttribute("type");
			sword::SWBuf who = tag.getAttribute("who");
			const char *lev = tag.getAttribute("level");
			int level = (lev) ? atoi(lev) : 1;

			if ((!tag.isEndTag()) && (!tag.isEmpty())) {
				myUserData->quote.who = who;

				if(osisQToTick) //alternate " and '
					buf.append((level % 2) ? '\"' : '\'');

				if (who == "Jesus") {
					buf.append("<span class=\"jesuswords\">");
				}
			}
			else if (tag.isEndTag()) {
				if (myUserData->quote.who == "Jesus") {
					buf.append("</span>");
				}

				if (osisQToTick) { //alternate " and '
					buf.append((level % 2) ? '\"' : '\'');
				}

				myUserData->quote.who = "";
			}
		}

		// abbr tag
		else if (!strcmp(tag.getName(), "abbr")) {
			if (!tag.isEndTag() && !tag.isEmpty()) {
				const sword::SWBuf expansion = tag.getAttribute("expansion");

				buf.append("<span class=\"abbreviation\" expansion=\"");
				buf.append(expansion);
				buf.append("\">");
			}
			else if (tag.isEndTag()) {
				buf.append("</span>");
			}
		}

		// <milestone> tag
		else if (!strcmp(tag.getName(), "milestone")) {
			const sword::SWBuf type = tag.getAttribute("type");

			if ((type == "screen") || (type == "line")) {//line break
				buf.append("<br/>");
				userData->supressAdjacentWhitespace = true;
			}
			else if (type == "x-p") { //e.g. occurs in the KJV2006 module
				//buf.append("<br/>");
				const sword::SWBuf marker = tag.getAttribute("marker");
				if (marker.length() > 0) {
					buf.append(marker);
				}
			}
		}
		//seg tag
		else if (!strcmp(tag.getName(), "seg")) {
			if (!tag.isEndTag() && !tag.isEmpty()) {

				const sword::SWBuf type = tag.getAttribute("type");

				if (type == "morph") {//line break
					//This code is for WLC and MORPH (WHI)
					sword::XMLTag outTag("span");
					outTag.setAttribute("class", "morphSegmentation");
					const char* attrValue;
					//Transfer the values to the span
					//Problem: the data is in hebrew/aramaic, how to encode in HTML/BibleTime?
					if ((attrValue = tag.getAttribute("lemma"))) outTag.setAttribute("lemma", attrValue);
					if ((attrValue = tag.getAttribute("morph"))) outTag.setAttribute("morph", attrValue);
					if ((attrValue = tag.getAttribute("homonym"))) outTag.setAttribute("homonym", attrValue);

					buf.append(outTag.toString());
					//buf.append("<span class=\"morphSegmentation\">");
				}
				else{
					buf.append("<span>");
				}
			}
			else { // seg end tag
				buf.append("</span>");
			}
			//qWarning(QString("handled <seg> token. result: %1").arg(buf.c_str()).latin1());
		}
		
		//divine name, don't use simple tag replacing because it may have attributes
		else if (!strcmp(tag.getName(), "divineName")) {
			if (!tag.isEndTag()) {
				buf.append("<span class=\"name\"><span class=\"divine\">");
			}
			else { //all hi replacements are html spans
				buf.append("</span></span>");
			}
		}
		
		else { //all tokens handled by OSISHTMLHref will run through the filter now
			return sword::OSISHTMLHREF::handleToken(buf, token, userData);
		}
	}

	return false;
}
const QString CInfoDisplay::decodeCrossReference( const QString& data ) {
	Q_ASSERT(!data.isEmpty());
	if (data.isEmpty()) {
		return QString("<div class=\"crossrefinfo\"><h3>%1</h3></div>")
				.arg(tr("Cross references"));
	}

	//  qWarning("setting crossref %s", data.latin1());

	CSwordBackend::DisplayOptions dispOpts;
	dispOpts.lineBreaks  = false;
	dispOpts.verseNumbers = true;

	CSwordBackend::FilterOptions filterOpts;
	filterOpts.headings    = false;
	filterOpts.strongNumbers  = false;
	filterOpts.morphTags    = false;
	filterOpts.lemmas     = false;
	filterOpts.footnotes   = false;
	filterOpts.scriptureReferences = false;

	CrossRefRendering renderer(dispOpts, filterOpts);
	CTextRendering::KeyTree tree;

	//  const bool isBible = true;
	CSwordModuleInfo* module = CBTConfig::get
									(CBTConfig::standardBible);

	//a prefixed module gives the module to look into
	QRegExp re("^[^ ]+:");
	//  re.setMinimal(true);
	int pos = re.indexIn(data);
	if (pos != -1) {
		pos += re.matchedLength()-1;
	}

	if (pos > 0) {
		const QString moduleName = data.left(pos);
		//     qWarning("found module %s", moduleName.latin1());
		module = CPointers::backend()->findModuleByName(moduleName);
		if (!module) {
			module = CBTConfig::get
							(CBTConfig::standardBible);
		}
		//   Q_ASSERT(module);
	}

	//Q_ASSERT(module); //why? the existense of the module is tested later
	CTextRendering::KeyTreeItem::Settings settings (
		false,
		CTextRendering::KeyTreeItem::Settings::CompleteShort
	);

	if (module && (module->type() == CSwordModuleInfo::Bible)) {
		VerseKey vk;
		sword::ListKey refs = vk.ParseVerseList((const char*)data.mid((pos == -1) ? 0 : pos+1).toUtf8(), "Gen 1:1", true);

		for (int i = 0; i < refs.Count(); ++i) {
			SWKey* key = refs.getElement(i);
			Q_ASSERT(key);
			VerseKey* vk = dynamic_cast<VerseKey*>(key);

			CTextRendering::KeyTreeItem* itm = (CTextRendering::KeyTreeItem*)0; //explicit conversion for MS VS
			if (vk && vk->isBoundSet()) { //render a range of keys
				itm = new CTextRendering::KeyTreeItem(
						QString::fromUtf8(vk->LowerBound().getText()),
						QString::fromUtf8(vk->UpperBound().getText()),
						module,
						settings
					);
			}
			else {
				itm = new CTextRendering::KeyTreeItem(
						QString::fromUtf8(key->getText()),
						QString::fromUtf8(key->getText()),
						module,
						settings
					);
			}

			Q_ASSERT(itm);

			tree.append( itm );
		}
	}
	else if (module) {
		CTextRendering::KeyTreeItem* itm = new CTextRendering::KeyTreeItem(
												data.mid((pos == -1) ? 0 : pos+1),
												module,
												settings
											);
		tree.append( itm );
	}

	//  qWarning("rendered the tree: %s", renderer.renderKeyTree(tree).latin1());
	//spanns containing rtl text need dir=rtl on their parent tag to be aligned properly
    QString lang = "en";  // default english
    if (module)
        lang = module->language()->abbrev();
	return QString("<div class=\"crossrefinfo\" lang=\"%1\"><h3>%2</h3><div class=\"para\" dir=\"%3\">%4</div></div>")
            .arg(lang)
			.arg(tr("Cross references"))
	        .arg(module ? ((module->textDirection() == CSwordModuleInfo::LeftToRight) ? "ltr" : "rtl") : "")
			.arg(renderer.renderKeyTree(tree));
}
const QString CInfoDisplay::decodeMorph( const QString& data ) {
	QStringList morphs = data.split("|");
	QString ret;

	foreach(QString morph, morphs) {
		//qDebug() << "CInfoDisplay::decodeMorph, morph: " << morph;
		CSwordModuleInfo* module = 0;
		bool skipFirstChar = false;
		QString value = "";
		QString valueClass = "";

		int valStart = morph.indexOf(':');
		if (valStart > -1) {
			valueClass = morph.mid(0, valStart);
			//qDebug() << "valueClass: " << valueClass;
			module = CPointers::backend()->findModuleByName( valueClass );
		}
		value = morph.mid(valStart+1); //works for prepended module and without (-1 +1 == 0).

		// if we don't have a class assigned or desired one isn't installed...
		if (!module) {
			// Morphs usually don't have [GH] prepended, but some old OLB
			// codes do.  We should check if we're digit after first char
			// to better guess this.
			if (value.size() > 1 && value.at(1).isDigit()) {
				switch (value.at(0).toLatin1()) {
					case 'G':
					module = CBTConfig::get
									(CBTConfig::standardGreekMorphLexicon);
					skipFirstChar = true;
					break;
					case 'H':
					module = CBTConfig::get
									(CBTConfig::standardHebrewMorphLexicon);
					skipFirstChar = true;
					break;
					default:
					skipFirstChar = false;
					//TODO: we can't tell here if it's a greek or hebrew moprh code, that's a problem we have to solve
					//       module = CBTConfig::get(CBTConfig::standardGreekMorphLexicon);
					break;
				}
			}
			//if it is still not set use the default
			if (!module) {
				module = CBTConfig::get
								(CBTConfig::standardGreekMorphLexicon);
			}
		}

		QString text;
		//Q_ASSERT(module);
		if (module) {
			boost::scoped_ptr<CSwordKey> key( CSwordKey::createInstance(module) );

			//skip H or G (language sign) if we have to skip it
			const bool isOk = key->key( skipFirstChar ? value.mid(1) : value );
			//Q_ASSERT(isOk);
			if (!isOk) { //try to use the other morph lexicon, because this one failed with the current morph code
				key->module(CBTConfig::get
								(CBTConfig::standardHebrewMorphLexicon));
				key->key( skipFirstChar ? value.mid(1) : value );
			}

			text = key->renderedText();
		}

		//if the module wasn't found just display an empty morph info
        QString lang = "en";  // default to english
        if (module)
            lang = module->language()->abbrev();
		ret.append( QString("<div class=\"morphinfo\" lang=\"%1\"><h3>%2: %3</h3><p>%4</p></div>")
                    .arg(lang)
					.arg(tr("Morphology"))
					.arg(value)
					.arg(text)
					);
	}