diff --git a/can_structs.h b/can_structs.h index da60766..74e5b0a 100644 --- a/can_structs.h +++ b/can_structs.h @@ -16,11 +16,24 @@ public: uint32_t len; unsigned char data[8]; uint64_t timestamp; + uint32_t frameCount; //used in overwrite mode friend bool operator<(const CANFrame& l, const CANFrame& r) { return l.timestamp < r.timestamp; } + + CANFrame() + { + ID = 0; + bus = 0; + extended = false; + remote = false; + isReceived = true; + len = 0; + timestamp = 0; + frameCount = 1; + } }; class CANFltObserver diff --git a/canframemodel.cpp b/canframemodel.cpp index 199a196..5865268 100644 --- a/canframemodel.cpp +++ b/canframemodel.cpp @@ -289,7 +289,8 @@ QVariant CANFrameModel::data(const QModelIndex &index, int role) const case Column::Extended: return QString::number(thisFrame.extended); case Column::Remote: - return QString::number(thisFrame.remote); + if (!overwriteDups) return QString::number(thisFrame.remote); + return QString::number(thisFrame.frameCount); case Column::Direction: if (thisFrame.isReceived) return QString(tr("Rx")); return QString(tr("Tx")); @@ -383,7 +384,8 @@ QVariant CANFrameModel::headerData(int section, Qt::Orientation orientation, case Column::Extended: return QString(tr("Ext")); case Column::Remote: - return QString(tr("Rem")); + if (!overwriteDups) return QString(tr("Rem")); + return QString(tr("Cnt")); case Column::Direction: return QString(tr("Dir")); case Column::Bus: @@ -429,6 +431,7 @@ void CANFrameModel::addFrame(const CANFrame& frame, bool autoRefresh = false) if (filters[tempFrame.ID]) { if (autoRefresh) beginInsertRows(QModelIndex(), filteredFrames.count(), filteredFrames.count()); + tempFrame.frameCount = 1; filteredFrames.append(tempFrame); if (autoRefresh) endInsertRows(); } @@ -440,6 +443,7 @@ void CANFrameModel::addFrame(const CANFrame& frame, bool autoRefresh = false) { if (frames[i].ID == tempFrame.ID) { + tempFrame.frameCount = frames[i].frameCount + 1; frames.replace(i, tempFrame); found = true; break; @@ -451,6 +455,7 @@ void CANFrameModel::addFrame(const CANFrame& frame, bool autoRefresh = false) if (filters[tempFrame.ID]) { if (autoRefresh) beginInsertRows(QModelIndex(), filteredFrames.count(), filteredFrames.count()); + tempFrame.frameCount = 1; filteredFrames.append(tempFrame); if (autoRefresh) endInsertRows(); } diff --git a/config.h b/config.h index b072063..c9a1b1c 100644 --- a/config.h +++ b/config.h @@ -1,7 +1,7 @@ #ifndef CONFIG #define CONFIG -#define VERSION 196 +#define VERSION 197 //try to keep this in sync. //SavvyCAN will complain if you connect a GVRET board with a revision diff --git a/connections/canconmanager.cpp b/connections/canconmanager.cpp index 014248f..2b285b4 100644 --- a/connections/canconmanager.cpp +++ b/connections/canconmanager.cpp @@ -94,10 +94,10 @@ void CANConManager::refreshCanList() if (mConns.count() == 0) { + tempFrames.clear(); //TODO: Seems to crash under heavy load. Find out why. - if(buslessFrames.size()) { - tempFrames.clear(); - tempFrames.append(buslessFrames); //make a copy and pass that copy + if(buslessFrames.size()) { + tempFrames = buslessFrames; //make a copy and pass that copy buslessFrames.clear(); //delete all frames from the original emit framesReceived(nullptr, tempFrames); } diff --git a/dbc/dbchandler.cpp b/dbc/dbchandler.cpp index b1353c2..6bf7c85 100644 --- a/dbc/dbchandler.cpp +++ b/dbc/dbchandler.cpp @@ -1472,6 +1472,21 @@ DBC_MESSAGE* DBCHandler::findMessage(const CANFrame &frame) return nullptr; } +/* + * As above, a real shortcut function that searches all files in order to try to find a message with the given name +*/ +DBC_MESSAGE* DBCHandler::findMessage(const QString msgName) +{ + DBC_MESSAGE *msg = nullptr; + for(int i = 0; i < loadedFiles.count(); i++) + { + DBCFile * file = getFileByIdx(i); + msg = file->messageHandler->findMsgByName(msgName); + if (msg) return msg; //if it's not null then we have a match so return it + } + return nullptr; //no match, tough luck, return null +} + int DBCHandler::getFileCount() { return loadedFiles.count(); diff --git a/dbc/dbchandler.h b/dbc/dbchandler.h index eb4a32b..ebe48e1 100644 --- a/dbc/dbchandler.h +++ b/dbc/dbchandler.h @@ -96,6 +96,7 @@ public: void removeAllFiles(); void swapFiles(int pos1, int pos2); DBC_MESSAGE* findMessage(const CANFrame &frame); + DBC_MESSAGE* findMessage(const QString msgName); int getFileCount(); DBCFile* getFileByIdx(int idx); DBCFile* getFileByName(QString name); diff --git a/dbc/dbcloadsavewindow.cpp b/dbc/dbcloadsavewindow.cpp index e4dcb5c..07f34ab 100644 --- a/dbc/dbcloadsavewindow.cpp +++ b/dbc/dbcloadsavewindow.cpp @@ -34,7 +34,7 @@ DBCLoadSaveWindow::DBCLoadSaveWindow(const QVector *frames, QWidget *p connect(ui->tableFiles, &QTableWidget::cellChanged, this, &DBCLoadSaveWindow::cellChanged); connect(ui->tableFiles, &QTableWidget::cellDoubleClicked, this, &DBCLoadSaveWindow::cellDoubleClicked); - editorWindow = new DBCMainEditor(frames); + editorWindow = new DBCMainEditor(frames, this); currentlyEditingFile = nullptr; installEventFilter(this); diff --git a/dbc/dbcmaineditor.cpp b/dbc/dbcmaineditor.cpp index 56a9899..62819e9 100644 --- a/dbc/dbcmaineditor.cpp +++ b/dbc/dbcmaineditor.cpp @@ -49,7 +49,7 @@ DBCMainEditor::DBCMainEditor( const QVector *frames, QWidget *parent) connect(ui->MessagesTable, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onCustomMenuMessage(QPoint))); ui->MessagesTable->setContextMenuPolicy(Qt::CustomContextMenu); - sigEditor = new DBCSignalEditor(); + sigEditor = new DBCSignalEditor(this); installEventFilter(this); } diff --git a/framefileio.cpp b/framefileio.cpp index fd6d01b..aa38478 100644 --- a/framefileio.cpp +++ b/framefileio.cpp @@ -3,7 +3,7 @@ #include #include #include - +#include #include #include "utility.h" @@ -139,6 +139,7 @@ bool FrameFileIO::loadFrameFile(QString &fileName, QVector* frameCache bool result = false; QStringList filters; + filters.append(QString(tr("Autodetect File Type (*.*)"))); filters.append(QString(tr("GVRET Logs (*.csv *.CSV)"))); filters.append(QString(tr("CRTD Logs (*.txt *.TXT)"))); filters.append(QString(tr("Generic ID/Data CSV (*.csv *.CSV)"))); @@ -176,24 +177,25 @@ bool FrameFileIO::loadFrameFile(QString &fileName, QVector* frameCache qApp->processEvents(); - if (dialog.selectedNameFilter() == filters[0]) result = loadNativeCSVFile(filename, frameCache); - if (dialog.selectedNameFilter() == filters[1]) result = loadCRTDFile(filename, frameCache); - if (dialog.selectedNameFilter() == filters[2]) result = loadGenericCSVFile(filename, frameCache); - if (dialog.selectedNameFilter() == filters[3]) result = loadLogFile(filename, frameCache); - if (dialog.selectedNameFilter() == filters[4]) result = loadMicrochipFile(filename, frameCache); - if (dialog.selectedNameFilter() == filters[5]) result = loadTraceFile(filename, frameCache); - if (dialog.selectedNameFilter() == filters[6]) result = loadIXXATFile(filename, frameCache); - if (dialog.selectedNameFilter() == filters[7]) result = loadCANDOFile(filename, frameCache); - if (dialog.selectedNameFilter() == filters[8]) result = loadVehicleSpyFile(filename, frameCache); - if (dialog.selectedNameFilter() == filters[9]) result = loadCanDumpFile(filename, frameCache); - if (dialog.selectedNameFilter() == filters[10]) result = loadPCANFile(filename, frameCache); - if (dialog.selectedNameFilter() == filters[11]) result = loadKvaserFile(filename, frameCache, false); - if (dialog.selectedNameFilter() == filters[12]) result = loadKvaserFile(filename, frameCache, true); - if (dialog.selectedNameFilter() == filters[13]) result = loadCanalyzerASC(filename, frameCache); - if (dialog.selectedNameFilter() == filters[14]) result = loadCanalyzerBLF(filename, frameCache); - if (dialog.selectedNameFilter() == filters[15]) result = loadCANHackerFile(filename, frameCache); - if (dialog.selectedNameFilter() == filters[16]) result = loadCabanaFile(filename, frameCache); - if (dialog.selectedNameFilter() == filters[17]) result = loadCANOpenFile(filename, frameCache); + if (dialog.selectedNameFilter() == filters[0]) result = autoDetectLoadFile(filename, frameCache); + if (dialog.selectedNameFilter() == filters[1]) result = loadNativeCSVFile(filename, frameCache); + if (dialog.selectedNameFilter() == filters[2]) result = loadCRTDFile(filename, frameCache); + if (dialog.selectedNameFilter() == filters[3]) result = loadGenericCSVFile(filename, frameCache); + if (dialog.selectedNameFilter() == filters[4]) result = loadLogFile(filename, frameCache); + if (dialog.selectedNameFilter() == filters[5]) result = loadMicrochipFile(filename, frameCache); + if (dialog.selectedNameFilter() == filters[6]) result = loadTraceFile(filename, frameCache); + if (dialog.selectedNameFilter() == filters[7]) result = loadIXXATFile(filename, frameCache); + if (dialog.selectedNameFilter() == filters[8]) result = loadCANDOFile(filename, frameCache); + if (dialog.selectedNameFilter() == filters[9]) result = loadVehicleSpyFile(filename, frameCache); + if (dialog.selectedNameFilter() == filters[10]) result = loadCanDumpFile(filename, frameCache); + if (dialog.selectedNameFilter() == filters[11]) result = loadPCANFile(filename, frameCache); + if (dialog.selectedNameFilter() == filters[12]) result = loadKvaserFile(filename, frameCache, false); + if (dialog.selectedNameFilter() == filters[13]) result = loadKvaserFile(filename, frameCache, true); + if (dialog.selectedNameFilter() == filters[14]) result = loadCanalyzerASC(filename, frameCache); + if (dialog.selectedNameFilter() == filters[15]) result = loadCanalyzerBLF(filename, frameCache); + if (dialog.selectedNameFilter() == filters[16]) result = loadCANHackerFile(filename, frameCache); + if (dialog.selectedNameFilter() == filters[17]) result = loadCabanaFile(filename, frameCache); + if (dialog.selectedNameFilter() == filters[18]) result = loadCANOpenFile(filename, frameCache); progress.cancel(); @@ -215,6 +217,220 @@ bool FrameFileIO::loadFrameFile(QString &fileName, QVector* frameCache } +//Try every format by first uses the "is" functions which try to detect whether a given file is a good match to that +//file format or not. Those functions are much less tolerant than the load functions and so should help to discriminate +//whether a file could be loaded or not by a given loader. The loader return is still used in case the guess was wrong. +bool FrameFileIO::autoDetectLoadFile(QString filename, QVector* frames) +{ + if (isCanalyzerBLF(filename)) + { + if (loadCanalyzerBLF(filename, frames)) + { + qDebug() << "Loaded as Canalyzer BLF successfully!"; + return true; + } + } + + if (isNativeCSVFile(filename)) + { + if (loadNativeCSVFile(filename, frames)) + { + qDebug() << "Loaded as native CSV successfully!"; + return true; + } + } + + if (isCanalyzerASC(filename)) + { + if (loadCanalyzerASC(filename, frames)) + { + qDebug() << "Loaded as Canalyzer ASC successfully!"; + return true; + } + } + + if (isCRTDFile(filename)) + { + if (loadCRTDFile(filename, frames)) + { + qDebug() << "Loaded as CRTD successfully!"; + return true; + } + } + + + if (isTraceFile(filename)) + { + if (loadTraceFile(filename, frames)) + { + qDebug() << "Loaded as trace successfully!"; + return true; + } + } + + if (isVehicleSpyFile(filename)) + { + if (loadVehicleSpyFile(filename, frames)) + { + qDebug() << "Loaded as vehicle spy successfully!"; + return true; + } + } + + if (isCanDumpFile(filename)) + { + if (loadCanDumpFile(filename, frames)) + { + qDebug() << "Loaded as candump successfully!"; + return true; + } + } + + if (isCANHackerFile(filename)) + { + if (loadCANHackerFile(filename, frames)) + { + qDebug() << "Loaded as CANHacker successfully!"; + return true; + } + } + + if (isCabanaFile(filename)) + { + if (loadCabanaFile(filename, frames)) + { + qDebug() << "Loaded as Cabana successfully!"; + return true; + } + } + + if (isCANOpenFile(filename)) + { + if (loadCANOpenFile(filename, frames)) + { + qDebug() << "Loaded as CANOpen Magic successfully!"; + return true; + } + } + + if (isLogFile(filename)) + { + if (loadLogFile(filename, frames)) + { + qDebug() << "Loaded as Busmaster Log successfully!"; + return true; + } + } + + if (isPCANFile(filename)) + { + if (loadPCANFile(filename, frames)) + { + qDebug() << "Loaded as PCAN successfully!"; + return true; + } + } + + if (isIXXATFile(filename)) + { + if (loadIXXATFile(filename, frames)) + { + qDebug() << "Loaded as IXXAT successfully!"; + return true; + } + } + + if (isMicrochipFile(filename)) + { + if (loadMicrochipFile(filename, frames)) + { + qDebug() << "Loaded as microchip successfully!"; + return true; + } + } + + if (isCANDOFile(filename)) + { + if (loadCANDOFile(filename, frames)) + { + qDebug() << "Loaded as CANDO successfully!"; + return true; + } + } + + if (isKvaserFile(filename)) + { + if (loadKvaserFile(filename, frames,true)) + { + qDebug() << "Loaded as Kvaser HEX successfully!"; + return true; + } + if (loadKvaserFile(filename, frames,false)) + { + qDebug() << "Loaded as KVaser Decimal successfully!"; + return true; + } + } + + if (isGenericCSVFile(filename)) + { + if (loadGenericCSVFile(filename, frames)) + { + qDebug() << "Loaded as generic CSV successfully!"; + return true; + } + } + + return false; +} + + +bool FrameFileIO::isVehicleSpyFile(QString filename) +{ + QFile *inFile = new QFile(filename); + QByteArray line; + bool foundProbableHeader = false; + bool isMatch = false; + if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) + { + delete inFile; + return false; + } + try { + for (int i = 0; i < 10; i++) + { + if (!inFile->atEnd()) + { + line = inFile->readLine().simplified().toUpper(); + if (line.startsWith("LINE") && line.contains("TIME") && line.contains("B1")) + { + foundProbableHeader = true; + } + } + } + if (foundProbableHeader) + { + if (!inFile->atEnd()) + { + line = inFile->readLine().simplified().toUpper(); + inFile->close(); + QList tokens = line.split(','); + if (tokens.length() > 20) + { + if (tokens[9].toInt(NULL, 16) > 0) isMatch = true; + } + } + } + } + catch (...) + { + isMatch = false; + } + inFile->close(); + return isMatch; +} + + //2,2550.368293675,0.003818174999651092,67371008,F,F,HS CAN $119,HS CAN,,119,F,F,00,00,00,00,00,00,0D,8B,,, //Line,Abs Time(Sec),Rel Time (Sec),Status,Er,Tx,Description,Network,Node,Arb ID,Remote,Xtd,B1,B2,B3,B4,B5,B6,B7,B8,Value,Trigger,Signals // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @@ -294,6 +510,52 @@ bool FrameFileIO::saveVehicleSpyFile(QString filename, const QVector * return true; } +bool FrameFileIO::isCRTDFile(QString filename) +{ + QFile *inFile = new QFile(filename); + QByteArray line; + int lineCounter = 0; + bool isMatch = false; + + if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) + { + delete inFile; + return false; + } + + try + { + line = inFile->readLine().toUpper(); //read out the header first and discard it. + + while (!inFile->atEnd() && lineCounter < 100) { + lineCounter++; + line = inFile->readLine().simplified(); + if (line.length() > 2) + { + QList tokens = line.split(' '); + if (tokens.length() > 2) + { + char firstChar = tokens[1].left(1)[0]; + if (firstChar == 'R' || firstChar == 'T') + { + if (tokens[1] == "R29" || tokens[1] == "T29") isMatch = true; + if (tokens[1] == "R11" || tokens[1] == "T11") isMatch = true; + } + } + else isMatch = false; + } + if (lineCounter > 10) break; + } + } + catch (...) + { + isMatch = false; + } + inFile->close(); + delete inFile; + return isMatch; +} + //CRTD format from Mark Webb-Johnson / OVMS project /* Sample data in CRTD format @@ -389,6 +651,55 @@ bool FrameFileIO::loadCRTDFile(QString filename, QVector* frames) return !foundErrors; } + +bool FrameFileIO::isCANHackerFile(QString filename) +{ + QFile *inFile = new QFile(filename); + QByteArray line; + int lineCounter = 0; + bool isMatch = false; + + if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) + { + delete inFile; + return false; + } + try + { + line = inFile->readLine().toUpper(); //read out the header first and discard it. + if (!line.contains("CANHACKER")) return false; + + while (!inFile->atEnd()) { + lineCounter++; + line = inFile->readLine().simplified(); + if (line.length() > 2) + { + QList tokens = line.split(' '); + if (tokens.length() > 3) + { + if (tokens[1].toInt(NULL, 16) > 0) + { + int len = tokens[2].toInt(); + if (len > -1 && len < 9) + { + isMatch = true; + } + } + } + else isMatch = false; + } + if (lineCounter > 10) break; + } + } + catch (...) + { + isMatch = false; + } + inFile->close(); + delete inFile; + return isMatch; +} + // CANHacker trace format // Time ID DLC Data Comment // 00.000 00004000 8 36 47 19 43 01 00 00 80 @@ -459,6 +770,59 @@ bool FrameFileIO::loadCANHackerFile(QString filename, QVector* frames) return !foundErrors; } +bool FrameFileIO::isCANOpenFile(QString filename) +{ + QFile *inFile = new QFile(filename); + QByteArray line; + int lineCounter = 0; + bool isMatch = false; + + if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) + { + delete inFile; + return false; + } + + try + { + line = inFile->readLine().toUpper(); + if (!line.contains("CANOPEN MAGIC")) return false; + line = inFile->readLine(); + line = inFile->readLine(); + line = inFile->readLine(); + line = inFile->readLine(); + + while (!inFile->atEnd()) { + lineCounter++; + if (lineCounter > 10) + { + break; + } + + line = inFile->readLine().replace('\"', ' ').simplified(); + if (line.length() > 2) + { + QList tokens = line.split(','); + if (tokens.length() > 11) + { + if (Utility::ParseStringToNum(tokens[5].simplified()) > 0) + { + QList dataTok = tokens[11].simplified().split(' '); + if ( dataTok.length() > -1 && dataTok.length() < 9) isMatch = true; + } + } + else isMatch = false; + } + } + } + catch (...) + { + isMatch = false; + } + inFile->close(); + delete inFile; + return isMatch; +} //"Message Number","Time (ms)","Time","Excel Time","Count","ID","Flags","Message Type","Node","Details","Process Data","Data (Hex)","Data (Text)","Data (Decimal)","Length","Raw Message" //"0","0.000","8:09:42:48.7953090'",43447.7100146116,"","0x2E1","","Default: PDO","","Default: TPDO 2 of Node 0x61 (97)","","10 21 04 00 00 00 00 00 ",". ! . . . . . . ","U:0 S:0","8","10 21 04 00 00 00 00 00" @@ -576,6 +940,71 @@ bool FrameFileIO::saveCRTDFile(QString filename, const QVector* frames } +bool FrameFileIO::isPCANFile(QString filename) +{ + QFile *inFile = new QFile(filename); + QByteArray line; + int lineCounter = 0; + int fileVersion = 1; + bool isMatch = false; + + if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) + { + delete inFile; + return false; + } + + try + { + while (!inFile->atEnd()) { + lineCounter++; + if (lineCounter > 10) + { + break; + } + line = inFile->readLine(); + if (line.startsWith(";$FILEVERSION=2.0")) fileVersion = 2; + if (line.startsWith(';')) continue; + if (line.length() > 41) + { + if (fileVersion == 1) + { + int id = line.mid(28, 8).simplified().toUInt(NULL, 16); + if (id > 0 && id < 0x1FFFFFFF) + { + int len = line.mid(38,1).toInt(); + if (len > - 1 && len < 9) isMatch = true; + } + else isMatch = false; + } + + else if (fileVersion == 2) + { + int id = line.mid(25, 8).simplified().toUInt(NULL, 16); + if (id > 0 && id < 0x1FFFFFFF) + { + int len = line.mid(37,2).trimmed().toInt(); + if (len > 0 && len < 9) + { + QList tokens = line.mid(40, len * 3).split(' '); + if (tokens.length() == len) isMatch = true; + } + else isMatch = false; + } + } + } + //else foundErrors = true; + } + } + catch (...) + { + isMatch = false; + } + inFile->close(); + delete inFile; + return isMatch; +} + /*Fixed length lines Version 1.1 //; Message Number @@ -625,7 +1054,7 @@ bool FrameFileIO::loadPCANFile(QString filename, QVector* frames) line = inFile->readLine(); if (line.startsWith(";$FILEVERSION=2.0")) fileVersion = 2; if (line.startsWith(';')) continue; - if (line.length() > 2) + if (line.length() > 41) { if (fileVersion == 1) { @@ -699,6 +1128,66 @@ bool FrameFileIO::loadPCANFile(QString filename, QVector* frames) return !foundErrors; } +bool FrameFileIO::isCanalyzerASC(QString filename) +{ + QFile *inFile = new QFile(filename); + QByteArray line; + int lineCounter = 0; + bool isMatch = true; + bool inHeader = true; + QList tokens; + + if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) + { + delete inFile; + return false; + } + try + { + while (!inFile->atEnd() && lineCounter < 10) { + lineCounter++; + if (lineCounter > 10) + { + qApp->processEvents(); + lineCounter = 0; + } + line = inFile->readLine(); + if (inHeader) + { + if (line.startsWith("//") || lineCounter > 4) + { + inHeader = false; + continue; + } + } + if (inHeader) continue; + if (line.length() > 2) + { + tokens = line.simplified().split(' '); + if (tokens.length() > 5) + { + uint64_t time = (uint64_t)(tokens[0].toDouble() * (double)1000000.0); + if (time <= 0) isMatch = false; + int id = tokens[2].toUInt(NULL, 16); + if (id < 1 || id > 0x1FFFFFFF) isMatch = false; + int len = tokens[5].toUInt(); + if (len < 0 || len > 8) isMatch = false; + if (tokens.count() < (len + 6)) isMatch = false; + } + else isMatch = false; + } + else isMatch = false; + } + } + catch (...) + { + isMatch = false; + } + inFile->close(); + delete inFile; + return isMatch; +} + //There tends to be four lines of header first. The last of which starts with // so first burn off lines //until a line starting with // is seen, then the rest are formatted like this: // 47.971842 2 248 Rx d 8 FF FF FF FF FF FF FF FF @@ -748,6 +1237,7 @@ bool FrameFileIO::loadCanalyzerASC(QString filename, QVector* frames) thisFrame.timestamp = (uint64_t)(tokens[0].toDouble() * (double)1000000.0); thisFrame.ID = tokens[2].toUInt(NULL, 16); thisFrame.len = tokens[5].toUInt(); + if (thisFrame.len > 8) return false; thisFrame.isReceived = tokens[3].toUpper().contains("RX"); thisFrame.bus = tokens[1].toUInt(); thisFrame.extended = (thisFrame.ID > 0x7FF); @@ -850,6 +1340,32 @@ bool FrameFileIO::saveCanalyzerASC(QString filename, const QVector* fr return true; } +bool FrameFileIO::isCanalyzerBLF(QString filename) +{ + BLF_FILE_HEADER header; + + QFile *inFile = new QFile(filename); + bool isMatch = false; + + if (!inFile->open(QIODevice::ReadOnly)) + { + delete inFile; + return false; + } + inFile->read((char *)&header, sizeof(header)); + if (qFromLittleEndian(header.sig) == 0x47474F4C) + { + qDebug() << "Proper BLF file header token"; + isMatch = true; + } + else isMatch = false; + + inFile->close(); + delete inFile; + + return isMatch; +} + //this one is pretty complicated and handled by it's own class bool FrameFileIO::loadCanalyzerBLF(QString filename, QVector *frames) { @@ -857,6 +1373,58 @@ bool FrameFileIO::loadCanalyzerBLF(QString filename, QVector *frames) return blf.loadBLF(filename, frames); } +bool FrameFileIO::isNativeCSVFile(QString filename) +{ + QFile *inFile = new QFile(filename); + QByteArray line; + int fileVersion = 1; + bool isMatch = true; + + if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) + { + delete inFile; + return false; + } + try + { + line = inFile->readLine().toUpper(); //read out the header first and discard it. + if (line.length() < 23) isMatch = false; + else if (line.at(23) == 'D') fileVersion = 2; //Dir is found starting at position 23 if this is a V2 file + + if (!line.contains("TIME STAMP")) isMatch = false; + if (!line.contains("EXTENDED")) isMatch = false; + if (!line.contains("D1")) isMatch = false; + + if (!inFile->atEnd()) { + line = inFile->readLine().simplified(); + if (line.length() > 2) + { + QList tokens = line.split(','); + if (tokens.length() >= 5) + { + if (fileVersion == 1) + { + if (tokens[4].toUInt() > 8) isMatch = false; + } + else if (fileVersion == 2) + { + if ( tokens[5].toUInt() > 8) isMatch = false; + } + } + else isMatch = false; + } + } + } + catch (...) + { + isMatch = false; + } + inFile->close(); + delete inFile; + + return isMatch; +} + //The "native" file format for this program //Time Stamp,ID,Extended,Dir,Bus,LEN,D1,D2,D3,D4,D5,D6,D7,D8 //39747828,000005EB,false,Rx,0,8,E8,45,85,4B,4A,28,36,69, @@ -1089,6 +1657,56 @@ bool FrameFileIO::flushContinuousNative() return false; } + +bool FrameFileIO::isGenericCSVFile(QString filename) +{ + QFile *inFile = new QFile(filename); + QByteArray line; + bool isMatch = true; + + if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) + { + delete inFile; + return false; + } + try + { + line = inFile->readLine(); //read out the header first and discard it. + + if (!inFile->atEnd()) { + line = inFile->readLine(); + if (line.length() > 2) + { + QList tokens = line.split(','); + + int ID = tokens[0].toInt(NULL, 16); + if (ID < 1 || ID > 0x1FFFFFFF) isMatch = false; + + if (tokens.count() < 2) + { + isMatch = false; + } + + if (isMatch) + { + QList dataTok = tokens[1].split(' '); + int len = dataTok.length(); + if (len > 8) isMatch = false; + if (len < 2) isMatch = false; + } + } + else isMatch = false; + } + } + catch (...) + { + isMatch = false; + } + inFile->close(); + delete inFile; + return isMatch; +} + bool FrameFileIO::loadGenericCSVFile(QString filename, QVector* frames) { QFile *inFile = new QFile(filename); @@ -1182,6 +1800,58 @@ bool FrameFileIO::saveGenericCSVFile(QString filename, const QVector* return true; } +bool FrameFileIO::isLogFile(QString filename) +{ + QFile *inFile = new QFile(filename); + QByteArray line; + bool isMatch = true; + + if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) + { + delete inFile; + return false; + } + + try + { + line = inFile->readLine().toUpper(); + if (!line.contains("BUSMASTER")) isMatch = false; + + while (!inFile->atEnd() && line.startsWith("***")) { + line = inFile->readLine().toUpper(); + } + + if (!inFile->atEnd()) + { + line = inFile->readLine().toUpper(); + if (line.length() > 1) + { + QList tokens = line.split(' '); + if (tokens.length() >= 6) + { + QList timeToks = tokens[0].split(':'); + if (timeToks.count() != 4) isMatch = false; + + int ID = tokens[3].right(tokens[3].length() - 2).toInt(NULL, 16); + if (ID < 1 || ID > 0x1FFFFFFF) isMatch = false; + if (tokens[4] != "S" && tokens[4] != "X" && tokens[4] != "SR" && tokens[4] != "XR") isMatch = false; + int len = tokens[5].toUInt(); + if (len > 8) isMatch = false; + } + else isMatch = false; + } + } + } + catch (...) + { + isMatch = false; + } + + inFile->close(); + delete inFile; + return isMatch; +} + //busmaster log file /* tokens: @@ -1356,6 +2026,41 @@ bool FrameFileIO::saveLogFile(QString filename, const QVector* frames) return true; } +bool FrameFileIO::isIXXATFile(QString filename) +{ + QFile *inFile = new QFile(filename); + QByteArray line; + bool isMatch = true; + + if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) + { + delete inFile; + return false; + } + + try + { + line = inFile->readLine().toUpper(); + if (!line.contains("IXXAT")) isMatch = false; + + for (int i = 0; i < 6; i++) + { + if (!inFile->atEnd()) line = inFile->readLine().toUpper(); + else isMatch = false; + } + + if (!line.contains("FORMAT")) isMatch = false; + } + catch (...) + { + isMatch = false; + } + + inFile->close(); + delete inFile; + return isMatch; +} + //"00:01:03.03","223","Std","","00 00 00 00 49 00 00 01 " bool FrameFileIO::loadIXXATFile(QString filename, QVector* frames) { @@ -1400,6 +2105,7 @@ bool FrameFileIO::loadIXXATFile(QString filename, QVector* frames) { timeStamp = 0; foundErrors = true; + return false; } thisFrame.timestamp = timeStamp; thisFrame.ID = Utility::unQuote(tokens[1]).toInt(NULL, 16); @@ -1413,6 +2119,7 @@ bool FrameFileIO::loadIXXATFile(QString filename, QVector* frames) { thisFrame.extended = false; foundErrors = true; + return false; } thisFrame.isReceived = true; @@ -1421,10 +2128,11 @@ bool FrameFileIO::loadIXXATFile(QString filename, QVector* frames) QStringList dataToks = Utility::unQuote(tokens[4]).simplified().split(' '); thisFrame.len = dataToks.length(); + if (thisFrame.len > 8) return false; for (unsigned int d = 0; d < thisFrame.len; d++) thisFrame.data[d] = dataToks[d].toInt(NULL, 16); frames->append(thisFrame); } - else foundErrors = true; + else return false; } } inFile->close(); @@ -1485,6 +2193,54 @@ bool FrameFileIO::saveIXXATFile(QString filename, const QVector* frame return true; } +bool FrameFileIO::isCANDOFile(QString filename) +{ + QFile *inFile = new QFile(filename); + int lineCounter = 0; + QByteArray data; + bool isMatch = true; + + if (!inFile->open(QIODevice::ReadOnly)) + { + delete inFile; + return false; + } + + //this file format is in static 12 byte blocks. + //Bytes 0 - 1 are a time stamp + //Bytes 2 - 3 are the data length (top 4 bits) then ID (bottom 11 bits) + //Bytes 4 - 11 are the data bytes (padded with FF for bytes not used) + try + { + while (!inFile->atEnd() && lineCounter < 200) + { + lineCounter++; + + data = inFile->read(12); + + int ID = (((unsigned int)data[3] & 0x0F) * 256 + (unsigned int)data[2]); + int len = (unsigned char)data[3] >> 4; + + if (len <= 8 && ID <= 0x7FF) + { + if (len < 8) + { + if (data[4 + len] != (char)0xFF) isMatch = false; + } + } + else isMatch = false; + } + } + catch (...) + { + isMatch = false; + } + + inFile->close(); + delete inFile; + return isMatch; +} + bool FrameFileIO::loadCANDOFile(QString filename, QVector* frames) { QFile *inFile = new QFile(filename); @@ -1601,6 +2357,59 @@ bool FrameFileIO::saveCANDOFile(QString filename, const QVector* frame return true; } +bool FrameFileIO::isMicrochipFile(QString filename) +{ + QFile *inFile = new QFile(filename); + QByteArray line; + bool inComment = false; + int lineCounter = 0; + bool isMatch = true; + + if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) + { + delete inFile; + return false; + } + try + { + while (!inFile->atEnd() && lineCounter < 100) { + lineCounter++; + + line = inFile->readLine(); + if (line.length() > 2) + { + if (line.startsWith("//")) + { + inComment = !inComment; + } + else + { + if (!inComment) + { + QList tokens = line.split(';'); + if (tokens.length() >= 4) + { + int ID = Utility::ParseStringToNum(tokens[2]); + if (ID < 1 || ID > 0x1FFFFFFF) isMatch = false; + int len = tokens[3].toInt(); + if ( len > 8 || len < 0 ) isMatch = false; + if ( (len + 4) < tokens.length() ) isMatch = false; + } + else isMatch = false; + } + } + } + } + } + catch (...) + { + isMatch = false; + } + inFile->close(); + delete inFile; + return isMatch; +} + //log file from microchip tool /* tokens: @@ -1739,6 +2548,61 @@ bool FrameFileIO::saveMicrochipFile(QString filename, const QVector* f return true; } +bool FrameFileIO::isTraceFile(QString filename) +{ + QFile *inFile = new QFile(filename); + QByteArray line; + int lineCounter = 0; + bool isMatch = true; + + if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) + { + delete inFile; + return false; + } + + try + { + while (!inFile->atEnd() && lineCounter < 100) { + lineCounter++; + + line = inFile->readLine(); + line = line.trimmed(); + if (line.length() > 2) + { + if (line.startsWith(";")) + { + // a comment. Ignore it. + } + else + { + QList tokens = line.split('\t'); + if (tokens.length() > 3) + { + QList timestampToks = tokens[1].split(':'); + if (timestampToks.count() != 4) isMatch = false; + + int ID = tokens[2].toLong(NULL, 16); + if (ID < 1 || ID > 0x1FFFFFFF) isMatch = false; + int len = tokens[3].toInt(); + if (len > 8 || len < 0) isMatch = false; + QList dataToks = tokens[4].split(' '); + if (len > (unsigned int) dataToks.length()) isMatch = false; + } + else isMatch = false; + } + } + } + } + catch (...) + { + isMatch = false; + } + + inFile->close(); + delete inFile; + return isMatch; +} /* ; CAN Logger trace file @@ -1980,6 +2844,123 @@ bool FrameFileIO::saveCanDumpFile(QString filename, const QVector * fr return true; } +bool FrameFileIO::isCanDumpFile(QString filename) +{ + QFile *inFile = new QFile(filename); + QByteArray line; + QList tokens; + QRegExp timeExp("^\\((\\S+)\\)$"); + QRegExp IdValExp("^(\\S+)#(\\S+)$"); + QRegExp valExp("(\\S{2})"); + int lineCounter = 0; + int pos = 0; + bool isMatch = true; + bool ret; + + if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) + { + delete inFile; + return false; + } + + try + { + while (!inFile->atEnd() && lineCounter < 100) { + lineCounter++; + + line = inFile->readLine().toUpper(); + if (line.length() > 1) + { + /* tokenize */ + tokens.clear(); + tokens = line.simplified().split(' '); + if(tokens.count() < 3) isMatch = false; + + /* timestamp */ + ret = timeExp.exactMatch(tokens[0]); + if(!ret) isMatch = false; + + uint64_t timestamp = (uint64_t)(timeExp.cap(1).toDouble(&ret) * (double)1000000.0); + if(!ret) isMatch = false; + + if (line.contains('[')) //the expanded format + { + //(1551774790.942758) can1 7A8 [8] F4 DC D1 83 0E 02 00 00 + // 0 1 2 3 4 5 6 7 8 9 10 11 + if (tokens.count() < 3) + { + isMatch = false; + continue; + } + int ID = tokens[2].toULong(nullptr, 16); + if (ID > 0x1FFFFFFF || ID == 0) isMatch = false; + int len = tokens[3].at(1) - '0'; + if (len < 0 || len > 8) isMatch = false; + } + else //the more concise format + { + /* ID & value */ + //qDebug() << tokens[2]; + if (tokens.count() < 3) + { + isMatch = false; + continue; + } + ret = IdValExp.exactMatch(tokens[2]); + if(!ret) + { + isMatch = false; + continue; + } + + /* ID */ + int ID = IdValExp.cap(1).toInt(&ret, 16); + + QString val= IdValExp.cap(2); + + pos = 0; + int len = 0; + if (val.startsWith("R") && val.at(1).isDigit()) { + len = val.at(1).toLatin1() - '0'; + if (len < 0 || len > 8) + { + isMatch = false; + continue; + } + } else { + /* val byte per byte */ + int lng = 0; + while ((pos = valExp.indexIn(val, pos)) != -1) + { + lng++; + if (lng > 8) + { + isMatch = false; + break; + } + int data = valExp.cap(1).toInt(&ret, 16); + if(!ret) + { + isMatch = false; + break; + } + + pos += valExp.matchedLength(); + } + } + } + } + } + } + catch (...) + { + isMatch = false; + } + inFile->close(); + delete inFile; + return isMatch; +} + /* (0.003800) vcan0 164#0000c01aa8000013 or @@ -2092,6 +3073,51 @@ bool FrameFileIO::loadCanDumpFile(QString filename, QVector* frames) return true; } + +bool FrameFileIO::isKvaserFile(QString filename) +{ + QFile *inFile = new QFile(filename); + QByteArray line; + int lineCounter = 0; + bool isMatch = true; + + if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) + { + delete inFile; + return false; + } + try + { + line = inFile->readLine().simplified().toUpper(); + if (!line.contains("CHN")) isMatch = false; + if (!line.contains("FLG")) isMatch = false; + if (!line.contains("D0")) isMatch = false; + + if (inFile->atEnd()) isMatch = false; + + while (!inFile->atEnd() && lineCounter < 10) { + lineCounter++; + + line = inFile->readLine().toUpper(); + + if (line.length() > 70) { + //Chn Identifier Flg DLC D0...1...2...3...4...5...6..D7 Time Dir + // 0 000000AD 8 FF FF 00 00 00 00 00 00 154.266550 R + int len = line.mid(21, 3).simplified().toInt(); + if (len > 8 || len < 0) isMatch = false; + } + else isMatch = false; + } + } + catch (...) + { + isMatch = false; + } + inFile->close(); + delete inFile; + return isMatch; +} + //Chn Identifier Flg DLC D0...1...2...3...4...5...6..D7 Time Dir // 0 000000AD 8 FF FF 00 00 00 00 00 00 154.266550 R bool FrameFileIO::loadKvaserFile(QString filename, QVector *frames, bool useHex) @@ -2153,6 +3179,50 @@ bool FrameFileIO::loadKvaserFile(QString filename, QVector *frames, bo return !foundErrors; } +bool FrameFileIO::isCabanaFile(QString filename) +{ + QFile *inFile = new QFile(filename); + QByteArray line; + int lineCounter = 0; + bool isMatch = true; + + if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) + { + delete inFile; + return false; + } + try + { + line = inFile->readLine().toUpper(); //read out the header first and discard it. + if (!line.contains("TIME")) isMatch = false; + if (!line.contains("ADDR")) isMatch = false; + + while (!inFile->atEnd() || lineCounter < 100) { + lineCounter++; + + line = inFile->readLine().simplified(); + if (line.length() > 2) + { + QList tokens = line.split(','); + if (tokens.length() >= 3 && tokens.length() < 5) + { + int ID = tokens[1].toInt(); + if (ID < 1 || ID > 0x1FFFFFFF) isMatch = false; + } + else isMatch = false; + } + } + } + catch (...) + { + isMatch = false; + } + + inFile->close(); + delete inFile; + return isMatch; +} + //Cabana uses a CSV file with four columns //time,addr,bus,data //time is in seconds, with quite a bit of resolution after the decimal point. Does not start at 0, probably time-since-boot? @@ -2264,7 +3334,6 @@ bool FrameFileIO::saveCabanaFile(QString filename, const QVector* fram } outFile->write("time,addr,bus,data"); - //outFile->write("Time Stamp,ID,Extended,Dir,Bus,LEN,D1,D2,D3,D4,D5,D6,D7,D8"); outFile->write("\n"); for (int c = 0; c < frames->count(); c++) diff --git a/framefileio.h b/framefileio.h index 820426c..8839e76 100644 --- a/framefileio.h +++ b/framefileio.h @@ -30,6 +30,7 @@ public: static bool saveFrameFile(QString &, const QVector*); //These do the actual loading and saving and can be used directly if you'd prefer + static bool autoDetectLoadFile(QString, QVector*); static bool loadCRTDFile(QString, QVector*); static bool loadNativeCSVFile(QString, QVector*); static bool loadGenericCSVFile(QString, QVector*); @@ -47,6 +48,27 @@ public: static bool loadCANHackerFile(QString filename, QVector* frames); static bool loadCabanaFile(QString filename, QVector* frames); static bool loadCANOpenFile(QString filename, QVector* frames); + + //functions that pre-scan a file to try to figure out if they could read it. Used to automatically determine + //file type and load it. + static bool isCRTDFile(QString); + static bool isNativeCSVFile(QString); + static bool isGenericCSVFile(QString); + static bool isLogFile(QString); + static bool isMicrochipFile(QString); + static bool isTraceFile(QString); + static bool isIXXATFile(QString); + static bool isCANDOFile(QString); + static bool isVehicleSpyFile(QString); + static bool isCanDumpFile(QString); + static bool isPCANFile(QString); + static bool isKvaserFile(QString); + static bool isCanalyzerASC(QString); + static bool isCanalyzerBLF(QString); + static bool isCANHackerFile(QString filename); + static bool isCabanaFile(QString filename); + static bool isCANOpenFile(QString filename); + static bool saveCRTDFile(QString, const QVector*); static bool saveNativeCSVFile(QString, const QVector*); static bool saveGenericCSVFile(QString, const QVector*); @@ -59,6 +81,7 @@ public: static bool saveCanDumpFile(QString filename, const QVector * frames); static bool saveCabanaFile(QString filename, const QVector* frames); static bool saveCanalyzerASC(QString filename, const QVector* frames); + static bool openContinuousNative(); static bool closeContinuousNative(); static bool writeContinuousNative(const QVector*, int); diff --git a/mainwindow.cpp b/mainwindow.cpp index 1d6f896..15181e1 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -200,7 +200,6 @@ MainWindow::~MainWindow() delete model; delete elapsedTime; delete dbcHandler; - } //kill every sub window that could be open. At the moment a hard coded list @@ -249,6 +248,7 @@ void MainWindow::killWindow(QDialog *win) void MainWindow::exitApp() { this->close(); + QApplication::quit(); //forces the whole application to terminate when the main window is closed } void MainWindow::closeEvent(QCloseEvent *event) @@ -406,10 +406,14 @@ void MainWindow::overwriteToggled(bool state) QMessageBox::Yes|QMessageBox::No); if (confirmDialog == QMessageBox::Yes) { - model->setOverwriteMode(state); + model->setOverwriteMode(true); } else ui->cbOverwrite->setCheckState(Qt::Unchecked); } + else + { + model->setOverwriteMode(false); + } } void MainWindow::updateFilterList() diff --git a/qcustomplot.cpp b/qcustomplot.cpp index 154576a..51722e2 100644 --- a/qcustomplot.cpp +++ b/qcustomplot.cpp @@ -1,7 +1,7 @@ /*************************************************************************** ** ** ** QCustomPlot, an easy to use, modern plotting widget for Qt ** -** Copyright (C) 2011-2017 Emanuel Eichhammer ** +** Copyright (C) 2011-2018 Emanuel Eichhammer ** ** ** ** 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 ** @@ -19,15 +19,15 @@ **************************************************************************** ** Author: Emanuel Eichhammer ** ** Website/Contact: http://www.qcustomplot.com/ ** -** Date: 04.09.17 ** -** Version: 2.0.0 ** +** Date: 25.06.18 ** +** Version: 2.0.1 ** ****************************************************************************/ #include "qcustomplot.h" /* including file 'src/vector2d.cpp', size 7340 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPVector2D @@ -260,7 +260,7 @@ QCPVector2D &QCPVector2D::operator-=(const QCPVector2D &vector) /* including file 'src/painter.cpp', size 8670 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPPainter @@ -478,7 +478,7 @@ void QCPPainter::makeNonCosmetic() /* including file 'src/paintbuffer.cpp', size 18502 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPAbstractPaintBuffer @@ -950,8 +950,8 @@ void QCPPaintBufferGlFbo::reallocateBuffer() /* end of 'src/paintbuffer.cpp' */ -/* including file 'src/layer.cpp', size 37064 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* including file 'src/layer.cpp', size 37304 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPLayer @@ -1464,9 +1464,13 @@ bool QCPLayerable::realVisibility() const placed in \a details. So in the subsequent \ref selectEvent, the decision which part was selected doesn't have to be done a second time for a single selection operation. + In the case of 1D Plottables (\ref QCPAbstractPlottable1D, like \ref QCPGraph or \ref QCPBars) \a + details will be set to a \ref QCPDataSelection, describing the closest data point to \a pos. + You may pass 0 as \a details to indicate that you are not interested in those selection details. - \see selectEvent, deselectEvent, mousePressEvent, wheelEvent, QCustomPlot::setInteractions + \see selectEvent, deselectEvent, mousePressEvent, wheelEvent, QCustomPlot::setInteractions, + QCPAbstractPlottable1D::selectTestRect */ double QCPLayerable::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const { @@ -1804,7 +1808,7 @@ void QCPLayerable::wheelEvent(QWheelEvent *event) /* including file 'src/axis/range.cpp', size 12221 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPRange @@ -2125,8 +2129,8 @@ bool QCPRange::validRange(const QCPRange &range) /* end of 'src/axis/range.cpp' */ -/* including file 'src/selection.cpp', size 21906 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* including file 'src/selection.cpp', size 21941 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPDataRange @@ -2136,8 +2140,9 @@ bool QCPRange::validRange(const QCPRange &range) \brief Describes a data range given by begin and end index QCPDataRange holds two integers describing the begin (\ref setBegin) and end (\ref setEnd) index - of a contiguous set of data points. The end index points to the data point above the last data point that's part of - the data range, similarly to the nomenclature used in standard iterators. + of a contiguous set of data points. The end index points to the data point just after the last + data point that's part of the data range, similarly to the nomenclature used in standard + iterators. Data Ranges are not bound to a certain plottable, thus they can be freely exchanged, created and modified. If a non-contiguous data set shall be described, the class \ref QCPDataSelection is @@ -2184,7 +2189,7 @@ bool QCPRange::validRange(const QCPRange &range) /*! \fn void QCPDataRange::setEnd(int end) - Sets the end of this data range. The \a end index points to the data point just above the last + Sets the end of this data range. The \a end index points to the data point just after the last data point that is part of the data range. No checks or corrections are made to ensure the resulting range is valid (\ref isValid). @@ -2300,7 +2305,7 @@ bool QCPDataRange::intersects(const QCPDataRange &other) const } /*! - Returns whether all data points described by this data range are also in \a other. + Returns whether all data points of \a other are also contained inside this data range. \see intersects */ @@ -2631,7 +2636,8 @@ void QCPDataSelection::enforceType(QCP::SelectionType type) } case QCP::stDataRange: { - mDataRanges = QList() << span(); + if (!isEmpty()) + mDataRanges = QList() << span(); break; } case QCP::stMultipleDataRanges: @@ -2726,7 +2732,7 @@ QCPDataSelection QCPDataSelection::inverse(const QCPDataRange &outerRange) const /* including file 'src/selectionrect.cpp', size 9224 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPSelectionRect @@ -2954,8 +2960,8 @@ void QCPSelectionRect::draw(QCPPainter *painter) /* end of 'src/selectionrect.cpp' */ -/* including file 'src/layout.cpp', size 79064 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* including file 'src/layout.cpp', size 79139 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPMarginGroup @@ -4074,7 +4080,7 @@ QCPLayoutGrid::QCPLayoutGrid() : mColumnSpacing(5), mRowSpacing(5), mWrap(0), - mFillOrder(foRowsFirst) + mFillOrder(foColumnsFirst) { } @@ -4350,7 +4356,8 @@ void QCPLayoutGrid::setWrap(int count) The specified \a order defines whether rows or columns are filled first. Using \ref setWrap, you can control at which row/column count wrapping into the next column/row will occur. If you set it to zero, no wrapping will ever occur. Changing the fill order also changes the meaning of the - linear index used e.g. in \ref elementAt and \ref takeAt. + linear index used e.g. in \ref elementAt and \ref takeAt. The default fill order for \ref + QCPLayoutGrid is \ref foColumnsFirst. If you want to have all current elements arranged in the new order, set \a rearrange to true. The elements will be rearranged in a way that tries to preserve their linear index. However, empty @@ -5125,7 +5132,7 @@ void QCPLayoutInset::addElement(QCPLayoutElement *element, const QRectF &rect) /* including file 'src/lineending.cpp', size 11536 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPLineEnding @@ -5424,7 +5431,7 @@ void QCPLineEnding::draw(QCPPainter *painter, const QCPVector2D &pos, double ang /* including file 'src/axis/axisticker.cpp', size 18664 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPAxisTicker @@ -5844,7 +5851,7 @@ double QCPAxisTicker::cleanMantissa(double input) const /* including file 'src/axis/axistickerdatetime.cpp', size 14443 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPAxisTickerDateTime @@ -6141,7 +6148,7 @@ double QCPAxisTickerDateTime::dateTimeToKey(const QDate date) /* including file 'src/axis/axistickertime.cpp', size 11747 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPAxisTickerTime @@ -6390,7 +6397,7 @@ void QCPAxisTickerTime::replaceUnit(QString &text, QCPAxisTickerTime::TimeUnit u /* including file 'src/axis/axistickerfixed.cpp', size 5583 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPAxisTickerFixed @@ -6491,8 +6498,8 @@ double QCPAxisTickerFixed::getTickStep(const QCPRange &range) /* end of 'src/axis/axistickerfixed.cpp' */ -/* including file 'src/axis/axistickertext.cpp', size 8653 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* including file 'src/axis/axistickertext.cpp', size 8661 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPAxisTickerText @@ -6562,7 +6569,7 @@ void QCPAxisTickerText::setTicks(const QMap &ticks) \see addTicks, addTick, clear */ -void QCPAxisTickerText::setTicks(const QVector &positions, const QVector labels) +void QCPAxisTickerText::setTicks(const QVector &positions, const QVector &labels) { clear(); addTicks(positions, labels); @@ -6600,7 +6607,7 @@ void QCPAxisTickerText::clear() \see addTicks, setTicks, clear */ -void QCPAxisTickerText::addTick(double position, QString label) +void QCPAxisTickerText::addTick(double position, const QString &label) { mTicks.insert(position, label); } @@ -6705,7 +6712,7 @@ QVector QCPAxisTickerText::createTickVector(double tickStep, const QCPRa /* including file 'src/axis/axistickerpi.cpp', size 11170 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPAxisTickerPi @@ -6992,7 +6999,7 @@ QString QCPAxisTickerPi::unicodeSubscript(int number) const /* including file 'src/axis/axistickerlog.cpp', size 7106 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPAxisTickerLog @@ -7132,8 +7139,8 @@ QVector QCPAxisTickerLog::createTickVector(double tickStep, const QCPRan /* end of 'src/axis/axistickerlog.cpp' */ -/* including file 'src/axis/axis.cpp', size 99397 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* including file 'src/axis/axis.cpp', size 99515 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -7653,10 +7660,14 @@ QCPLineEnding QCPAxis::upperEnding() const /*! Sets whether the axis uses a linear scale or a logarithmic scale. - Note that this method controls the coordinate transformation. You will likely also want to use a - logarithmic tick spacing and labeling, which can be achieved by setting an instance of \ref - QCPAxisTickerLog via \ref setTicker. See the documentation of \ref QCPAxisTickerLog about the - details of logarithmic axis tick creation. + Note that this method controls the coordinate transformation. For logarithmic scales, you will + likely also want to use a logarithmic tick spacing and labeling, which can be achieved by setting + the axis ticker to an instance of \ref QCPAxisTickerLog : + + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpaxisticker-log-creation + + See the documentation of \ref QCPAxisTickerLog about the details of logarithmic axis tick + creation. \ref setNumberPrecision */ @@ -9819,7 +9830,7 @@ void QCPAxisPainterPrivate::getMaxTickLabelSize(const QFont &font, const QString /* including file 'src/scatterstyle.cpp', size 17450 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPScatterStyle @@ -10293,7 +10304,7 @@ void QCPScatterStyle::drawShape(QCPPainter *painter, double x, double y) const //amalgamation: add datacontainer.cpp /* including file 'src/plottable.cpp', size 38845 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPSelectionDecorator @@ -11265,8 +11276,8 @@ void QCPAbstractPlottable::deselectEvent(bool *selectionStateChanged) /* end of 'src/plottable.cpp' */ -/* including file 'src/item.cpp', size 49269 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* including file 'src/item.cpp', size 49271 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPItemAnchor @@ -12411,7 +12422,7 @@ double QCPAbstractItem::rectDistance(const QRectF &rect, const QPointF &pos, boo QList lines; lines << QLineF(rect.topLeft(), rect.topRight()) << QLineF(rect.bottomLeft(), rect.bottomRight()) << QLineF(rect.topLeft(), rect.bottomLeft()) << QLineF(rect.topRight(), rect.bottomRight()); - double minDistSqr = std::numeric_limits::max(); + double minDistSqr = (std::numeric_limits::max)(); for (int i=0; i QCustomPlot::axisRects() const { @@ -15718,8 +15745,8 @@ void QCustomPlot::toPainter(QCPPainter *painter, int width, int height) //amalgamation: add plottable1d.cpp -/* including file 'src/colorgradient.cpp', size 24646 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* including file 'src/colorgradient.cpp', size 25342 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -16282,10 +16309,22 @@ void QCPColorGradient::updateColorBuffer() QMap::const_iterator it = mColorStops.lowerBound(position); if (it == mColorStops.constEnd()) // position is on or after last stop, use color of last stop { - mColorBuffer[i] = (it-1).value().rgba(); + if (useAlpha) + { + const QColor col = (it-1).value(); + const float alphaPremultiplier = col.alpha()/255.0f; // since we use QImage::Format_ARGB32_Premultiplied + mColorBuffer[i] = qRgba(col.red()*alphaPremultiplier, col.green()*alphaPremultiplier, col.blue()*alphaPremultiplier, col.alpha()); + } else + mColorBuffer[i] = (it-1).value().rgba(); } else if (it == mColorStops.constBegin()) // position is on or before first stop, use color of first stop { - mColorBuffer[i] = it.value().rgba(); + if (useAlpha) + { + const QColor col = it.value(); + const float alphaPremultiplier = col.alpha()/255.0f; // since we use QImage::Format_ARGB32_Premultiplied + mColorBuffer[i] = qRgba(col.red()*alphaPremultiplier, col.green()*alphaPremultiplier, col.blue()*alphaPremultiplier, col.alpha()); + } else + mColorBuffer[i] = it.value().rgba(); } else // position is in between stops (or on an intermediate stop), interpolate color { QMap::const_iterator high = it; @@ -16359,7 +16398,7 @@ void QCPColorGradient::updateColorBuffer() /* including file 'src/selectiondecorator-bracket.cpp', size 12313 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPSelectionDecoratorBracket @@ -16645,7 +16684,7 @@ QPointF QCPSelectionDecoratorBracket::getPixelCoordinates(const QCPPlottableInte /* including file 'src/layoutelements/layoutelement-axisrect.cpp', size 47584 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -17919,8 +17958,8 @@ void QCPAxisRect::wheelEvent(QWheelEvent *event) /* end of 'src/layoutelements/layoutelement-axisrect.cpp' */ -/* including file 'src/layoutelements/layoutelement-legend.cpp', size 31097 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* including file 'src/layoutelements/layoutelement-legend.cpp', size 31153 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPAbstractLegendItem @@ -18259,7 +18298,7 @@ QSize QCPPlottableLegendItem::minimumOuterSizeHint() const Use the methods \ref setFillOrder and \ref setWrap inherited from \ref QCPLayoutGrid to control in which order (column first or row first) the legend is filled up when calling \ref addItem, and - at which column or row wrapping occurs. + at which column or row wrapping occurs. The default fill order for legends is \ref foRowsFirst. By default, every QCustomPlot has one legend (\ref QCustomPlot::legend) which is placed in the inset layout of the main axis rect (\ref QCPAxisRect::insetLayout). To move the legend to another @@ -18836,7 +18875,7 @@ void QCPLegend::parentPlotInitialized(QCustomPlot *parentPlot) /* including file 'src/layoutelements/layoutelement-textelement.cpp', size 12761 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPTextElement @@ -19239,8 +19278,8 @@ QColor QCPTextElement::mainTextColor() const /* end of 'src/layoutelements/layoutelement-textelement.cpp' */ -/* including file 'src/layoutelements/layoutelement-colorscale.cpp', size 25770 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* including file 'src/layoutelements/layoutelement-colorscale.cpp', size 26246 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -19459,13 +19498,22 @@ void QCPColorScale::setDataRange(const QCPRange &dataRange) } /*! - Sets the scale type of the color scale, i.e. whether values are linearly associated with colors + Sets the scale type of the color scale, i.e. whether values are associated with colors linearly or logarithmically. It is equivalent to calling QCPColorMap::setDataScaleType on any of the connected color maps. It is also equivalent to directly accessing the \ref axis and setting its scale type with \ref QCPAxis::setScaleType. + Note that this method controls the coordinate transformation. For logarithmic scales, you will + likely also want to use a logarithmic tick spacing and labeling, which can be achieved by setting + the color scale's \ref axis ticker to an instance of \ref QCPAxisTickerLog : + + \snippet documentation/doc-code-snippets/mainwindow.cpp qcpaxisticker-log-colorscale + + See the documentation of \ref QCPAxisTickerLog about the details of logarithmic axis tick + creation. + \see setDataRange, setGradient */ void QCPColorScale::setDataScaleType(QCPAxis::ScaleType scaleType) @@ -19899,8 +19947,8 @@ void QCPColorScaleAxisRectPrivate::axisSelectableChanged(QCPAxis::SelectablePart /* end of 'src/layoutelements/layoutelement-colorscale.cpp' */ -/* including file 'src/plottables/plottable-graph.cpp', size 73960 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* including file 'src/plottables/plottable-graph.cpp', size 74194 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPGraphData @@ -20259,7 +20307,14 @@ void QCPGraph::addData(double key, double value) mDataContainer->add(QCPGraphData(key, value)); } -/* inherits documentation from base class */ +/*! + Implements a selectTest specific to this plottable's point geometry. + + If \a details is not 0, it will be set to a \ref QCPDataSelection, describing the closest data + point to \a pos. + + \seebaseclassmethod \ref QCPAbstractPlottable::selectTest +*/ double QCPGraph::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const { if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty()) @@ -20857,11 +20912,11 @@ void QCPGraph::getOptimizedLineData(QVector *lineData, const QCPGr if (begin == end) return; int dataCount = end-begin; - int maxCount = std::numeric_limits::max(); + int maxCount = (std::numeric_limits::max)(); if (mAdaptiveSampling) { double keyPixelSpan = qAbs(keyAxis->coordToPixel(begin->key)-keyAxis->coordToPixel((end-1)->key)); - if (2*keyPixelSpan+2 < (double)std::numeric_limits::max()) + if (2*keyPixelSpan+2 < static_cast((std::numeric_limits::max)())) maxCount = 2*keyPixelSpan+2; } @@ -20958,7 +21013,7 @@ void QCPGraph::getOptimizedScatterData(QVector *scatterData, QCPGr } if (begin == end) return; int dataCount = end-begin; - int maxCount = std::numeric_limits::max(); + int maxCount = (std::numeric_limits::max)(); if (mAdaptiveSampling) { int keyPixelSpan = qAbs(keyAxis->coordToPixel(begin->key)-keyAxis->coordToPixel((end-1)->key)); @@ -21583,7 +21638,7 @@ double QCPGraph::pointDistance(const QPointF &pixelPoint, QCPGraphDataContainer: return -1.0; // calculate minimum distances to graph data points and find closestData iterator: - double minDistSqr = std::numeric_limits::max(); + double minDistSqr = (std::numeric_limits::max)(); // determine which key range comes into question, taking selection tolerance around pos into account: double posKeyMin, posKeyMax, dummy; pixelsToCoords(pixelPoint-QPointF(mParentPlot->selectionTolerance(), mParentPlot->selectionTolerance()), posKeyMin, dummy); @@ -21647,8 +21702,8 @@ int QCPGraph::findIndexBelowY(const QVector *data, double y) const /* end of 'src/plottables/plottable-graph.cpp' */ -/* including file 'src/plottables/plottable-curve.cpp', size 63527 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* including file 'src/plottables/plottable-curve.cpp', size 63742 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPCurveData @@ -22014,7 +22069,14 @@ void QCPCurve::addData(double key, double value) mDataContainer->add(QCPCurveData(0.0, key, value)); } -/* inherits documentation from base class */ +/*! + Implements a selectTest specific to this plottable's point geometry. + + If \a details is not 0, it will be set to a \ref QCPDataSelection, describing the closest data + point to \a pos. + + \seebaseclassmethod \ref QCPAbstractPlottable::selectTest +*/ double QCPCurve::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const { if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty()) @@ -23064,7 +23126,7 @@ double QCPCurve::pointDistance(const QPointF &pixelPoint, QCPCurveDataContainer: } // calculate minimum distances to curve data points and find closestData iterator: - double minDistSqr = std::numeric_limits::max(); + double minDistSqr = (std::numeric_limits::max)(); // iterate over found data points and then choose the one with the shortest distance to pos: QCPCurveDataContainer::const_iterator begin = mDataContainer->constBegin(); QCPCurveDataContainer::const_iterator end = mDataContainer->constEnd(); @@ -23096,8 +23158,8 @@ double QCPCurve::pointDistance(const QPointF &pixelPoint, QCPCurveDataContainer: /* end of 'src/plottables/plottable-curve.cpp' */ -/* including file 'src/plottables/plottable-bars.cpp', size 43512 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* including file 'src/plottables/plottable-bars.cpp', size 43725 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -23844,7 +23906,14 @@ QCPDataSelection QCPBars::selectTestRect(const QRectF &rect, bool onlySelectable return result; } -/* inherits documentation from base class */ +/*! + Implements a selectTest specific to this plottable's point geometry. + + If \a details is not 0, it will be set to a \ref QCPDataSelection, describing the closest data + point to \a pos. + + \seebaseclassmethod \ref QCPAbstractPlottable::selectTest +*/ double QCPBars::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const { Q_UNUSED(details) @@ -24264,8 +24333,8 @@ void QCPBars::connectBars(QCPBars *lower, QCPBars *upper) /* end of 'src/plottables/plottable-bars.cpp' */ -/* including file 'src/plottables/plottable-statisticalbox.cpp', size 28622 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* including file 'src/plottables/plottable-statisticalbox.cpp', size 28837 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPStatisticalBoxData @@ -24669,7 +24738,14 @@ QCPDataSelection QCPStatisticalBox::selectTestRect(const QRectF &rect, bool only return result; } -/* inherits documentation from base class */ +/*! + Implements a selectTest specific to this plottable's point geometry. + + If \a details is not 0, it will be set to a \ref QCPDataSelection, describing the closest data + point to \a pos. + + \seebaseclassmethod \ref QCPAbstractPlottable::selectTest +*/ double QCPStatisticalBox::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const { Q_UNUSED(details) @@ -24684,7 +24760,7 @@ double QCPStatisticalBox::selectTest(const QPointF &pos, bool onlySelectable, QV QCPStatisticalBoxDataContainer::const_iterator visibleBegin, visibleEnd; QCPStatisticalBoxDataContainer::const_iterator closestDataPoint = mDataContainer->constEnd(); getVisibleDataBounds(visibleBegin, visibleEnd); - double minDistSqr = std::numeric_limits::max(); + double minDistSqr = (std::numeric_limits::max)(); for (QCPStatisticalBoxDataContainer::const_iterator it=visibleBegin; it!=visibleEnd; ++it) { if (getQuartileBox(it).contains(pos)) // quartile box @@ -24919,7 +24995,7 @@ QVector QCPStatisticalBox::getWhiskerBarLines(QCPStatisticalBoxDataConta /* including file 'src/plottables/plottable-colormap.cpp', size 47881 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPColorMapData @@ -26053,8 +26129,8 @@ void QCPColorMap::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const /* end of 'src/plottables/plottable-colormap.cpp' */ -/* including file 'src/plottables/plottable-financial.cpp', size 42610 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* including file 'src/plottables/plottable-financial.cpp', size 42827 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPFinancialData @@ -26459,7 +26535,14 @@ QCPDataSelection QCPFinancial::selectTestRect(const QRectF &rect, bool onlySelec return result; } -/* inherits documentation from base class */ +/*! + Implements a selectTest specific to this plottable's point geometry. + + If \a details is not 0, it will be set to a \ref QCPDataSelection, describing the closest data + point to \a pos. + + \seebaseclassmethod \ref QCPAbstractPlottable::selectTest +*/ double QCPFinancial::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const { Q_UNUSED(details) @@ -26852,7 +26935,7 @@ double QCPFinancial::ohlcSelectTest(const QPointF &pos, const QCPFinancialDataCo QCPAxis *valueAxis = mValueAxis.data(); if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; } - double minDistSqr = std::numeric_limits::max(); + double minDistSqr = (std::numeric_limits::max)(); if (keyAxis->orientation() == Qt::Horizontal) { for (QCPFinancialDataContainer::const_iterator it=begin; it!=end; ++it) @@ -26899,7 +26982,7 @@ double QCPFinancial::candlestickSelectTest(const QPointF &pos, const QCPFinancia QCPAxis *valueAxis = mValueAxis.data(); if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; } - double minDistSqr = std::numeric_limits::max(); + double minDistSqr = (std::numeric_limits::max)(); if (keyAxis->orientation() == Qt::Horizontal) { for (QCPFinancialDataContainer::const_iterator it=begin; it!=end; ++it) @@ -27008,8 +27091,8 @@ QRectF QCPFinancial::selectionHitBox(QCPFinancialDataContainer::const_iterator i /* end of 'src/plottables/plottable-financial.cpp' */ -/* including file 'src/plottables/plottable-errorbar.cpp', size 37355 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* including file 'src/plottables/plottable-errorbar.cpp', size 37570 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPErrorBarsData @@ -27451,7 +27534,14 @@ int QCPErrorBars::findEnd(double sortKey, bool expandedRange) const return 0; } -/* inherits documentation from base class */ +/*! + Implements a selectTest specific to this plottable's point geometry. + + If \a details is not 0, it will be set to a \ref QCPDataSelection, describing the closest data + point to \a pos. + + \seebaseclassmethod \ref QCPAbstractPlottable::selectTest +*/ double QCPErrorBars::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const { if (!mDataPlottable) return -1; @@ -27870,7 +27960,7 @@ double QCPErrorBars::pointDistance(const QPointF &pixelPoint, QCPErrorBarsDataCo getVisibleDataBounds(begin, end, QCPDataRange(0, dataCount())); // calculate minimum distances to error backbones (whiskers are ignored for speed) and find closestData iterator: - double minDistSqr = std::numeric_limits::max(); + double minDistSqr = (std::numeric_limits::max)(); QVector backbones, whiskers; for (QCPErrorBarsDataContainer::const_iterator it=begin; it!=end; ++it) { @@ -27970,7 +28060,7 @@ bool QCPErrorBars::rectIntersectsLine(const QRectF &pixelRect, const QLineF &lin /* including file 'src/items/item-straightline.cpp', size 7592 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPItemStraightLine @@ -28151,7 +28241,7 @@ QPen QCPItemStraightLine::mainPen() const /* including file 'src/items/item-line.cpp', size 8498 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPItemLine @@ -28381,8 +28471,8 @@ QPen QCPItemLine::mainPen() const /* end of 'src/items/item-line.cpp' */ -/* including file 'src/items/item-curve.cpp', size 7159 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* including file 'src/items/item-curve.cpp', size 7248 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPItemCurve @@ -28492,9 +28582,12 @@ double QCPItemCurve::selectTest(const QPointF &pos, bool onlySelectable, QVarian QPainterPath cubicPath(startVec); cubicPath.cubicTo(startDirVec, endDirVec, endVec); - QPolygonF polygon = cubicPath.toSubpathPolygons().first(); + QList polygons = cubicPath.toSubpathPolygons(); + if (polygons.isEmpty()) + return -1; + const QPolygonF polygon = polygons.first(); QCPVector2D p(pos); - double minDistSqr = std::numeric_limits::max(); + double minDistSqr = (std::numeric_limits::max)(); for (int i=1; i &candidates) const; double getMantissa(double input, double *magnitude=0) const; double cleanMantissa(double input) const; + +private: + Q_DISABLE_COPY(QCPAxisTicker) + }; Q_DECLARE_METATYPE(QCPAxisTicker::TickStepStrategy) Q_DECLARE_METATYPE(QSharedPointer) @@ -1589,7 +1598,7 @@ Q_DECLARE_METATYPE(QSharedPointer) /* including file 'src/axis/axistickerdatetime.h', size 3289 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPAxisTickerDateTime : public QCPAxisTicker { @@ -1630,7 +1639,7 @@ protected: /* including file 'src/axis/axistickertime.h', size 3542 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPAxisTickerTime : public QCPAxisTicker { @@ -1682,7 +1691,7 @@ Q_DECLARE_METATYPE(QCPAxisTickerTime::TimeUnit) /* including file 'src/axis/axistickerfixed.h', size 3308 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPAxisTickerFixed : public QCPAxisTicker { @@ -1723,8 +1732,8 @@ Q_DECLARE_METATYPE(QCPAxisTickerFixed::ScaleStrategy) /* end of 'src/axis/axistickerfixed.h' */ -/* including file 'src/axis/axistickertext.h', size 3085 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* including file 'src/axis/axistickertext.h', size 3090 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPAxisTickerText : public QCPAxisTicker { @@ -1737,12 +1746,12 @@ public: // setters: void setTicks(const QMap &ticks); - void setTicks(const QVector &positions, const QVector labels); + void setTicks(const QVector &positions, const QVector &labels); void setSubTickCount(int subTicks); // non-virtual methods: void clear(); - void addTick(double position, QString label); + void addTick(double position, const QString &label); void addTicks(const QMap &ticks); void addTicks(const QVector &positions, const QVector &labels); @@ -1756,14 +1765,13 @@ protected: virtual int getSubTickCount(double tickStep) Q_DECL_OVERRIDE; virtual QString getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision) Q_DECL_OVERRIDE; virtual QVector createTickVector(double tickStep, const QCPRange &range) Q_DECL_OVERRIDE; - }; /* end of 'src/axis/axistickertext.h' */ /* including file 'src/axis/axistickerpi.h', size 3911 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPAxisTickerPi : public QCPAxisTicker { @@ -1822,7 +1830,7 @@ Q_DECLARE_METATYPE(QCPAxisTickerPi::FractionStyle) /* including file 'src/axis/axistickerlog.h', size 2663 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPAxisTickerLog : public QCPAxisTicker { @@ -1854,8 +1862,8 @@ protected: /* end of 'src/axis/axistickerlog.h' */ -/* including file 'src/axis/axis.h', size 20634 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* including file 'src/axis/axis.h', size 20698 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPGrid :public QCPLayerable { @@ -2183,10 +2191,10 @@ protected: virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged) Q_DECL_OVERRIDE; virtual void deselectEvent(bool *selectionStateChanged) Q_DECL_OVERRIDE; // mouse events: - virtual void mousePressEvent(QMouseEvent *event, const QVariant &details); - virtual void mouseMoveEvent(QMouseEvent *event, const QPointF &startPos); - virtual void mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos); - virtual void wheelEvent(QWheelEvent *event); + virtual void mousePressEvent(QMouseEvent *event, const QVariant &details) Q_DECL_OVERRIDE; + virtual void mouseMoveEvent(QMouseEvent *event, const QPointF &startPos) Q_DECL_OVERRIDE; + virtual void mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos) Q_DECL_OVERRIDE; + virtual void wheelEvent(QWheelEvent *event) Q_DECL_OVERRIDE; // non-virtual methods: void setupTickVectors(); @@ -2283,7 +2291,7 @@ protected: /* including file 'src/scatterstyle.h', size 7275 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPScatterStyle { @@ -2390,7 +2398,7 @@ Q_DECLARE_METATYPE(QCPScatterStyle::ScatterShape) /* including file 'src/datacontainer.h', size 4596 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ /*! \relates QCPDataContainer Returns whether the sort key of \a a is less than the sort key of \a b. @@ -2460,7 +2468,7 @@ protected: // include implementation in header since it is a class template: /* including file 'src/datacontainer.cpp', size 31349 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPDataContainer @@ -3234,8 +3242,8 @@ void QCPDataContainer::performAutoSqueeze() /* end of 'src/datacontainer.h' */ -/* including file 'src/plottable.h', size 8312 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* including file 'src/plottable.h', size 8433 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPSelectionDecorator { @@ -3329,7 +3337,7 @@ public: void setSelectionDecorator(QCPSelectionDecorator *decorator); // introduced virtual methods: - virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const = 0; + virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE = 0; // actually introduced in QCPLayerable as non-pure, but we want to force reimplementation for plottables virtual QCPPlottableInterface1D *interface1D() { return 0; } virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const = 0; virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const = 0; @@ -3392,7 +3400,7 @@ private: /* including file 'src/item.h', size 9384 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPItemAnchor { @@ -3577,7 +3585,7 @@ private: /* including file 'src/core.h', size 14886 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCustomPlot : public QWidget { @@ -3845,7 +3853,7 @@ Q_DECLARE_METATYPE(QCustomPlot::RefreshPriority) /* including file 'src/plottable1d.h', size 4544 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCPPlottableInterface1D { @@ -3904,8 +3912,8 @@ private: // include implementation in header since it is a class template: -/* including file 'src/plottable1d.cpp', size 22240 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* including file 'src/plottable1d.cpp', size 22361 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCPPlottableInterface1D @@ -4279,6 +4287,9 @@ int QCPAbstractPlottable1D::findEnd(double sortKey, bool expandedRange point-like. Most subclasses will want to reimplement this method again, to provide a more accurate hit test based on the true data visualization geometry. + If \a details is not 0, it will be set to a \ref QCPDataSelection, describing the closest data point + to \a pos. + \seebaseclassmethod */ template @@ -4290,7 +4301,7 @@ double QCPAbstractPlottable1D::selectTest(const QPointF &pos, bool onl return -1; QCPDataSelection selectionResult; - double minDistSqr = std::numeric_limits::max(); + double minDistSqr = (std::numeric_limits::max)(); int minDistIndex = mDataContainer->size(); typename QCPDataContainer::const_iterator begin = mDataContainer->constBegin(); @@ -4425,7 +4436,7 @@ void QCPAbstractPlottable1D::drawPolyline(QCPPainter *painter, const Q /* including file 'src/colorgradient.h', size 6243 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPColorGradient { @@ -4508,7 +4519,7 @@ Q_DECLARE_METATYPE(QCPColorGradient::GradientPreset) /* including file 'src/selectiondecorator-bracket.h', size 4442 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPSelectionDecoratorBracket : public QCPSelectionDecorator { @@ -4577,7 +4588,7 @@ Q_DECLARE_METATYPE(QCPSelectionDecoratorBracket::BracketStyle) /* including file 'src/layoutelements/layoutelement-axisrect.h', size 7507 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPAxisRect : public QCPLayoutElement { @@ -4703,7 +4714,7 @@ private: /* including file 'src/layoutelements/layoutelement-legend.h', size 10397 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPAbstractLegendItem : public QCPLayoutElement { @@ -4921,7 +4932,7 @@ Q_DECLARE_METATYPE(QCPLegend::SelectablePart) /* including file 'src/layoutelements/layoutelement-textelement.h', size 5353 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPTextElement : public QCPLayoutElement { @@ -5008,7 +5019,7 @@ private: /* including file 'src/layoutelements/layoutelement-colorscale.h', size 5923 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCPColorScaleAxisRectPrivate : public QCPAxisRect @@ -5116,7 +5127,7 @@ private: /* including file 'src/plottables/plottable-graph.h', size 9294 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPGraphData { @@ -5255,7 +5266,7 @@ Q_DECLARE_METATYPE(QCPGraph::LineStyle) /* including file 'src/plottables/plottable-curve.h', size 7409 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPCurveData { @@ -5369,8 +5380,8 @@ Q_DECLARE_METATYPE(QCPCurve::LineStyle) /* end of 'src/plottables/plottable-curve.h' */ -/* including file 'src/plottables/plottable-bars.h', size 8924 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* including file 'src/plottables/plottable-bars.h', size 8933 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPBarsGroup : public QObject { @@ -5392,7 +5403,7 @@ public: }; Q_ENUMS(SpacingType) - QCPBarsGroup(QCustomPlot *parentPlot); + explicit QCPBarsGroup(QCustomPlot *parentPlot); virtual ~QCPBarsGroup(); // getters: @@ -5558,7 +5569,7 @@ Q_DECLARE_METATYPE(QCPBars::WidthType) /* including file 'src/plottables/plottable-statisticalbox.h', size 7516 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPStatisticalBoxData { @@ -5675,7 +5686,7 @@ protected: /* including file 'src/plottables/plottable-colormap.h', size 7070 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPColorMapData { @@ -5811,7 +5822,7 @@ protected: /* including file 'src/plottables/plottable-financial.h', size 8622 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPFinancialData { @@ -5950,7 +5961,7 @@ Q_DECLARE_METATYPE(QCPFinancial::ChartStyle) /* including file 'src/plottables/plottable-errorbar.h', size 7727 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPErrorBarsData { @@ -6075,7 +6086,7 @@ protected: /* including file 'src/items/item-straightline.h', size 3117 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPItemStraightLine : public QCPAbstractItem { @@ -6118,7 +6129,7 @@ protected: /* including file 'src/items/item-line.h', size 3407 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPItemLine : public QCPAbstractItem { @@ -6168,7 +6179,7 @@ protected: /* including file 'src/items/item-curve.h', size 3379 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPItemCurve : public QCPAbstractItem { @@ -6219,7 +6230,7 @@ protected: /* including file 'src/items/item-rect.h', size 3688 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPItemRect : public QCPAbstractItem { @@ -6278,7 +6289,7 @@ protected: /* including file 'src/items/item-text.h', size 5554 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPItemText : public QCPAbstractItem { @@ -6375,7 +6386,7 @@ protected: /* including file 'src/items/item-ellipse.h', size 3868 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPItemEllipse : public QCPAbstractItem { @@ -6437,7 +6448,7 @@ protected: /* including file 'src/items/item-pixmap.h', size 4373 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPItemPixmap : public QCPAbstractItem { @@ -6506,7 +6517,7 @@ protected: /* including file 'src/items/item-tracer.h', size 4762 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPItemTracer : public QCPAbstractItem { @@ -6592,7 +6603,7 @@ Q_DECLARE_METATYPE(QCPItemTracer::TracerStyle) /* including file 'src/items/item-bracket.h', size 3969 */ -/* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */ +/* commit ce344b3f96a62e5f652585e55f1ae7c7883cd45b 2018-06-25 01:03:39 +0200 */ class QCP_LIB_DECL QCPItemBracket : public QCPAbstractItem { diff --git a/re/graphingwindow.cpp b/re/graphingwindow.cpp index b311623..7316fc5 100644 --- a/re/graphingwindow.cpp +++ b/re/graphingwindow.cpp @@ -1020,7 +1020,7 @@ void GraphingWindow::showParamsDialog(int idx = -1) { dbcHandler = DBCHandler::getReference(); - NewGraphDialog *thisDialog = new NewGraphDialog(dbcHandler); + NewGraphDialog *thisDialog = new NewGraphDialog(dbcHandler, this); if (idx > -1) { @@ -1096,7 +1096,23 @@ void GraphingWindow::createGraph(GraphParams ¶ms, bool createGraphParam) if (thisFrame.ID == params.ID) frameCache.append(thisFrame); } + //to fix weirdness where a graph that has no data won't be able to be edited, selected, or deleted properly + //we'll check for the condition that there is nothing to graph and add a single dummy frame to the cache + //that has all data bytes = 0. This allows the graph to be edited and deleted. No idea why you can't otherwise. + if (frameCache.count() == 0) + { + CANFrame dummy; + dummy.ID = params.ID; + dummy.bus = 0; + dummy.len = 8; + dummy.remote = false; + dummy.timestamp = 0; + for (int i = 0; i < 8; i++) dummy.data[i] = 0; + frameCache.append(dummy); + } + int numEntries = frameCache.count() / params.stride; + if (numEntries < 1) numEntries = 1; //could happen if stride is larger than frame count params.x.reserve(numEntries); params.y.reserve(numEntries); diff --git a/re/graphingwindow.h b/re/graphingwindow.h index cc79cbc..2d08220 100644 --- a/re/graphingwindow.h +++ b/re/graphingwindow.h @@ -26,6 +26,7 @@ public: QColor color; QCPGraph *ref; QString graphName; + DBC_SIGNAL *associatedSignal; //the below stuff is used for internal purposes only - code should be refactored so these can be private QVector x, y; double xbias; diff --git a/re/newgraphdialog.cpp b/re/newgraphdialog.cpp index 0297a39..1e13110 100644 --- a/re/newgraphdialog.cpp +++ b/re/newgraphdialog.cpp @@ -29,9 +29,11 @@ NewGraphDialog::NewGraphDialog(DBCHandler *handler, QWidget *parent) : connect(ui->txtDataLen, SIGNAL(textChanged(QString)), this, SLOT(handleDataLenUpdate())); connect(ui->cbIntel, SIGNAL(toggled(bool)), this, SLOT(drawBitfield())); connect(ui->btnCopySignal, SIGNAL(clicked(bool)), this, SLOT(copySignalToParamsUI())); + connect(ui->cbSignals, SIGNAL(currentIndexChanged(int)), this, SLOT(drawBitfield())); startBit = 0; dataLen = 1; + assocSignal = nullptr; loadMessages(); @@ -85,6 +87,56 @@ void NewGraphDialog::colorSwatchClick() } +//check whether the current values on the left match the signal selected on the right +void NewGraphDialog::checkSignalAgreement() +{ + GraphParams testingParams; + bool bAgree = true; + bool sigSigned = false; + DBC_SIGNAL *sig; + DBC_MESSAGE *msg; + + if (dbcHandler == nullptr) return; + if (dbcHandler->getFileCount() == 0) return; + + msg = dbcHandler->findMessage(ui->cbMessages->currentText()); + if (msg) + { + sig = msg->sigHandler->findSignalByName(ui->cbSignals->currentText()); + } + + if (sig) + { + if (sig->valType == SIGNED_INT) sigSigned = true; + + testingParams.ID = Utility::ParseStringToNum(ui->txtID->text()); + testingParams.bias = ui->txtBias->text().toFloat(); + testingParams.isSigned = ui->cbSigned->isChecked(); + testingParams.intelFormat = ui->cbIntel->isChecked(); + testingParams.scale = ui->txtScale->text().toFloat(); + testingParams.startBit = startBit; + testingParams.numBits = dataLen; + + if (testingParams.ID != msg->ID) bAgree = false; + if (abs(testingParams.bias - sig->bias) > 0.01) bAgree = false; + if (testingParams.isSigned != sigSigned) bAgree = false; + if (testingParams.intelFormat != sig->intelByteOrder) bAgree = false; + if (abs(testingParams.scale - sig->factor) > 0.01) bAgree = false; + if (testingParams.startBit != sig->startBit) bAgree = false; + if (testingParams.numBits != sig->signalSize) bAgree = false; + } + if (bAgree) + { + ui->lblMsgStatus->setText("Graph params match this signal"); + //assocSignal = sig; + } + else + { + ui->lblMsgStatus->setText("Signal and Graph Params do not match"); + //assocSignal = nullptr; //ya done broke it, null the associated signal since the user is changing things + } +} + void NewGraphDialog::clearParams() { ui->txtID->clear(); @@ -112,8 +164,13 @@ void NewGraphDialog::setParams(GraphParams ¶ms) QPalette p = ui->colorSwatch->palette(); p.setColor(QPalette::Button, params.color); ui->colorSwatch->setPalette(p); + + assocSignal = params.associatedSignal; + loadMessages(); + loadSignals(0); drawBitfield(); + checkSignalAgreement(); } void NewGraphDialog::getParams(GraphParams ¶ms) @@ -132,6 +189,8 @@ void NewGraphDialog::getParams(GraphParams ¶ms) params.startBit = startBit; params.numBits = dataLen; + params.associatedSignal = assocSignal; + //now catch stupidity and bring it to defaults if (params.mask == 0) params.mask = 0xFFFFFFFF; if (fabs(params.scale) < 0.00000001) params.scale = 1.0f; @@ -140,6 +199,7 @@ void NewGraphDialog::getParams(GraphParams ¶ms) void NewGraphDialog::loadMessages() { + DBC_MESSAGE *msg; ui->cbMessages->clear(); if (dbcHandler == NULL) return; if (dbcHandler->getFileCount() == 0) return; @@ -147,7 +207,16 @@ void NewGraphDialog::loadMessages() { for (int x = 0; x < dbcHandler->getFileByIdx(y)->messageHandler->getCount(); x++) { - ui->cbMessages->addItem(dbcHandler->getFileByIdx(y)->messageHandler->findMsgByIdx(x)->name); + msg = dbcHandler->getFileByIdx(y)->messageHandler->findMsgByIdx(x); + if (msg) + { + ui->cbMessages->addItem(msg->name); + if (assocSignal && msg->name == assocSignal->parentMessage->name) + { + ui->cbMessages->setCurrentIndex(ui->cbMessages->count() -1); + qDebug() << "Found my parent"; + } + } } } } @@ -160,13 +229,25 @@ void NewGraphDialog::loadSignals(int idx) //look it up based on index but by name is probably safer and this operation //is not time critical at all. DBC_MESSAGE *msg = dbcHandler->getFileByIdx(0)->messageHandler->findMsgByName(ui->cbMessages->currentText()); + DBC_SIGNAL *sig; if (msg == NULL) return; + ui->cbSignals->clear(); for (int x = 0; x < msg->sigHandler->getCount(); x++) { - ui->cbSignals->addItem(msg->sigHandler->findSignalByIdx(x)->name); + sig = msg->sigHandler->findSignalByIdx(x); + if (sig) + { + ui->cbSignals->addItem(sig->name); + if (assocSignal && sig->name == assocSignal->name) + { + ui->cbSignals->setCurrentIndex(ui->cbSignals->count() - 1); + qDebug() << "Found me"; + } + } } + checkSignalAgreement(); } void NewGraphDialog::bitfieldClicked(int x,int y) @@ -175,7 +256,7 @@ void NewGraphDialog::bitfieldClicked(int x,int y) qDebug() << "Clicked bit: " << bit; startBit = bit; - drawBitfield(); + drawBitfield(); } void NewGraphDialog::drawBitfield() @@ -213,6 +294,8 @@ void NewGraphDialog::drawBitfield() } ui->gridData->updateData((unsigned char *)&bitField, true); + + checkSignalAgreement(); } void NewGraphDialog::handleDataLenUpdate() @@ -221,6 +304,7 @@ void NewGraphDialog::handleDataLenUpdate() if (dataLen < 1) dataLen = 1; if (dataLen > 63) dataLen = 63; drawBitfield(); + checkSignalAgreement(); } void NewGraphDialog::copySignalToParamsUI() @@ -242,4 +326,6 @@ void NewGraphDialog::copySignalToParamsUI() if (sig->valType == SIGNED_INT) ui->cbSigned->setChecked(true); else ui->cbSigned->setChecked(false); drawBitfield(); + assocSignal = sig; + checkSignalAgreement(); } diff --git a/re/newgraphdialog.h b/re/newgraphdialog.h index 365456d..9200f47 100644 --- a/re/newgraphdialog.h +++ b/re/newgraphdialog.h @@ -33,9 +33,11 @@ private slots: private: bool eventFilter(QObject *obj, QEvent *event); + void checkSignalAgreement(); Ui::NewGraphDialog *ui; DBCHandler *dbcHandler; + DBC_SIGNAL *assocSignal; int startBit, dataLen; }; diff --git a/ui/newgraphdialog.ui b/ui/newgraphdialog.ui index 6ee8ae2..c99f7c1 100644 --- a/ui/newgraphdialog.ui +++ b/ui/newgraphdialog.ui @@ -241,6 +241,20 @@ + + + + + 12 + 75 + true + + + + + + +