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.
tdewebdev/quanta/src/dtds.cpp

1104 lines
38 KiB

/***************************************************************************
dtds.cpp
-------------------
begin : 12.02.2004 (extract from quanta_init and others)
copyright : (C) 2000 by Dmitry Poplavsky & Alexander Yakovlev <pdima@users.sourceforge.net,yshurik@linuxfan.com>
(C) 2001-2003 by Andras Mantia <amantia@kde.org>
(C) 2000, 2003 by Eric Laffoon <sequitur@kde.org>
(C) 2004 by Jens Herden <jhe at epost.de>
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
//qt includes
#include <tqfile.h>
#include <qextfileinfo.h>
#include <tqdom.h>
#include <tqcursor.h>
#include <tqtimer.h>
// include files for KDE
#include <tdeapplication.h>
#include <kcombobox.h>
#include <tdeconfig.h>
#include <kdialogbase.h>
#include <kurl.h>
#include <kurlrequester.h>
#include <tdeglobal.h>
#include <tdemessagebox.h>
#include <tdelocale.h>
#include <kstandarddirs.h>
#include <tdefiledialog.h>
// application specific includes
#include "quantacommon.h"
#include "resource.h"
#include "dtdparser.h"
#include "document.h"
#include "viewmanager.h"
#include "loadentitydlgs.h"
#include "dtds.h"
/** filename for the desciption of the dtd */
const TQString m_rcFilename("description.rc");
/**
This constructor reads the dictionary of known dtd's, the attributes and tags will be loaded
on the first access to one dtd.
*/
DTDs::DTDs(TQObject *parent)
:TQObject(parent)
{
connect(this, TQT_SIGNAL(hideSplash()), parent, TQT_SLOT(slotHideSplash()));
connect(this, TQT_SIGNAL(enableIdleTimer(bool)), parent, TQT_SLOT(slotEnableIdleTimer(bool)));
connect(this, TQT_SIGNAL(loadToolbarForDTD(const TQString&)), parent, TQT_SLOT(slotLoadToolbarForDTD(const TQString&)));
// kdDebug(24000) << "dtds::dtds" << endl;
m_dict = new TQDict<DTDStruct>(119, false); //optimized for max 119 DTD. This should be enough.
m_dict->setAutoDelete(true);
m_doc = new TQDomDocument();
TQString localKDEdir = TDEGlobal::instance()->dirs()->localtdedir();
TQStringList tagsResourceDirs = TDEGlobal::instance()->dirs()->findDirs("appdata", "dtep");
TQStringList tagsDirs;
TQStringList::ConstIterator end = tagsResourceDirs.constEnd();
for ( TQStringList::ConstIterator it = tagsResourceDirs.constBegin(); it != end; ++it )
{
if ((*it).startsWith(localKDEdir))
{
TQDir dir(*it);
dir.setFilter(TQDir::Dirs);
TQStringList subDirs = dir.entryList();
TQStringList::ConstIterator subitEnd = subDirs.constEnd();
for ( TQStringList::ConstIterator subit = subDirs.constBegin(); subit != subitEnd; ++subit )
{
// kdDebug(24000) << "dtds::dtds add:" << *it + *subit+"/" << endl;
if ((*subit != ".") && (*subit != ".."))
tagsDirs += *it + *subit + "/";
}
}
}
for ( TQStringList::ConstIterator it = tagsResourceDirs.constBegin(); it != end; ++it )
{
if (!(*it).startsWith(localKDEdir))
{
TQDir dir(*it);
dir.setFilter(TQDir::Dirs);
TQStringList subDirs = dir.entryList();
TQStringList::ConstIterator subitEnd = subDirs.constEnd();
for ( TQStringList::ConstIterator subit = subDirs.constBegin(); subit != subitEnd; ++subit )
{
// kdDebug(24000) << "dtds::dtds add2:" << *it + *subit+"/" << endl;
if ((*subit != ".") && (*subit != ".."))
tagsDirs += *it + *subit + "/";
}
}
}
// kdDebug(24000) << tagsDirs.count() << " folders found." << endl;
TQTime t;
t.start();
TQStringList::ConstIterator tagsDirsEnd = tagsDirs.constEnd();
for ( TQStringList::ConstIterator it = tagsDirs.constBegin(); it != tagsDirsEnd; ++it )
{
// kdDebug(24000) << "read:" << *it << endl;
readTagDir(*it, false); // read all tags, but only short form
}
kdDebug(24000) << "DTD reading: " << t.elapsed() << endl;
//load the mimetypes from the insideDTDs
TQDictIterator<DTDStruct> it(*m_dict);
for( ; it.current(); ++it )
{
DTDStruct * dtd = it.current();
for (uint i = 0; i < dtd->insideDTDs.count(); i++)
{
const DTDStruct *insideDTD = m_dict->find(dtd->insideDTDs[i]); // search but don't load
if (!insideDTD)
insideDTD = m_dict->find(getDTDNameFromNickName(dtd->insideDTDs[i])); // search but don't load
if (insideDTD && !insideDTD->toplevel)
dtd->mimeTypes += insideDTD->mimeTypes;
}
}
// kdDebug(24000) << "dtds::dtds constructed" << endl;
}
DTDs::~DTDs()
{
TQDictIterator<DTDStruct> it(*m_dict);
for( ; it.current(); ++it )
{
removeDTD (it.current());
}
delete m_dict;
delete m_doc;
}
void DTDs::removeDTD(DTDStruct *dtd)
{
if (dtd)
{
delete dtd->tagsList;
dtd->tagsList = 0L;
delete dtd->commonAttrs;
dtd->commonAttrs = 0L;
m_dict->remove(dtd->name.lower());
}
}
/** Reads the tag files and the description.rc from tagDir in order to
build up the internal DTD and tag structures. */
bool DTDs::readTagDir(const TQString &dirName, bool loadAll)
{
// kdDebug(24000) << "dtds::readTagDir:" << dirName << " all:" << loadAll << endl;
TQString tmpStr = dirName + m_rcFilename;
if (!TQFile::exists(tmpStr))
return false;
TDEConfig *dtdConfig = new TDEConfig(tmpStr, true);
dtdConfig->setGroup("General");
TQString dtdName = dtdConfig->readEntry("Name", "Unknown");
if (m_dict->find(dtdName.lower()))
{
delete dtdConfig;
kdDebug(24000) << "dtds::readTagDir from " << dirName
<< " canceled, DTD " << dtdName << " found in memory" << endl;
return false;
}
//read the general DTD info
DTDStruct *dtd = new DTDStruct;
dtd->fileName = tmpStr;
dtd->name = dtdName;
dtd->nickName = dtdConfig->readEntry("NickName", dtdName);
dtd->mimeTypes = dtdConfig->readListEntry("MimeTypes");
for (uint i = 0; i < dtd->mimeTypes.count(); i++)
dtd->mimeTypes[i] = dtd->mimeTypes[i].stripWhiteSpace();
dtd->family = dtdConfig->readNumEntry("Family", Xml);
if (dtd->family != Xml)
dtd->toplevel = dtdConfig->readBoolEntry("TopLevel", false);
else
dtd->toplevel = true;
dtd->tagsList = 0L;
dtd->commonAttrs = 0L;
//Read the areas that define the areas
dtdConfig->setGroup("Parsing rules");
TQStringList definitionAreaBorders = dtdConfig->readListEntry("AreaBorders");
for (uint i = 0; i < definitionAreaBorders.count(); i++)
{
TQStringList tmpStrList = TQStringList::split(" ", definitionAreaBorders[i].stripWhiteSpace());
dtd->definitionAreas[tmpStrList[0].stripWhiteSpace()] = tmpStrList[1].stripWhiteSpace();
}
//Read the tags that define this DTD
TQStringList tmpStrList = dtdConfig->readListEntry("Tags");
for (uint i = 0; i < tmpStrList.count(); i++)
{
tmpStr = tmpStrList[i].stripWhiteSpace();
int pos = tmpStr.find('(');
dtd->definitionTags[tmpStr.left(pos).stripWhiteSpace()] = tmpStr.mid(pos+1, tmpStr.findRev(')')-pos-1).stripWhiteSpace();
}
//Which DTD can be present in this one?
dtd->insideDTDs = dtdConfig->readListEntry("MayContain");
for (uint i = 0; i < dtd->insideDTDs.count(); i++)
{
dtd->insideDTDs[i] = dtd->insideDTDs[i].stripWhiteSpace().lower();
}
m_dict->insert(dtdName.lower(), dtd); //insert the structure into the dictionary
delete dtdConfig;
if (!loadAll)
{
dtd->loaded = false;
return true;
}
dtd->loaded = readTagDir2(dtd);
return dtd->loaded;
}
/** Reads the tag files and the description.rc from dtd in order to
build up the internal DTD and tag structures.
*/
bool DTDs::readTagDir2(DTDStruct *dtd)
{
// kdDebug(24000) << "dtds::readTagDir2:" << dtd->name << " at " << dtd->fileName << endl;
if (!TQFile::exists(dtd->fileName)) return false;
kapp->setOverrideCursor( TQCursor(TQt::WaitCursor) );
TDEConfig *dtdConfig = new TDEConfig(dtd->fileName, true);
//read the general DTD info
dtdConfig->setGroup("General");
dtd->commonAttrs = new AttributeListDict();
dtd->commonAttrs->setAutoDelete(true);
bool caseSensitive = dtdConfig->readBoolEntry("CaseSensitive");
dtd->url = dtdConfig->readEntry("URL");
dtd->doctypeStr = dtdConfig->readEntry("DoctypeString");
if (dtd->doctypeStr.isEmpty())
{
dtd->doctypeStr = "PUBLIC \"" + dtd->name + "\"";
if (!dtd->url.isEmpty())
dtd->doctypeStr += " \"" + dtd->url + "\"";
}
dtd->doctypeStr.prepend(' ');
dtd->inheritsTagsFrom = dtdConfig->readEntry("Inherits").lower();
dtd->documentation = dtdConfig->readEntry("Documentation").lower();
dtd->defaultExtension = dtdConfig->readEntry("DefaultExtension");
dtd->caseSensitive = caseSensitive;
int numOfTags = 0;
TQTagList *tagList = new TQTagList(119, false); //max 119 tag in a DTD
tagList->setAutoDelete(true);
//read all the tag files
KURL dirURL(dtd->fileName);
dirURL.setFileName("");
TQString dirName = dirURL.path(1);
if (TQFile::exists(dirName + "common.tag"))
readTagFile(dirName + "common.tag", dtd, 0L);
//bool idleTimerStatus = quantaApp->slotEnableIdleTimer(false);
emit enableIdleTimer(false);
KURL::List files = QExtFileInfo::allFilesRelative(dirURL, "*.tag", 0L);
//quantaApp->slotEnableIdleTimer(idleTimerStatus);
emit enableIdleTimer(true);
TQString tmpStr;
KURL::List::ConstIterator end_f = files.constEnd();
for ( KURL::List::ConstIterator it_f = files.constBegin(); it_f != end_f; ++it_f )
{
tmpStr = (*it_f).path(-1);
if (!tmpStr.isEmpty())
{
tmpStr.prepend(dirName);
if (!tmpStr.endsWith("/common.tag"))
numOfTags += readTagFile(tmpStr, dtd, tagList);
}
}
//read the toolbars
dtdConfig->setGroup("Toolbars");
tmpStr = QuantaCommon::readPathEntry(dtdConfig, "Location"); //holds the location of the toolbars
if (!tmpStr.endsWith("/") && !tmpStr.isEmpty())
{
tmpStr.append("/");
}
dtd->toolbars = dtdConfig->readListEntry("Names");
for (uint i = 0; i < dtd->toolbars.count(); i++)
{
dtd->toolbars[i] = tmpStr + dtd->toolbars[i].stripWhiteSpace() + toolbarExtension;
}
//read the extra tags and their attributes
dtdConfig->setGroup("Extra tags");
dtd->defaultAttrType = dtdConfig->readEntry("DefaultAttrType","input");
TQStrList extraTagsList;
dtdConfig->readListEntry("List",extraTagsList);
TQString option;
TQStrList optionsList;
TQStrList attrList;
for (uint i = 0 ; i < extraTagsList.count(); i++)
{
TQTag *tag = new TQTag();
tag->setName(TQString(extraTagsList.at(i)).stripWhiteSpace());
tmpStr = (dtd->caseSensitive) ? tag->name() : tag->name().upper();
if (tagList->find(tmpStr)) //the tag is already defined in a .tag file
{
delete tag;
continue; //skip this tag
}
tag->parentDTD = dtd;
//read the possible stopping tags
TQStrList stoppingTags;
dtdConfig->readListEntry(tag->name() + "_stoppingtags",stoppingTags);
for (uint j = 0; j < stoppingTags.count(); j++)
{
TQString stopTag = TQString(stoppingTags.at(j)).stripWhiteSpace();
if (!dtd->caseSensitive) stopTag = stopTag.upper();
tag->stoppingTags.append(stopTag);
}
//read the possible tag options
optionsList.clear();
dtdConfig->readListEntry(tag->name() + "_options",optionsList);
for (uint j = 0; j < optionsList.count(); j++)
{
option = TQString(optionsList.at(j)).stripWhiteSpace();
TQDictIterator<AttributeList> it(*(dtd->commonAttrs));
for( ; it.current(); ++it )
{
tmpStr = "has" + TQString(it.currentKey()).stripWhiteSpace();
if (option == tmpStr)
{
tag->commonGroups += TQString(it.currentKey()).stripWhiteSpace();
}
}
if (option == "single")
{
tag->setSingle(true);
}
if (option == "optional")
{
tag->setOptional(true);
}
}
attrList.clear();
dtdConfig->readListEntry(tag->name(), attrList);
for (uint j = 0; j < attrList.count(); j++)
{
Attribute* attr = new Attribute;
attr->name = TQString(attrList.at(j)).stripWhiteSpace();
attr->type = dtd->defaultAttrType;
tag->addAttribute(attr);
delete attr;
}
if (caseSensitive)
{
tagList->insert(tag->name(),tag); //append the tag to the list for this DTD
} else
{
tagList->insert(tag->name().upper(),tag);
}
}
dtd->tagsList = tagList;
dtd->tagsList->setAutoDelete(true);
/**** Code for the new parser *****/
dtdConfig->setGroup("Parsing rules");
bool appendCommonRules = dtdConfig->readBoolEntry("AppendCommonSpecialAreas", true);
//Read the special areas and area names
TQString rxStr = "";
if (dtd->family == Xml && appendCommonRules)
{
dtd->specialAreas["<?xml"] = "?>";
dtd->specialAreaNames["<?xml"] = "XML PI";
dtd->specialAreas["<!--"] = "-->";
dtd->specialAreaNames["<!--"] = "comment";
// dtd->specialAreas["<!"] = ">";
// dtd->specialAreaNames["<!"] = "DTD";
dtd->insideDTDs.append("dtd");
tmpStr = "(<?xml)|(<!--)|(<!)|";
rxStr = QuantaCommon::makeRxCompatible(tmpStr);
}
TQStringList specialAreasList = dtdConfig->readListEntry("SpecialAreas");
TQStringList specialAreaNameList = dtdConfig->readListEntry("SpecialAreaNames");
TQStringList tmpStrList;
for (uint i = 0; i < specialAreasList.count(); i++)
{
if (!specialAreasList[i].stripWhiteSpace().isEmpty())
{
tmpStrList = TQStringList::split(" ",specialAreasList[i].stripWhiteSpace());
tmpStr = tmpStrList[0].stripWhiteSpace();
rxStr.append(QuantaCommon::makeRxCompatible(tmpStr)+"|");
dtd->specialAreas[tmpStr] = tmpStrList[1].stripWhiteSpace();
dtd->specialAreaNames[tmpStr] = specialAreaNameList[i];
}
}
if (rxStr.isEmpty())
{
dtd->specialAreaStartRx.setPattern("");
} else
{
dtd->specialAreaStartRx.setPattern(rxStr.left(rxStr.length() - 1));
}
//Read the special tags
tmpStrList = dtdConfig->readListEntry("SpecialTags");
for (uint i = 0; i < tmpStrList.count(); i++)
{
tmpStr = tmpStrList[i].stripWhiteSpace();
int pos = tmpStr.find('(');
dtd->specialTags[tmpStr.left(pos).stripWhiteSpace()] = tmpStr.mid(pos+1, tmpStr.findRev(')')-pos-1).stripWhiteSpace();
}
//static const TQString quotationStr = "\\\\\"|\\\\'";
rxStr = "\\\\\"|\\\\'|";
TQStringList commentsList = dtdConfig->readListEntry("Comments");
if (dtd->family == Xml && appendCommonRules)
commentsList.append("<!-- -->");
TQString tmpStr2;
for (uint i = 0; i < commentsList.count(); i++)
{
tmpStrList = TQStringList::split(" ",commentsList[i].stripWhiteSpace());
tmpStr = tmpStrList[0].stripWhiteSpace();
rxStr += QuantaCommon::makeRxCompatible(tmpStr);
rxStr += "|";
tmpStr2 = tmpStrList[1].stripWhiteSpace();
if (tmpStr2 == "EOL")
tmpStr2 = '\n';
dtd->comments[tmpStr] = tmpStr2;
}
dtd->commentsStartRx.setPattern(rxStr.left(rxStr.length()-1));
/**** End of code for the new parser *****/
//read the definition of a structure, and the structure keywords
TQStringList structKeywords = dtdConfig->readListEntry("StructKeywords",',');
if (structKeywords.count() !=0 )
{
tmpStr = "\\b(";
for (uint i = 0; i < structKeywords.count(); i++)
{
tmpStr += structKeywords[i].stripWhiteSpace()+"|";
}
tmpStr.truncate(tmpStr.length()-1);
tmpStr += ")\\b";
} else
{
tmpStr = "\\b[\\d\\S\\w]+\\b";
}
dtd->structKeywordsRx.setPattern(tmpStr);
structKeywords = dtdConfig->readListEntry("LocalScopeKeywords",',');
if (structKeywords.count() !=0 )
{
tmpStr = "\\b(";
for (uint i = 0; i < structKeywords.count(); i++)
{
tmpStr += structKeywords[i].stripWhiteSpace()+"|";
}
tmpStr.truncate(tmpStr.length()-1);
tmpStr += ")\\b";
} else
{
tmpStr = "\\b[\\d\\S\\w]+\\b";
}
dtd->localScopeKeywordsRx.setPattern(tmpStr);
dtd->structRx.setPattern(dtdConfig->readEntry("StructRx","\\{|\\}").stripWhiteSpace());
dtd->structBeginStr = dtdConfig->readEntry("StructBeginStr","{").stripWhiteSpace();
dtd->structEndStr = dtdConfig->readEntry("StructEndStr","}").stripWhiteSpace();
dtdConfig->setGroup("Extra rules");
dtd->minusAllowedInWord = dtdConfig->readBoolEntry("MinusAllowedInWord", false);
tmpStr = dtdConfig->readEntry("TagAutoCompleteAfter", "<").stripWhiteSpace();
if (tmpStr.upper() == "NONE")
dtd->tagAutoCompleteAfter = '\0';
else
if (tmpStr.upper() == "ALWAYS")
dtd->tagAutoCompleteAfter = '\1';
else
dtd->tagAutoCompleteAfter = tmpStr.at(0);
dtd->requestSpaceBeforeTagAutoCompletion = dtdConfig->readBoolEntry("RequestSpaceBeforeTagAutoCompletion", false);
dtd->attrAutoCompleteAfter = dtdConfig->readEntry("AttributeAutoCompleteAfter","(").stripWhiteSpace().at(0);
dtd->attributeSeparator = dtdConfig->readEntry("AttributeSeparator").stripWhiteSpace().at(0);
if (dtd->attributeSeparator.isNull())
{
dtd->attributeSeparator = (dtd->family == Xml) ? '\"' : ',';
}
dtd->tagSeparator = dtdConfig->readEntry("TagSeparator").stripWhiteSpace().at(0);
if (dtd->tagSeparator.isNull())
dtd->tagSeparator = dtd->attributeSeparator;
dtd->booleanAttributes = dtdConfig->readEntry("BooleanAttributes","extended");
dtd->booleanTrue = dtdConfig->readEntry("BooleanTrue","true");
dtd->booleanFalse = dtdConfig->readEntry("BooleanFalse","false");
dtd->singleTagStyle = dtdConfig->readEntry("Single Tag Style", "xml").lower();
dtd->variableGroupIndex = dtdConfig->readNumEntry("VariableGroupIndex", 0) - 1;
dtd->functionGroupIndex = dtdConfig->readNumEntry("FunctionGroupIndex", 0) - 1;
dtd->classGroupIndex = dtdConfig->readNumEntry("ClassGroupIndex", 0) - 1;
if (dtd->classGroupIndex != -1)
{
tmpStr = dtdConfig->readEntry("MemberAutoCompleteAfter").stripWhiteSpace();
dtd->memberAutoCompleteAfter.setPattern(tmpStr);
}
dtd->objectGroupIndex = dtdConfig->readNumEntry("ObjectGroupIndex", 0) - 1;
//read the definition of different structure groups, like links, images, functions
//classes, etc.
uint structGroupsCount = dtdConfig->readNumEntry("StructGroupsCount", 0);
if (structGroupsCount > MAX_STRUCTGROUPSCOUNT)
structGroupsCount = MAX_STRUCTGROUPSCOUNT; //max. 10 groups
if (dtd->family == Script)
{
StructTreeGroup group;
TQRegExp attrRx("\\([^\\)]*\\)");
TQString tagStr;
for (uint index = 1; index <= structGroupsCount; index++)
{
dtdConfig->setGroup(TQString("StructGroup_%1").arg(index));
//new code
group.name = dtdConfig->readEntry("Name").stripWhiteSpace();
group.noName = dtdConfig->readEntry("No_Name").stripWhiteSpace();
group.icon = dtdConfig->readEntry("Icon").stripWhiteSpace();
tmpStr = dtdConfig->readEntry("DefinitionRx").stripWhiteSpace();
group.definitionRx.setPattern(tmpStr);
tmpStr = dtdConfig->readEntry("UsageRx").stripWhiteSpace();
group.usageRx.setPattern(tmpStr);
tmpStr = dtdConfig->readEntry("TypeRx").stripWhiteSpace();
group.typeRx.setPattern(tmpStr);
group.hasDefinitionRx = !group.definitionRx.pattern().isEmpty();
group.isMinimalDefinitionRx = dtdConfig->readBoolEntry("DefinitionRx_Minimal", false);
group.appendToTags = dtdConfig->readBoolEntry("AppendToTags", false);
group.parentGroup = dtdConfig->readEntry("ParentGroup").stripWhiteSpace();
tagStr = dtdConfig->readEntry("TagType", "Text").stripWhiteSpace();
if (tagStr == "XmlTag")
group.tagType = Tag::XmlTag;
else if (tagStr == "XmlTagEnd")
group.tagType = Tag::XmlTagEnd;
else if (tagStr == "Text")
group.tagType = Tag::Text;
else if (tagStr == "Comment")
group.tagType = Tag::Comment;
else if (tagStr == "CSS")
group.tagType = Tag::CSS;
else if (tagStr == "ScriptTag")
group.tagType = Tag::ScriptTag;
else if (tagStr == "ScriptStructureBegin")
group.tagType = Tag::ScriptStructureBegin;
else if (tagStr == "ScriptStructureEnd")
group.tagType = Tag::ScriptStructureEnd;
else group.tagType = -1;
tmpStr = dtdConfig->readEntry("AutoCompleteAfter").stripWhiteSpace();
group.autoCompleteAfterRx.setPattern(tmpStr);
tmpStr = dtdConfig->readEntry("RemoveFromAutoCompleteWord").stripWhiteSpace();
group.removeFromAutoCompleteWordRx.setPattern(tmpStr);
group.hasFileName = dtdConfig->readBoolEntry("HasFileName", false);
group.parseFile = dtdConfig->readBoolEntry("ParseFile", false);
tmpStr = dtdConfig->readEntry("FileNameRx").stripWhiteSpace();
group.fileNameRx.setPattern(tmpStr);
dtd->structTreeGroups.append(group);
}
} else
{
XMLStructGroup group;
TQRegExp attrRx("\\([^\\)]*\\)");
TQString tagName;
for (uint index = 1; index <= structGroupsCount; index++)
{
dtdConfig->setGroup(TQString("StructGroup_%1").arg(index));
group.name = dtdConfig->readEntry("Name").stripWhiteSpace();
group.noName = dtdConfig->readEntry("No_Name").stripWhiteSpace();
group.icon = dtdConfig->readEntry("Icon").stripWhiteSpace();
group.appendToTags = dtdConfig->readBoolEntry("AppendToTags", false);
group.parentGroup = dtdConfig->readEntry("ParentGroup").stripWhiteSpace();
TQString tagStr = dtdConfig->readEntry("Tag").stripWhiteSpace();
if (!tagStr.isEmpty())
{
attrRx.search(tagStr);
tmpStr = attrRx.cap();
tmpStrList = TQStringList::split(',', tmpStr.mid(1, tmpStr.length()-2));
tagName = tagStr.left(tagStr.find('(')).lower();
group.attributes.clear();
for (uint i = 0; i < tmpStrList.count(); i++)
group.attributes += tmpStrList[i].stripWhiteSpace();
group.hasFileName = dtdConfig->readBoolEntry("HasFileName", false);
tmpStr = dtdConfig->readEntry("FileNameRx").stripWhiteSpace();
group.fileNameRx.setPattern(tmpStr);
dtd->xmlStructTreeGroups.insert(tagName, group);
}
}
}
delete dtdConfig;
dtd->loaded = true;
resolveInherited(dtd);
kapp->restoreOverrideCursor();
return true;
}
void DTDs::resolveInherited (DTDStruct *dtd)
{
//Resolve the inheritence
if (!dtd->inheritsTagsFrom.isEmpty())
{
DTDStruct *parent = (DTDStruct *) find(dtd->inheritsTagsFrom); // this loads the dtd, if not present in memory
TQDictIterator<TQTag> tag_it(*(parent->tagsList));
for ( ; tag_it.current(); ++tag_it)
{
TQTag *tag = tag_it.current();
TQString searchForTag = (dtd->caseSensitive) ? tag->name() : tag->name().upper();
if (!dtd->tagsList->find(searchForTag))
{
TQTag *newTag = new TQTag(*tag);
dtd->tagsList->insert(searchForTag, newTag);
}
}
}
//Read the pseudo DTD area definition strings (special area/tag string)
//from the DTD's which may be present in the DTD (May_Contain setting)
TQMap<TQString, TQString>::ConstIterator mapIt;
TQString specialAreaStartRxStr = dtd->specialAreaStartRx.pattern();
if (!specialAreaStartRxStr.isEmpty())
specialAreaStartRxStr += "|";
for (uint i = 0; i < dtd->insideDTDs.count(); i++)
{
const DTDStruct *insideDTD = m_dict->find(dtd->insideDTDs[i]); // search but don't load
if (!insideDTD)
insideDTD = m_dict->find(getDTDNameFromNickName(dtd->insideDTDs[i])); // search but don't load
if (insideDTD)
{
for (mapIt = insideDTD->definitionAreas.begin(); mapIt != insideDTD->definitionAreas.end(); ++mapIt)
{
TQString tmpStr = mapIt.key();
dtd->specialAreas[tmpStr] = mapIt.data();
dtd->specialAreaNames[tmpStr] = dtd->insideDTDs[i];
specialAreaStartRxStr.append("(?:" + QuantaCommon::makeRxCompatible(tmpStr) + ")|");
}
for (mapIt = insideDTD->definitionTags.begin(); mapIt != insideDTD->definitionTags.end(); ++mapIt)
{
dtd->specialTags[mapIt.key()] = mapIt.data();
}
}
dtd->specialAreaStartRx.setPattern(specialAreaStartRxStr.left(specialAreaStartRxStr.length() - 1));
};
}
/** Reads the tags for the tag files. Returns the number of read tags. */
uint DTDs::readTagFile(const TQString& fileName, DTDStruct* parentDTD, TQTagList *tagList)
{
// kdDebug(24000) << "dtds::readTagFile:" << fileName << endl;
TQFile f(fileName);
if (! f.exists())
kdError() << "dtds::readTagFile file does not exist:" << fileName << endl;
else
{
bool result = f.open( IO_ReadOnly );
if (! result)
kdError() << "dtds::readTagFile unable to open:" << fileName
<< " Status: " << f.status() << endl;
}
TQString errorMsg;
int errorLine, errorCol;
if (!m_doc->setContent( &f, &errorMsg, &errorLine, &errorCol ))
{
emit hideSplash();
KMessageBox::error(0L, i18n("<qt>The DTD tag file %1 is not valid.<br> The error message is: <i>%2 in line %3, column %4.</i></qt>").arg(fileName).arg(errorMsg).arg(errorLine).arg(errorCol),
i18n("Invalid Tag File"));
kdWarning() << fileName << ": " << errorMsg << ": " << errorLine << "," << errorCol << endl;
}
f.close();
TQDomNodeList nodeList = m_doc->elementsByTagName("tag");
uint numOfTags = nodeList.count();
for (uint i = 0; i < numOfTags; i++)
{
TQDomNode n = nodeList.item(i);
TQDomElement e = n.toElement();
if (e.attribute("type") == "class")
{
TQString extends = e.attribute("extends");
TQString name = e.attribute("name");
if (!name.isEmpty() && !extends.isEmpty())
parentDTD->classInheritance[name] = extends;
continue;
}
TQTag *tag = new TQTag();
tag->setName(e.attribute("name"));
tag->setFileName(fileName);
tag->parentDTD = parentDTD;
bool common = false;
setAttributes(&n, tag, common);
if (common)
{
TQString groupName = e.attribute("name");
AttributeList *attrs = tag->attributes();
attrs->setAutoDelete(false);
AttributeList *commonAttrList = new AttributeList; //no need to delete it
commonAttrList->setAutoDelete(true);
*commonAttrList = *attrs;
//delete tag;
parentDTD->commonAttrs->insert(groupName, commonAttrList);
} else
{
if (parentDTD->caseSensitive)
{
tagList->replace(tag->name(), tag); //append the tag to the list for this DTD
} else
{
tagList->replace(tag->name().upper(), tag);
}
}
}
return numOfTags;
}
/**
Parse the dom document and retrieve the tag attributes
*/
void DTDs::setAttributes(TQDomNode *dom, TQTag* tag, bool &common)
{
common = false;
Attribute *attr;
TQDomElement el = dom->toElement();
TQString tmpStr;
tmpStr = el.attribute("common");
if ((tmpStr != "1" && tmpStr != "yes")) //in case of common tags, we are not interested in these options
{
if (tag->parentDTD->commonAttrs)
{
TQDictIterator<AttributeList> it(*(tag->parentDTD->commonAttrs));
for( ; it.current(); ++it )
{
TQString lookForAttr = "has" + TQString(it.currentKey()).stripWhiteSpace();
tmpStr = el.attribute(lookForAttr);
if (tmpStr == "1" || tmpStr == "yes")
{
tag->commonGroups += TQString(it.currentKey()).stripWhiteSpace();
}
}
}
tmpStr = el.attribute("single");
if (tmpStr == "1" || tmpStr == "yes")
{
tag->setSingle(true);
}
tmpStr = el.attribute("optional");
if (tmpStr == "1" || tmpStr == "yes")
{
tag->setOptional(true);
}
tmpStr = el.attribute("scope");
tag->setScope(tmpStr);
tag->type = el.attribute("type", "xmltag");
tag->returnType = el.attribute("returnType", "");
tag->className = el.attribute("class", "");
tag->comment = el.attribute("comment", "");
if (!tag->comment.isEmpty())
tag->comment = " [" + i18n(tag->comment.ascii()) + "] ";
tag->comment.prepend(el.attribute("version"));
} else
{
common = true;
}
TQString attrList;
for ( TQDomNode n = dom->firstChild(); !n.isNull(); n = n.nextSibling() )
{
tmpStr = n.nodeName();
if (tmpStr == "children")
{
TQDomElement el = n.toElement();
TQDomElement item = el.firstChild().toElement();
while ( !item.isNull() )
{
tmpStr = item.tagName();
if (tmpStr == "child")
{
TQString childTag = item.attribute("name");
if (!tag->parentDTD->caseSensitive)
childTag = childTag.upper();
tag->childTags.insert(childTag, item.attribute("usage") == "required");
}
item = item.nextSibling().toElement();
}
} else
if (tmpStr == "stoppingtags") //read what tag can act as closing tag
{
TQDomElement el = n.toElement();
TQDomElement item = el.firstChild().toElement();
while ( !item.isNull() )
{
if (item.tagName() == "stoppingtag")
{
TQString stopTag = item.attribute("name");
if (!tag->parentDTD->caseSensitive)
stopTag = stopTag.upper();
tag->stoppingTags.append(stopTag);
}
item = item.nextSibling().toElement();
}
} else
if (tmpStr == "attr") //an attribute
{
TQDomElement el = n.toElement();
attr = new Attribute;
attr->name = el.attribute("name");
attr->source = el.attribute("source");
attr->interface = el.attribute("interface");
attr->method = el.attribute("method");
attr->arguments = el.attribute("arguments");
attr->type = el.attribute("type",tag->parentDTD->defaultAttrType);
attr->defaultValue = el.attribute("defaultValue");
attr->status = el.attribute("status");
if ( attr->type == "list" ) {
for ( TQDomElement attrEl = el.firstChild().toElement(); !attrEl.isNull(); attrEl = attrEl.nextSibling().toElement() ) {
if ( attrEl.tagName() == "items" ) {
TQDomElement item = attrEl.firstChild().toElement();
while ( !item.isNull() ) {
attr->values.append( item.text() );
item = item.nextSibling().toElement();
}
}
}
} else if ( attr->type == "check" ) {
attr->values.append("true");
attr->values.append("false");
} else if ( attr->type == "color" ) {
attr->values.append("Black");
attr->values.append("Silver");
attr->values.append("Gray");
attr->values.append("White");
attr->values.append("Maroon");
attr->values.append("Red");
attr->values.append("Purple");
attr->values.append("Fuchsia");
attr->values.append("Green");
attr->values.append("Lime");
attr->values.append("Olive");
attr->values.append("Yellow");
attr->values.append("Navy");
attr->values.append("Blue");
attr->values.append("Teal");
attr->values.append("Aqua");
} else if ( attr->type == "url" ) {
//not treated yet
} else if ( attr->type == "input" ) {
//not treated yet
}
if (tag->type == "function" || tag->type == "method")
{
if (attr->status == "optional")
{
attrList = attrList + "["+attr->type +" "+attr->name +"], ";
} else
{
attrList = attrList + attr->type +" "+attr->name +", ";
}
}
if (!attr->name.isEmpty())
{
tag->addAttribute(attr);
}
delete attr;
}
}
if (!attrList.isEmpty())
tag->comment.prepend(attrList.left(attrList.length() - 2) + "; ");
}
void DTDs::slotLoadDTD()
{
KURL url = KFileDialog::getOpenURL("", i18n("*.dtd|DTD Definitions"), 0L);
if (!url.isEmpty())
{
DTDParser dtdParser(url, TDEGlobal::dirs()->saveLocation("data") + resourceDir + "dtep");
if (dtdParser.parse())
{
TQString dirName = dtdParser.dirName();
TDEConfig dtdcfg(dirName + m_rcFilename, true);
dtdcfg.setGroup("General");
TQString dtdName = dtdcfg.readEntry("Name");
TQString nickName = dtdcfg.readEntry("NickName", dtdName);
DTDStruct * dtd = m_dict->find(dtdName) ;
if (dtd &&
KMessageBox::warningYesNo(0L, i18n("<qt>Do you want to replace the existing <b>%1</b> DTD?</qt>").arg(nickName), TQString(), i18n("Replace"), i18n("Do Not Replace")) == KMessageBox::No)
{
return;
}
removeDTD(dtd);
if (readTagDir(dirName))
{
TQString family = dtdcfg.readEntry("Family", "1");
Document *w = ViewManager::ref()->activeDocument();
if (family == "1" && w &&
KMessageBox::questionYesNo(0L, i18n("<qt>Use the newly loaded <b>%1</b> DTD for the current document?</qt>").arg(nickName), i18n("Change DTD"), i18n("Use"), i18n("Do Not Use")) == KMessageBox::Yes)
{
w->setDTDIdentifier(dtdName);
emit loadToolbarForDTD(w->getDTDIdentifier());
emit forceReparse();
}
}
}
}
}
void DTDs::slotLoadDTEP(const TQString &_dirName, bool askForAutoload)
{
TQString dirName = _dirName;
if (!dirName.endsWith("/"))
dirName += "/";
TDEConfig dtdcfg(dirName + m_rcFilename, true);
dtdcfg.setGroup("General");
TQString dtdName = dtdcfg.readEntry("Name");
TQString nickName = dtdcfg.readEntry("NickName", dtdName);
DTDStruct * dtd = m_dict->find(dtdName) ;
if ( dtd &&
KMessageBox::warningYesNo(0L, i18n("<qt>Do you want to replace the existing <b>%1</b> DTD?</qt>").arg(nickName), TQString(), i18n("Replace"), i18n("Do Not Replace")) == KMessageBox::No)
{
return;
}
removeDTD(dtd);
if (!readTagDir(dirName))
{
KMessageBox::error(0L, i18n("<qt>Cannot read the DTEP from <b>%1</b>. Check that the folder contains a valid DTEP (<i>description.rc and *.tag files</i>).</qt>").arg(dirName), i18n("Error Loading DTEP"));
} else
{
TQString family = dtdcfg.readEntry("Family", "1");
if (askForAutoload && KMessageBox::questionYesNo(0L, i18n("<qt>Autoload the <b>%1</b> DTD in the future?</qt>").arg(nickName), TQString(), i18n("Load"), i18n("Do Not Load")) == KMessageBox::Yes)
{
KURL src;
src.setPath(dirName);
KURL target;
TQString destDir = TDEGlobal::dirs()->saveLocation("data") + resourceDir + "dtep/";
target.setPath(destDir + src.fileName());
TDEIO::copy( src, target, false); //don't care about the result
}
Document *w = ViewManager::ref()->activeDocument();
if (family == "1" && w &&
KMessageBox::questionYesNo(0L, i18n("<qt>Use the newly loaded <b>%1</b> DTD for the current document?</qt>").arg(nickName), i18n("Change DTD"), i18n("Use"), i18n("Do Not Use")) == KMessageBox::Yes)
{
w->setDTDIdentifier(dtdName);
emit loadToolbarForDTD(w->getDTDIdentifier());
emit forceReparse();
}
}
}
void DTDs::slotLoadEntities()
{
KDialogBase dlg(0L, "loadentities", true, i18n("Load DTD Entities Into DTEP"), KDialogBase::Ok | KDialogBase::Cancel);
LoadEntityDlgS entitiesWidget(&dlg);
TQStringList lst(DTDs::ref()->nickNameList(true));
entitiesWidget.targetDTEPCombo->insertStringList(lst);
Document *w = ViewManager::ref()->activeDocument();
if (w)
{
TQString nickName = DTDs::ref()->getDTDNickNameFromName(w->getDTDIdentifier());
entitiesWidget.targetDTEPCombo->setCurrentItem(lst.findIndex(nickName));
}
dlg.setMainWidget(&entitiesWidget);
if (dlg.exec())
{
DTDStruct * dtd = m_dict->find(getDTDNameFromNickName(entitiesWidget.targetDTEPCombo->currentText()));
DTDParser dtdParser(KURL::fromPathOrURL(entitiesWidget.sourceDTDRequester->url()), TDEGlobal::dirs()->saveLocation("data") + resourceDir + "dtep");
TQString dtdDir = TQFileInfo(dtd->fileName).dirPath();
if (dtdParser.parse(dtdDir, true))
{
readTagFile(dtdDir + "/entities.tag", dtd, dtd->tagsList);
}
}
}
/** Returns the DTD name (identifier) corresponding to the DTD's nickname */
TQString DTDs::getDTDNameFromNickName(const TQString& nickName)
{
TQDictIterator<DTDStruct> it(*m_dict);
for( ; it.current(); ++it )
{
if (it.current()->nickName.lower() == nickName.lower())
{
return it.current()->name;
}
}
return nickName;
}
/** returns the known nick names */
TQStringList DTDs::nickNameList(bool topLevelOnly)
{
TQStringList nickList;
TQDictIterator<DTDStruct> it(*m_dict);
for( ; it.current(); ++it )
{
if (!topLevelOnly || it.current()->toplevel)
{
nickList << it.current()->nickName;
}
}
nickList.sort();
return nickList;
}
/** returns the known names */
TQStringList DTDs::nameList(bool topLevelOnly)
{
TQStringList nameList;
TQDictIterator<DTDStruct> it(*m_dict);
for( ; it.current(); ++it )
{
if (!topLevelOnly || it.current()->toplevel)
{
nameList << it.current()->name;
}
}
nameList.sort();
return nameList;
}
TQStringList DTDs::fileNameList(bool topLevelOnly)
{
TQStringList nameList;
TQDictIterator<DTDStruct> it(*m_dict);
for( ; it.current(); ++it )
{
if (!topLevelOnly || it.current()->toplevel)
{
nameList << (it.current()->name + "|" + it.current()->fileName);
}
}
return nameList;
}
const DTDStruct * DTDs::DTDforURL(const KURL &url)
{
TQValueList<DTDStruct*> foundList;
TQDictIterator<DTDStruct> it(*m_dict);
for( ; it.current(); ++it )
{
if (it.current()->toplevel && canHandle(it.current(), url))
{
foundList.append(it.current());
}
}
if (foundList.isEmpty())
return find("empty");
else
{
TQString path = url.path();
for (uint i = 0; i < foundList.count(); i++)
{
if (path.endsWith('.' + foundList[i]->defaultExtension))
return foundList[i];
}
return foundList[0];
}
}
bool DTDs::canHandle(const DTDStruct *dtd, const KURL &url)
{
TQString mimetype = KMimeType::findByURL(url)->name();
if (dtd->mimeTypes.contains(mimetype))
return true;
if (url.path().endsWith('.' + dtd->defaultExtension))
return true;
return false;
}
#include "dtds.moc"