diff --git a/docs/RELEASES b/docs/RELEASES index dcec7df..333e2f2 100644 --- a/docs/RELEASES +++ b/docs/RELEASES @@ -1,3 +1,12 @@ +0.9.1 - Fixed the bug I totally missed when implementing hyperlink support + yesterday. They should now work properly. + + Parser improvements: IMP should now be able to recognize multiple + sentences within an intel report and handle more complex reports + such as "20+ red KBP. XHQ clr! JEIV pocket status?". + + Fixed a memory leak introduced in 0.9.0. + 0.9.0 - Automatic update checking! Versions beyond 0.9.0 should alert you when a new release has been uploaded/committed. @@ -282,5 +291,6 @@ Coming soon: - fix display of messages with backslashes. +- fix bug where map starts updating only every 5 seconds. - additional sound/volume options for alerts, dependant on jump distance. - better pilot cache/cleanup. diff --git a/src/chatitemdelegate.cpp b/src/chatitemdelegate.cpp index cdd9ebf..9dccb51 100644 --- a/src/chatitemdelegate.cpp +++ b/src/chatitemdelegate.cpp @@ -65,7 +65,7 @@ void ChatItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opti doc->documentLayout()->draw(painter, ctx); painter->restore(); - doc->deleteLater(); + delete doc; } } @@ -114,6 +114,7 @@ QSize ChatItemDelegate::sizeHint(const QStyleOptionViewItem& option, const QMode doc->size().height() + padding.height() : m_avatarSize.height() + padding.height()); + delete doc; return textSize; } @@ -126,7 +127,6 @@ QString ChatItemDelegate::anchorAt(const QStyleOptionViewItem& option, QTextDocument* doc = document(options); QAbstractTextDocumentLayout* textLayout = doc->documentLayout(); - doc->deleteLater(); Q_ASSERT(textLayout != 0); @@ -135,5 +135,7 @@ QString ChatItemDelegate::anchorAt(const QStyleOptionViewItem& option, QPoint p = point; p.setX(p.x() - iconWidth - padding.width()); - return textLayout->anchorAt(p); + QString anchor = textLayout->anchorAt(p); + delete doc; + return anchor; } diff --git a/src/imp.pro b/src/imp.pro index c83c9e1..315d08b 100644 --- a/src/imp.pro +++ b/src/imp.pro @@ -12,7 +12,7 @@ greaterThan(QT_MAJOR_VERSION, 4): QT += widgets TARGET = imp TEMPLATE = app -VERSION = 0.9.0 +VERSION = 0.9.1 QMAKE_TARGET_COMPANY = EternalDusk QMAKE_TARGET_DESCRIPTION = Eve Online Intelligence Management Program QMAKE_TARGET_COPYRIGHT = (c) Copyright 2016-2017 Jesse Litton diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 79913d6..26b6179 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1245,7 +1245,7 @@ void MainWindow::fileChanged(const QString &absoluteFilePath) } } - if(toBeAddedToList) + if(toBeAddedToList && message.skipOutput != true) { addMessage(message); } diff --git a/src/map.cpp b/src/map.cpp index 5674e62..8a9c5f2 100755 --- a/src/map.cpp +++ b/src/map.cpp @@ -29,7 +29,7 @@ Map::Map(QObject *parent) : QObject(parent) { m_timer = new QTimer(this); - setRefresh(5000); + setRefresh(1000); connect(m_timer, SIGNAL(timeout()), this, SLOT(updateActiveSystems())); } diff --git a/src/meta.h b/src/meta.h index b40f76c..16299ba 100644 --- a/src/meta.h +++ b/src/meta.h @@ -27,8 +27,8 @@ static const struct Version { Version(){} - QString release = "0.9.0"; //VERSION; - QString name = "Round and Round Lycaan"; + QString release = "0.9.1"; //VERSION; + QString name = "Lycaan Trophy"; QString styleHeader1 = ""; QString styleFooter1 = ""; diff --git a/src/parser.cpp b/src/parser.cpp index 1a7f82d..5c98a89 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -26,7 +26,6 @@ #include #include #include -#include #include #include @@ -35,6 +34,8 @@ using namespace std; Parser::Parser(uint generation, QObject *parent) : QObject(parent) { this->generation = generation; + + listener.setMinimal(true); ignoreChars = "[\\\\_=!@#$%^&\\*,\\.\\[\\]\\(\\)\\{\\}\\?]"; // "\\_=!@#$%^&*,./[](){}?" @@ -135,23 +136,28 @@ QList Parser::fileChanged(const QString& path, int maxEntries, bool for (int i=startLine; i newMessages = parseLine(lines[i].trimmed().remove(0xfeff)); - newMessage.parserGeneration = generation; - newMessage.logInfo = &fileMap[path]; - if(newMessage.systems.length() > 0) + // Okay, these aren't really separate user-messages... they are + // messages to be processed by main window... clean up later. + foreach(MessageInfo newMessage, newMessages) { - newMessage.logInfo->systemLastMentioned = newMessage.systems[0]; - } - - if (!newMessage.indecipherable) - { - messageInfoList.append(newMessage); - } - else - { - qDebug() << "Parser::fileChanged() - Indecipherable message: " << - fileMap[path].channel << "> " << lines[i] << endl; + newMessage.parserGeneration = generation; + newMessage.logInfo = &fileMap[path]; + if(newMessage.systems.length() > 0) + { + newMessage.logInfo->systemLastMentioned = newMessage.systems[0]; + } + + if (!newMessage.indecipherable) + { + messageInfoList.append(newMessage); + } + else + { + qDebug() << "Parser::fileChanged() - Indecipherable message: " << + fileMap[path].channel << "> " << lines[i] << endl; + } } } fileMap[path].position = input.pos(); @@ -159,64 +165,167 @@ QList Parser::fileChanged(const QString& path, int maxEntries, bool return messageInfoList; } -MessageInfo Parser::parseLine(const QString& line) +QList Parser::parseLine(const QString& line) { - MessageInfo messageInfo; - messageInfo.originalLine = line; + QList messages; + + QDateTime dateTime; + QString sender, text; - // Get timestamp - QRegExp listener("^\\[ (.{19}) \\] ([^>]+) > ([^\\.\\?!]*)(.*)$"); - listener.setMinimal(true); + // Does message look valid? if (listener.indexIn(line.trimmed()) != -1) { - messageInfo.indecipherable = false; + dateTime = QDateTime::fromString(listener.cap(1), "yyyy.MM.dd HH:mm:ss"); + dateTime.setTimeSpec(Qt::UTC); + sender = listener.cap(2); + text = listener.cap(3); + } + else + { + // Can't make heads or tails of this, return upstream. + MessageInfo messageInfo; + messageInfo.originalLine = line; + messageInfo.indecipherable = true; + messages.append(messageInfo); + return messages; + } + - QString cap1 = listener.cap(1); - messageInfo.dateTime = QDateTime::fromString(listener.cap(1), "yyyy.MM.dd HH:mm:ss"); + // Is this a system message? + if (sender == "EVE System") + { + MessageInfo messageInfo; + messageInfo.originalLine = line; + messageInfo.dateTime = dateTime; messageInfo.dateTime.setTimeSpec(Qt::UTC); - messageInfo.sender = listener.cap(2); + messageInfo.sender = sender; + messageInfo.text = text; - messageInfo.text = listener.cap(3); + // Test to see if this is a system change message: + // EVE System > Channel changed to Local : JEIV-E - if (messageInfo.sender == "EVE System") + QRegExp sysMsgRegExp("^Channel changed to .* : (.*)$"); + sysMsgRegExp.setMinimal(true); + if (sysMsgRegExp.indexIn(text.trimmed()) != -1) { - //qDebug() << "Parser::parseLine(" << line << ")"; + //qDebug() << " is a system change message."; + messageInfo.systems.append(sysMsgRegExp.cap(1)); + messageInfo.flags.append(MessageFlag::SYSTEM_CHANGE); + messages.append(messageInfo); + return messages; + } - // Test to see if this is a system change message: - // EVE System > Channel changed to Local : JEIV-E + // Test to see if this is a MOTD message: - QRegExp sysMsgRegExp("^Channel changed to .* : (.*)$"); - sysMsgRegExp.setMinimal(true); - if (sysMsgRegExp.indexIn(messageInfo.text.trimmed()) != -1) - { - //qDebug() << " is a system change message."; - messageInfo.systems.append(sysMsgRegExp.cap(1)); - messageInfo.flags.append(MessageFlag::SYSTEM_CHANGE); - return messageInfo; - } + QRegExp motdRegExp("^Channel MOTD: (.*)$"); + if (motdRegExp.indexIn(messageInfo.text.trimmed()) != -1) + { + messageInfo.flags.append(MOTD); + messages.append(messageInfo); + return messages; + } - // Test to see if this is a MOTD message: + // Test to see if this is an ESS message: + QRegExp essRegExp("(.*) is now in proximity of the Encounter Surveillance System$"); + if (essRegExp.indexIn(messageInfo.text.trimmed()) != -1) + { + messageInfo.flags.append(ESS); + messageInfo.related.append(essRegExp.cap(1)); + messages.append(messageInfo); + return messages; + } + } - QRegExp motdRegExp("^Channel MOTD: (.*)$"); - if (motdRegExp.indexIn(messageInfo.text.trimmed()) != -1) - { - messageInfo.flags.append(MOTD); - return messageInfo; - } - // Test to see if this is an ESS message: - QRegExp essRegExp("(.*) is now in proximity of the Encounter Surveillance System$"); - if (essRegExp.indexIn(messageInfo.text.trimmed()) != -1) - { - messageInfo.flags.append(ESS); - messageInfo.related.append(essRegExp.cap(1)); - return messageInfo; - } + // Valid, non-system message. Deconstruct it into sentences and words. + QRegExp puncPreExp("^" + ignoreChars + "+"); + QRegExp puncPostExp(ignoreChars + "+$"); + + int currentPos = 0, + nextSpace = 0; + QList* impSentence = new QList; + QList> impSentences; + // Build sentences and their word info. + while(currentPos <= text.length()) + { + ImpWord impWord; + + nextSpace = text.indexOf(' ', currentPos+1); + if(nextSpace == -1) + nextSpace = text.length(); + + if(text[currentPos] == ' ') + { + currentPos++; + continue; + } + + impWord.raw = text.mid(currentPos, nextSpace-currentPos); + qDebug() << "Parser::identifyObjects - raw =" << impWord.raw; + + if (puncPostExp.indexIn(impWord.raw) != -1) + impWord.postfix = puncPostExp.cap(); + else + impWord.postfix = ""; + impWord.postfixStart = currentPos + impWord.raw.length() - impWord.postfix.length(); + + // Only build prefix punctuation, if entire string wasn't punctuation. + if(impWord.postfix != impWord.raw) + { + if (puncPreExp.indexIn(impWord.raw) != -1) + impWord.prefix = puncPreExp.cap(); + else + impWord.prefix = ""; + } + impWord.prefixStart = currentPos; + + qDebug() << "Parser::identifyObjects - prefix =" << impWord.prefix << + ", prefixStart =" << impWord.prefixStart << + ", postfix =" << impWord.postfix << + ", postfixStart =" << impWord.postfixStart; + + impWord.actual = impWord.raw.mid(impWord.prefix.length(), + impWord.raw.length()-impWord.postfix.length()); + + qDebug() << "Parser::identifyObjects - actual =" << impWord.actual; + + currentPos = nextSpace + 1; + impSentence->append(impWord); + + if(nextSpace >= text.length()) + impSentences.append(*impSentence); + else if(impWord.postfix.endsWith('.') || + impWord.postfix.endsWith('!') || + impWord.postfix.endsWith('?')) + { + impSentences.append(*impSentence); + delete impSentence; + impSentence = new QList; } + } + delete impSentence; + + // Now process each sentence in the message and return a message for each. + + QString markedUpText = ""; + foreach(QList s, impSentences) + { + qDebug() << "impSentence:"; + foreach (ImpWord iw, s) { + qDebug() << '\t' << iw.prefix << iw.actual << iw.postfix; + } + + MessageInfo messageInfo; + messageInfo.originalLine = line; + messageInfo.dateTime = dateTime; + messageInfo.dateTime.setTimeSpec(Qt::UTC); + messageInfo.sender = sender; + messageInfo.text = text; + messageInfo.skipOutput = true; // Bring in some bison later and dump this simple placeholder - identifyObjects(messageInfo); + markedUpText += identifyObjects(messageInfo, s); QString endingPunctuation = listener.cap(4); //qDebug() << "endingPunctuation = " << endingPunctuation; @@ -231,74 +340,73 @@ MessageInfo Parser::parseLine(const QString& line) { messageInfo.flags.append(MessageFlag::WARNING); } - } - else - { - messageInfo.indecipherable = true; + + messages.append(messageInfo); } - return messageInfo; + messages[messages.count()-1].markedUpText = markedUpText; + messages[messages.count()-1].skipOutput = false; + return messages; } -void Parser::identifyObjects(MessageInfo& messageInfo) +QString Parser::identifyObjects(MessageInfo& messageInfo, QList &sentence) { + QString markedUpText = ""; QStringList theseSystems; QStringList theseShips; QStringList theseGates; - // Turn any punctuation into whitespace and remove all redundant whitespace. - // Makes things like "name in XX-XXX...svipul" not get misparsed. - QString simChat = messageInfo.text; - simChat = simChat.replace(QRegExp(ignoreChars), " "); - simChat = simChat.simplified(); - QStringList words = simChat.split(" "); + // New parsing + qDebug() << "Parser::identifyObjects - Parsing sentence in: " << messageInfo.text; - messageInfo.markedUpText = ""; - - QString previousWord = ""; - for (int i = 0; i < words.length(); i++) + for(int i=0; i 0 && sentence[i].actual.toLower() != "gate") { // We don't want to clear a system if someone says a gate is clear. // "> KBP Dital gate clr!" messageInfo.flags.append(MessageFlag::CLEAR); - - messageInfo.markedUpText += ""; - messageInfo.markedUpText += words[i]; - messageInfo.markedUpText += ""; + markedUpText += sentence[i].prefix; + markedUpText += ""; + markedUpText += sentence[i].actual; + markedUpText += ""; + markedUpText += sentence[i].postfix; } } else if(ships.contains(lowerWord)) { - theseShips.append(words[i]); - messageInfo.markedUpText += ""; - messageInfo.markedUpText += words[i]; - messageInfo.markedUpText += ""; + theseShips.append(sentence[i].actual); + markedUpText += sentence[i].prefix; + markedUpText += ""; + markedUpText += sentence[i].actual; + markedUpText += ""; + markedUpText += sentence[i].postfix; } else if(lowerWord == "pocket") { @@ -306,69 +414,78 @@ void Parser::identifyObjects(MessageInfo& messageInfo) } else if(lowerWord.contains(QRegExp("[^ ]{3,5}://.+"))) { - messageInfo.markedUpText += ""; - messageInfo.markedUpText += words[i]; - messageInfo.markedUpText += ""; + markedUpText += ""; + markedUpText += sentence[i].raw; + markedUpText += ""; messageInfo.flags.append(MessageFlag::LINK); } else { - QString systemName = regionMap->getSystemByAbbreviation(words[i].toUpper()); + QString systemName = regionMap->getSystemByAbbreviation(sentence[i].actual.toUpper()); if(systemName.length() > 0) { - if(i < (words.length()-1) && words[i+1].toLower() == "gate") + if(i < (sentence.length()-1) && sentence[i+1].actual.toLower() == "gate") { - if(i < (words.length()-2) && words[i+2].toLower() == "to") + if(i < (sentence.length()-2) && sentence[i+2].actual.toLower() == "to") { // "x-x gate to..." theseSystems.append(systemName); - messageInfo.markedUpText += ""; - messageInfo.markedUpText += words[i]; - messageInfo.markedUpText += ""; + markedUpText += sentence[i].prefix; + markedUpText += ""; + markedUpText += sentence[i].actual; + markedUpText += ""; + markedUpText += sentence[i].postfix; } else { // "...at x-x gate" theseGates.append(systemName); - messageInfo.markedUpText += ""; - messageInfo.markedUpText += words[i]; - messageInfo.markedUpText += ""; + markedUpText += sentence[i].prefix; + markedUpText += ""; + markedUpText += sentence[i].actual; + markedUpText += ""; + markedUpText += sentence[i].postfix; } } - else if(i > 0 && left.contains(words[i-1].toLower()) ) + else if(i > 0 && left.contains(sentence[i-1].actual.toLower()) ) { // Check to see if it is "left *system*", "from *system*", etc. - messageInfo.markedUpText += ""; - messageInfo.markedUpText += words[i]; - messageInfo.markedUpText += ""; + markedUpText += sentence[i].prefix; + markedUpText += ""; + markedUpText += sentence[i].actual; + markedUpText += ""; + markedUpText += sentence[i].postfix; } else { // System mentioned, not adjacent to word 'gate' or one of // the words indicating they left a system. theseSystems.append(systemName); - messageInfo.markedUpText += ""; - messageInfo.markedUpText += words[i]; - messageInfo.markedUpText += ""; + markedUpText += sentence[i].prefix; + markedUpText += ""; + markedUpText += sentence[i].actual; + markedUpText += ""; + markedUpText += sentence[i].postfix; } } else if(lowerWord.length() >= 2) { - messageInfo.possiblePilots.append(words[i]); - messageInfo.markedUpText += ""; - messageInfo.markedUpText += words[i]; - messageInfo.markedUpText += ""; + messageInfo.possiblePilots.append(sentence[i].actual); + markedUpText += ""; + markedUpText += sentence[i].prefix; + markedUpText += sentence[i].actual; + markedUpText += sentence[i].postfix; } } - previousWord = lowerWord; - - if(i < words.length()) - messageInfo.markedUpText += " "; + if(i < sentence.length()) + markedUpText += " "; } messageInfo.systems = theseSystems; messageInfo.ships = theseShips; messageInfo.gates = theseGates; + + return markedUpText; } diff --git a/src/parser.h b/src/parser.h index 3adf09f..bdfd995 100755 --- a/src/parser.h +++ b/src/parser.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -51,6 +52,8 @@ struct LogInfo struct MessageInfo { uint parserGeneration = 0; + bool skipOutput = false; + QString originalLine; bool indecipherable = false; @@ -70,6 +73,16 @@ struct MessageInfo QList flags; }; +struct ImpWord +{ + QString raw; + QString prefix; + int prefixStart; + QString actual; + QString postfix; + int postfixStart; +}; + class Parser : public QObject { Q_OBJECT @@ -90,6 +103,9 @@ public slots: private: uint generation = 0; + //QRegExp listener = QRegExp("^\\[ (.{19}) \\] ([^>]+) > ([^\\.\\?!]*)(.*)$"); + QRegExp listener = QRegExp("^\\[ (.{19}) \\] ([^>]+) > (.*)$"); + QString lastListener; QString lastLocalSystem; @@ -106,9 +122,9 @@ public slots: Map* regionMap; void loadSet(QSet& set, QString& string); - void identifyObjects(MessageInfo& messageInfo); + QString identifyObjects(MessageInfo& messageInfo, QList& sentence); QString systemAbbreviation(const QString& word); - MessageInfo parseLine(const QString& line); + QList parseLine(const QString& line); }; #endif // PARSER_H diff --git a/src/svgmapview.cpp b/src/svgmapview.cpp index 673e58e..86907c6 100755 --- a/src/svgmapview.cpp +++ b/src/svgmapview.cpp @@ -72,6 +72,7 @@ SvgMapView::SvgMapView(QWidget *parent) : QGraphicsView(parent) SvgMapView::~SvgMapView() { + qDeleteAll(pilotShapes.begin(), pilotShapes.end()); findShape->deleteLater(); }