00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include "latexhelpers.h"
00024
00025 #include <QDebug>
00026 #include <QFile>
00027 #include <QMessageBox>
00028
00029 #include "prefsmanager.h"
00030 #include "scpaths.h"
00031
00032
00033 LatexHighlighter::LatexHighlighter(QTextDocument *document)
00034 : QSyntaxHighlighter(document)
00035 {
00036 QTextCharFormat mathFormat;
00037 mathFormat.setForeground(QColor(0, 128, 0));
00038 mathFormat.setFontWeight(QFont::Bold);
00039 setFormatFor(Math, mathFormat);
00040
00041 QTextCharFormat commandFormat;
00042 commandFormat.setForeground(QColor(255, 0, 0));
00043 setFormatFor(Command, commandFormat);
00044
00045 QTextCharFormat blockNameFormat;
00046 blockNameFormat.setForeground(QColor(0, 0, 255));
00047 setFormatFor(BlockName, blockNameFormat);
00048
00049 QTextCharFormat scribusFormat;
00050 scribusFormat.setForeground(QColor(192, 16, 112));
00051 scribusFormat.setFontWeight(QFont::Bold);
00052 setFormatFor(ScribusSpecial, scribusFormat);
00053
00054 QTextCharFormat commentFormat;
00055 commentFormat.setForeground(QColor(128, 128, 128));
00056 commentFormat.setFontItalic(true);
00057 setFormatFor(Comment, commentFormat);
00058 }
00059
00060 void LatexHighlighter::setFormatFor(Construct construct, const QTextCharFormat &format)
00061 {
00062 m_formats[construct] = format;
00063 rehighlight();
00064 }
00065
00066
00067 void LatexHighlighter::highlightBlock(const QString &text)
00068 {
00069 int state = previousBlockState();
00070 int len = text.length();
00071 int start = 0;
00072 int pos = 0;
00073
00074 while (pos < len) {
00075 switch (state) {
00076 case NormalState:
00077 default:
00078 while (pos < len) {
00079 QChar ch = text.at(pos);
00080 if (ch == '%') {
00081
00082 setFormat(pos, len - pos, m_formats[Comment]);
00083 pos = len;
00084 break;
00085 } else if (ch == '\\') {
00086 start = pos;
00087 pos++;
00088 if (pos >= len) break;
00089 ch = text.at(pos);
00090 if (ch == '[') {
00091 state = InMath;
00092 break;
00093 } else if ((text.mid(pos, 5) == "begin") || (text.mid(pos, 3) == "end")) {
00094 int saved_pos = pos;
00095 if (text.at(pos) == 'b') {
00096 setFormat(pos-1, 6, m_formats[Command]);
00097 pos += 5;
00098 } else {
00099 setFormat(pos-1, 4, m_formats[Command]);
00100 pos += 3;
00101 }
00102
00103 while (pos < len && text.at(pos++).isSpace()) ;
00104 if (text.at(pos-1) == '{') {
00105 start = pos;
00106 while (pos < len && text.at(pos++) != '}') ;
00107 pos--;
00108
00109 setFormat(start, pos - start, m_formats[BlockName]);
00110 } else {
00111 pos = saved_pos;
00112 }
00113 } else {
00114 while (pos < len) {
00115 ch = text.at(pos++);
00116
00117 if (!(((ch >= 'A') && (ch <= 'Z')) ||
00118 ((ch >= 'a') && (ch <= 'z')) || (ch == '@'))) break;
00119 }
00120
00121 if ((pos - start) == 2) {
00122
00123 setFormat(start, pos - start, m_formats[ScribusSpecial]);
00124 } else {
00125 setFormat(start, pos - start -1, m_formats[Command]);
00126 }
00127 }
00128 break;
00129 } else if (ch == '$') {
00130 if (text.mid(pos, 9) == "$scribus_") {
00131
00132 start = pos;
00133 pos++;
00134 while (pos < len && text.at(pos++) != '$') ;
00135 setFormat(start, pos - start, m_formats[ScribusSpecial]);
00136 } else {
00137 state = InDisplayMath;
00138 pos++;
00139 break;
00140 }
00141 }
00142 pos++;
00143 }
00144 break;
00145 case InDisplayMath:
00146 start = pos;
00147 if (start > 0) start--;
00148 while (pos < len) {
00149 if (text.at(pos) == '$') {
00150 pos++;
00151 state = NormalState;
00152 break;
00153 }
00154 pos++;
00155 }
00156 setFormat(start, pos - start, m_formats[Math]);
00157 break;
00158 case InMath:
00159 start = pos;
00160 if (start > 0) start--;
00161 while (pos < len) {
00162 if (text.mid(pos,2) == "\\]" ) {
00163 pos += 2;
00164 state = NormalState;
00165 break;
00166 }
00167 pos++;
00168 }
00169 setFormat(start, pos - start, m_formats[Math]);
00170 break;
00171 }
00172 }
00173 setCurrentBlockState(state);
00174 }
00175
00176
00177 bool LatexConfigParser::parseConfigFile(QString fn)
00178 {
00179 m_error = "";
00180 m_filename = fn;
00181 QFile f(fn);
00182 f.open(QIODevice::ReadOnly);
00183 xml.setDevice(&f);
00184
00185 while (!xml.atEnd()) {
00186 xml.readNext();
00187 if (xml.isWhitespace() || xml.isComment() || xml.isStartDocument() || xml.isEndDocument()) continue;
00188 if (xml.isStartElement() && xml.name() == "editorsettings") {
00189 m_description = xml.attributes().value("description").toString();
00190 m_icon = xml.attributes().value("icon").toString();
00191 if (m_description.isEmpty()) {
00192 m_description = fn;
00193 }
00194 parseElements();
00195 } else {
00196 formatError("Unexpected element at root level"+xml.name().toString()+", Token String: "+
00197 xml.tokenString());
00198 }
00199 }
00200 if (xml.hasError()) {
00201 formatError(xml.errorString());
00202 }
00203 f.close();
00204 if (!m_error.isEmpty()) {
00205 return false;
00206 }
00207 return true;
00208 }
00209
00210 void LatexConfigParser::parseElements()
00211 {
00212 while (!xml.atEnd()) {
00213 xml.readNext();
00214 if (xml.isEndElement() && xml.name() == "editorsettings") break;
00215 if (xml.isWhitespace() || xml.isComment() || xml.isEndElement()) continue;
00216 if (!xml.isStartElement()) {
00217 formatError("Unexpected element in <editorsettings>"+xml.name().toString()+", Token String: "+
00218 xml.tokenString());
00219 continue;
00220 }
00221
00222 if (xml.name() == "executable") {
00223 m_executable = xml.attributes().value("command").toString();
00224 } else if (xml.name() == "imagefile") {
00225 m_imageExtension = xml.attributes().value("extension").toString();
00226 } else if (xml.name() == "highlighter") {
00227 m_highlight_kate = xml.attributes().value("kate").toString();
00228 m_highlight_file = xml.attributes().value("file").toString();
00229 } else if (xml.name() == "empty-frame-text") {
00230 m_emptyFrameText = xml.readI18nText(true);
00231 } else if (xml.name() == "preamble") {
00232 m_preamble = xml.readElementText();
00233 } else if (xml.name() == "postamble") {
00234 m_postamble = xml.readElementText();
00235 } else if (xml.name() == "tab") {
00236 parseTab();
00237 } else {
00238 formatError("Unknown tag in <editorsettings>: "+xml.name().toString());
00239 }
00240 }
00241 }
00242
00243 void LatexConfigParser::formatError(QString message)
00244 {
00245 QString new_error = QString::number(xml.lineNumber()) + ":" +
00246 QString::number(xml.columnNumber()) + ":" + message;
00247 qDebug() << m_filename << new_error;
00248 m_error += new_error + "\n";
00249 }
00250
00251 void LatexConfigParser::parseTab()
00252 {
00253 QString type = xml.attributes().value("type").toString();
00254 bool itemstab = (type == "items");
00255 QString title = "";
00256 QString name, text, default_value;
00257
00258 while (!xml.atEnd()) {
00259 xml.readNext();
00260 if (xml.isWhitespace() || xml.isComment()) continue;
00261 if (xml.isEndElement() && xml.name() == "tab") break;
00262 if (!xml.isStartElement()) {
00263 formatError("Unexpected element in <tab>: "+xml.name().toString()+", Token String: "+
00264 xml.tokenString());
00265 continue;
00266 }
00267 if (xml.name() == "title") {
00268 if (!title.isEmpty()) {
00269 formatError("Second <title> tag in <tab>");
00270 }
00271 title = xml.readI18nText();
00272 } else if (xml.name() == "item") {
00273 if (!itemstab) {
00274 formatError("Found <item> in a 'settings'-tab!");
00275 }
00276 QString value = xml.attributes().value("value").toString();
00277 QString img = xml.attributes().value("image").toString();
00278 text = xml.readI18nText();
00279
00280 } else if (xml.name() == "comment" || xml.name() == "font"
00281 || xml.name() == "spinbox" || xml.name() == "color"
00282 || xml.name() == "text" || xml.name() == "list") {
00283
00284 QString tagname = xml.name().toString();
00285 name = xml.attributes().value("name").toString();
00286 default_value = xml.attributes().value("default").toString();
00287 if (xml.name() != "list") {
00288 text = xml.readI18nText();
00289 } else {
00290 ignoreList();
00291 }
00292 if (!name.isEmpty()) {
00293 if (properties.contains(name)) {
00294 formatError("Redeclared setting with name: " + name);
00295 } else {
00296 properties.insert(name, default_value);
00297 }
00298 }
00299 qDebug() << "For future use:" << tagname << name << text << default_value;
00300 } else {
00301 formatError("Unexpected element in <tab>: " + xml.name().toString());
00302 }
00303 }
00304
00305 if (title.isEmpty()) {
00306 formatError("Tab ended here, but no title was found!");
00307 }
00308 }
00309
00310 void LatexConfigParser::ignoreList()
00311 {
00312
00313 while (!xml.atEnd()) {
00314 xml.readNext();
00315 if (xml.isEndElement() && xml.name() == "list") break;
00316 }
00317 }
00318
00319 QString I18nXmlStreamReader::readI18nText(bool unindent)
00320 {
00321 QString language = PrefsManager::instance()->guiLanguage();
00322 QString result;
00323 int matchquality = 0;
00324 bool i18n = false;
00325 if (!isStartElement()) raiseError("readI18nText called without startelement!");
00326
00327 QString startTag = name().toString();
00328 while (!atEnd()) {
00329 readNext();
00330 if (isWhitespace() || isComment()) continue;
00331 if (isStartElement() && name() == startTag) {
00332 raiseError("Invalid nested elements.");
00333 return "Error";
00334 }
00335 if (isEndElement() && name() == startTag) {
00336 if (!unindent) {
00337 return result.trimmed();
00338 } else {
00339 QStringList splitted = result.split("\n");
00340 int i;
00341 int minspaces = 0xffff;
00342
00343 for (i = 1; i < splitted.size(); i++) {
00344 if (splitted[i].trimmed().isEmpty()) continue;
00345 int spaces;
00346 QString tmp = splitted[i];
00347 for (spaces = 0; spaces < tmp.length(); spaces++) {
00348 if (!tmp[spaces].isSpace()) break;
00349 }
00350 if (spaces < minspaces) minspaces = spaces;
00351 }
00352 for (i = 1; i < splitted.size(); i++) {
00353 splitted[i] = splitted[i].mid(minspaces);
00354 }
00355 return splitted.join("\n").trimmed();
00356 }
00357 }
00358 if (i18n) {
00359 if (isEndElement()) {
00360 if (name() == "i18n") {
00361 i18n = false;
00362 } else {
00363 raiseError("Invalid end element "+ name().toString());
00364 }
00365 continue;
00366 }
00367 if (!isStartElement()) {
00368 raiseError("Unexpected data!");
00369 }
00370 if (name() == language) {
00371 matchquality = 2;
00372 result = readElementText();
00373 } else if (language.startsWith(name().toString()) && matchquality <= 1) {
00374 matchquality = 1;
00375 result = readElementText();
00376 } else if (result.isEmpty()) {
00377 matchquality = 0;
00378 result = readElementText();
00379 } else {
00380 readElementText();
00381 }
00382 } else {
00383 if (isStartElement()) {
00384 if (name() == "i18n") {
00385 i18n = true;
00386 continue;
00387 } else {
00388 raiseError("Tag " + name().toString() +
00389 "found, but \"i18n\" or string data expected.");
00390 continue;
00391 }
00392 } else if (isCharacters()) {
00393 result = result + text().toString();
00394 }
00395 }
00396 }
00397 raiseError("Unexpected end of XML file");
00398 return result;
00399 }
00400
00401 LatexConfigCache* LatexConfigCache::_instance = 0;
00402
00403 LatexConfigCache* LatexConfigCache::instance()
00404 {
00405 if (!_instance) {
00406 _instance = new LatexConfigCache();
00407 }
00408 return _instance;
00409 }
00410
00411 LatexConfigParser* LatexConfigCache::parser(QString filename, bool warnOnError)
00412 {
00413 if (parsers.contains(filename)) {
00414 if (warnOnError && error[filename]) {
00415
00416 delete parsers[filename];
00417 createParser(filename, warnOnError);
00418 }
00419 } else {
00420 createParser(filename, warnOnError);
00421 }
00422 return parsers[filename];
00423 }
00424
00425 void LatexConfigCache::createParser(QString filename, bool warnOnError)
00426 {
00427 qDebug() << "Creating new parser" << filename;
00428 LatexConfigParser *parser = new LatexConfigParser();
00429 bool hasError = !parser->parseConfigFile(filename);
00430 parsers[filename] = parser;
00431 error[filename] = hasError;
00432 if (hasError) {
00433 QMessageBox::critical(0, QObject::tr("Error"), "<qt>" +
00434 QObject::tr("Parsing the configfile %1 failed! Depending on the type of the error "
00435 "latexframes might not work correctly!\n%2").arg(
00436 filename, parser->error())
00437 + "</qt>", 1, 0, 0);
00438 }
00439 }
00440
00441 bool LatexConfigCache::hasError(QString filename)
00442 {
00443 if (!error.contains(filename)) {
00444 return true;
00445 }
00446 return error[filename];
00447 }
00448
00449 QStringList LatexConfigCache::defaultConfigs()
00450 {
00451
00452
00453 QString base = ScPaths::instance().shareDir()+"/editorconfig/";
00454 QDir dir(base);
00455 QStringList files;
00456 files = dir.entryList(QStringList("*.xml"));
00457 files.sort();
00458 int i;
00459 for (i = 0; i < files.size(); i++) {
00460 if (files[i].compare("sample.xml",Qt::CaseInsensitive)==0) {
00461 files.removeAt(i);
00462 i--;
00463 } else {
00464 files[i] = base + files[i];
00465 }
00466 }
00467 return files;
00468 }