diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index c717ae58..61287ae7 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -8,7 +8,10 @@ on: jobs: build: name: GPXSee - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 + strategy: + matrix: + config: ['release', 'debug'] steps: - name: Checkout uses: actions/checkout@v4 @@ -19,6 +22,6 @@ jobs: - name: Create localization run: lrelease gpxsee.pro - name: Configure build - run: qmake gpxsee.pro + run: qmake CONFIG+=${{ matrix.config }} gpxsee.pro - name: Build project run: make -j2 diff --git a/.github/workflows/osx.yml b/.github/workflows/osx.yml index 5733c306..2423406b 100644 --- a/.github/workflows/osx.yml +++ b/.github/workflows/osx.yml @@ -15,7 +15,7 @@ jobs: - name: Install Qt uses: jurplel/install-qt-action@v4 with: - version: '6.8.1' + version: '6.8.2' modules: qtpositioning qtserialport qtimageformats - name: Create localization run: lrelease gpxsee.pro diff --git a/README.md b/README.md index 5971d59c..d61d3706 100644 --- a/README.md +++ b/README.md @@ -29,8 +29,8 @@ GPS log file formats. ## Build Build requirements: -* Qt5 >= 5.11 or Qt6 >= 6.2 (Android builds require Qt6) -* C++11 or newer compiler (tested: msvc2019, gcc 7.5.0, clang/Apple LLVM version +* Qt5 >= 5.15 or Qt6 >= 6.2 (Android builds require Qt6) +* C++11 or newer compiler (tested: msvc2022, gcc 11, clang/Apple LLVM version 10.0.0) Build steps: diff --git a/gpxsee.pro b/gpxsee.pro index 29183b9e..fb09e503 100644 --- a/gpxsee.pro +++ b/gpxsee.pro @@ -5,7 +5,6 @@ unix:!macx:!android { } VERSION = 13.36 - QT += core \ gui \ gui-private \ @@ -118,7 +117,7 @@ HEADERS += src/common/config.h \ src/data/gpsdumpparser.h \ src/data/style.h \ src/data/twonavparser.h \ - src/map/IMG/lights.h \ + src/map/IMG/light.h \ src/map/downloader.h \ src/map/demloader.h \ src/map/ENC/attributes.h \ diff --git a/src/GUI/app.cpp b/src/GUI/app.cpp index 90463471..6a913bfe 100644 --- a/src/GUI/app.cpp +++ b/src/GUI/app.cpp @@ -73,9 +73,7 @@ App::App(int &argc, char **argv) : QApplication(argc, argv) #if defined(Q_OS_WIN32) || defined(Q_OS_MAC) QIcon::setThemeName(APP_NAME); #endif // Q_OS_WIN32 || Q_OS_MAC -#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) QIcon::setFallbackThemeName(APP_NAME); -#endif // QT 5.12 _gui = new GUI(); diff --git a/src/GUI/graphview.cpp b/src/GUI/graphview.cpp index 063c6de8..c29086e1 100644 --- a/src/GUI/graphview.cpp +++ b/src/GUI/graphview.cpp @@ -25,15 +25,6 @@ #define IW(item) ((item)->boundingRect().width()) #define IH(item) ((item)->boundingRect().height()) -static inline QPoint POS(QWheelEvent *e) -{ -#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) - return e->pos(); -#else // QT 5.15 - return e->position().toPoint(); -#endif // QT 5.15 -} - static inline QPoint POS(QMouseEvent *e) { #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) @@ -393,7 +384,7 @@ void GraphView::wheelEvent(QWheelEvent *e) return; _angleDelta = _angleDelta % (15 * 8); - QPointF pos = mapToScene(POS(e)); + QPointF pos = mapToScene(e->position().toPoint()); QRectF gr(_grid->boundingRect()); QPointF r(pos.x() / gr.width(), pos.y() / gr.height()); @@ -404,7 +395,8 @@ void GraphView::wheelEvent(QWheelEvent *e) QPointF npos(mapFromScene(QPointF(r.x() * ngr.width(), r.y() * ngr.height()))); QScrollBar *sb = horizontalScrollBar(); - sb->setSliderPosition(sb->sliderPosition() + npos.x() - POS(e).x()); + sb->setSliderPosition(sb->sliderPosition() + npos.x() + - e->position().toPoint().x()); QGraphicsView::wheelEvent(e); } diff --git a/src/GUI/gui.cpp b/src/GUI/gui.cpp index b237016b..52bba0d5 100644 --- a/src/GUI/gui.cpp +++ b/src/GUI/gui.cpp @@ -2498,12 +2498,8 @@ QGeoPositionInfoSource *GUI::positionSource(const Options &options) { QGeoPositionInfoSource *source; -#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) - source = QGeoPositionInfoSource::createSource(options.plugin, this); -#else // QT 5.14 source = QGeoPositionInfoSource::createSource(options.plugin, options.pluginParams.value(options.plugin), this); -#endif // QT 5.14 if (source) source->setPreferredPositioningMethods( QGeoPositionInfoSource::SatellitePositioningMethods); diff --git a/src/GUI/mapview.cpp b/src/GUI/mapview.cpp index e6b278eb..d139403c 100644 --- a/src/GUI/mapview.cpp +++ b/src/GUI/mapview.cpp @@ -638,11 +638,7 @@ void MapView::wheelEvent(QWheelEvent *event) return; _wheelDelta = _wheelDelta % (15 * 8); -#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) - zoom((delta > 0) ? 1 : -1, event->pos(), shift); -#else // QT 5.15 zoom((delta > 0) ? 1 : -1, event->position().toPoint(), shift); -#endif // QT 5.15 /* Do not call QGraphicsView::wheelEvent() here as this would shift the view ! */ diff --git a/src/GUI/optionsdialog.cpp b/src/GUI/optionsdialog.cpp index 86ec4e95..d9c4103f 100644 --- a/src/GUI/optionsdialog.cpp +++ b/src/GUI/optionsdialog.cpp @@ -676,21 +676,17 @@ QWidget *OptionsDialog::createPositionPage() _positionPlugin = new QComboBox(); _positionPlugin->addItems(plugins); _positionPlugin->setCurrentIndex(_positionPlugin->findText(_options.plugin)); -#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) _pluginParameters = new PluginParameters(_positionPlugin->currentText(), _options.pluginParams); connect(_positionPlugin, &QComboBox::currentTextChanged, _pluginParameters, &PluginParameters::setPlugin); -#endif // QT 5.14 QFormLayout *pluginLayout = new QFormLayout(); pluginLayout->addRow(tr("Plugin:"), _positionPlugin); QVBoxLayout *sourceLayout = new QVBoxLayout(); sourceLayout->addLayout(pluginLayout); -#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) sourceLayout->addWidget(_pluginParameters); -#endif // QT 5.14 sourceLayout->addStretch(); QWidget *sourceTab = new QWidget(); @@ -1009,9 +1005,7 @@ void OptionsDialog::accept() _options.hillshadingZFactor = _hillshadingZFactor->value(); _options.plugin = _positionPlugin->currentText(); -#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) _options.pluginParams = _pluginParameters->parameters(); -#endif // QT 5.14 _options.useOpenGL = _useOpenGL->isChecked(); _options.enableHTTP2 = _enableHTTP2->isChecked(); diff --git a/src/GUI/optionsdialog.h b/src/GUI/optionsdialog.h index 283484ce..cbd7d889 100644 --- a/src/GUI/optionsdialog.h +++ b/src/GUI/optionsdialog.h @@ -193,9 +193,7 @@ private: QDoubleSpinBox *_hillshadingZFactor; // Position QComboBox *_positionPlugin; -#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) PluginParameters *_pluginParameters; -#endif // QT 5.14 // System QSpinBox *_pixmapCache; QSpinBox *_demCache; diff --git a/src/GUI/pathitem.cpp b/src/GUI/pathitem.cpp index d13d5002..646ca6a5 100644 --- a/src/GUI/pathitem.cpp +++ b/src/GUI/pathitem.cpp @@ -10,13 +10,6 @@ #include "markeritem.h" #include "pathitem.h" - -#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) -#define INTERSECTS intersect -#else // QT 5.15 -#define INTERSECTS intersects -#endif // QT 5.15 - #define GEOGRAPHICAL_MILE 1855.3248 Units PathItem::_units = Metric; @@ -79,14 +72,14 @@ bool PathItem::addSegment(const Coordinates &c1, const Coordinates &c2) QLineF l(QPointF(c1.lon(), c1.lat()), QPointF(c2.lon() + 360, c2.lat())); QLineF dl(QPointF(180, -90), QPointF(180, 90)); - l.INTERSECTS(dl, &p); + l.intersects(dl, &p); _painterPath.lineTo(_map->ll2xy(Coordinates(180, p.y()))); _painterPath.moveTo(_map->ll2xy(Coordinates(-180, p.y()))); } else { QLineF l(QPointF(c1.lon(), c1.lat()), QPointF(c2.lon() - 360, c2.lat())); QLineF dl(QPointF(-180, -90), QPointF(-180, 90)); - l.INTERSECTS(dl, &p); + l.intersects(dl, &p); _painterPath.lineTo(_map->ll2xy(Coordinates(-180, p.y()))); _painterPath.moveTo(_map->ll2xy(Coordinates(180, p.y()))); } diff --git a/src/GUI/popup.cpp b/src/GUI/popup.cpp index 5a1a0ea3..84f98409 100644 --- a/src/GUI/popup.cpp +++ b/src/GUI/popup.cpp @@ -9,9 +9,6 @@ #include #include #include -#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) -#include -#endif // QT 5.15 #include "tooltip.h" #include "thumbnail.h" #include "flowlayout.h" @@ -184,11 +181,7 @@ bool PopupFrame::eventFilter(QObject *o, QEvent *ev) void PopupFrame::place(const QPoint &pos, QWidget *w) { -#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) - QRect screen = QApplication::desktop()->screenGeometry(w); -#else // QT 5.15 QRect screen = w->screen()->geometry(); -#endif // QT 5.15 QPoint p(pos.x() + 2, pos.y() + 16); if (p.x() + width() > screen.x() + screen.width()) diff --git a/src/data/twonavparser.cpp b/src/data/twonavparser.cpp index 89c6e756..21c4d6c1 100644 --- a/src/data/twonavparser.cpp +++ b/src/data/twonavparser.cpp @@ -2,13 +2,6 @@ #include "map/gcs.h" #include "twonavparser.h" - -#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) -#define SKIP_EMPTY QString::SkipEmptyParts -#else // Qt 5.14 -#define SKIP_EMPTY Qt::SkipEmptyParts -#endif - static double lon(const QString &str) { QStringList l(str.split(QChar(0xBA))); @@ -123,7 +116,8 @@ bool TwoNavParser::parse(QFile *file, QList &tracks, }} break; case 'T': - {QStringList list(codec.toString(line).split(' ', SKIP_EMPTY)); + {QStringList list(codec.toString(line).split(' ', + Qt::SkipEmptyParts)); if (list.size() < 4) { _errorString = "Parse error"; return false; @@ -159,7 +153,8 @@ bool TwoNavParser::parse(QFile *file, QList &tracks, tracks.last().last().append(t);} break; case 'W': - {QStringList list(codec.toString(line).split(' ', SKIP_EMPTY)); + {QStringList list(codec.toString(line).split(' ', + Qt::SkipEmptyParts)); if (list.size() < 5) { _errorString = "Parse error"; return false; diff --git a/src/map/IMG/light.h b/src/map/IMG/light.h new file mode 100644 index 00000000..276d623d --- /dev/null +++ b/src/map/IMG/light.h @@ -0,0 +1,64 @@ +#ifndef IMG_LIGHT_H +#define IMG_LIGHT_H + +#include +#include + +namespace IMG { + +class Light +{ +public: + enum Color {None, Red, Green, White, Blue, Yellow, Violet, Amber}; + + class Sector + { + public: + Sector() : _color(None), _angle(0), _range(0) {} + Sector(Color color, quint32 angle, quint32 range) + : _color(color), _angle(angle), _range(range) {} + + Color color() const {return _color;} + quint32 angle() const {return _angle;} + quint32 range() const {return _range;} + + private: + Color _color; + quint32 _angle; + quint32 _range; + }; + + Light() : _color(None), _range(0) {} + Light(Color color, quint32 range) : _color(color), _range(range) {} + Light(const QVector §ors) + : _color(None), _range(0), _sectors(sectors) {} + + Color color() const {return _color;} + quint32 range() const {return _range;} + const QVector §ors() const {return _sectors;} + +private: + Color _color; + quint32 _range; + QVector _sectors; +}; + +} + +#ifndef QT_NO_DEBUG +inline QDebug operator<<(QDebug dbg, const IMG::Light::Sector §or) +{ + dbg.nospace() << "Sector(" << sector.color() << ", " << sector.angle() + << ", " << sector.range() << ")"; + return dbg.space(); +} + +inline QDebug operator<<(QDebug dbg, const IMG::Light &light) +{ + dbg.nospace() << "Light(" << light.color() << ", " << light.range() << ", " + << light.sectors() << ")"; + return dbg.space(); +} +#endif // QT_NO_DEBUG + +#endif // IMG_LIGHT_H diff --git a/src/map/IMG/lights.h b/src/map/IMG/lights.h deleted file mode 100644 index c1265801..00000000 --- a/src/map/IMG/lights.h +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef IMG_LIGHTS_H -#define IMG_LIGHTS_H - -#include -#include - -namespace IMG { - -class Lights -{ -public: - enum Color {None, Red, Green, White, Blue, Yellow, Violet, Amber}; - - struct Sector - { - Sector() : color(None), angle(0), range(0) {} - Sector(Color color, quint32 angle, quint32 range) - : color(color), angle(angle), range(range) {} - - Color color; - quint32 angle; - quint32 range; - }; - - Lights() : color(None), range(0) {} - bool isSectorLight() const - {return ((color && range > 6) || !sectors.isEmpty());} - - Color color; - quint32 range; - QVector sectors; -}; - -} - -#ifndef QT_NO_DEBUG -inline QDebug operator<<(QDebug dbg, const IMG::Lights::Sector §or) -{ - dbg.nospace() << "Sector(" << sector.color << ", " << sector.angle - << ", " << sector.range << ")"; - return dbg.space(); -} - -inline QDebug operator<<(QDebug dbg, const IMG::Lights &lights) -{ - dbg.nospace() << "Lights(" << lights.color << ", " << lights.range << ", " - << lights.sectors << ")"; - return dbg.space(); -} -#endif // QT_NO_DEBUG - -#endif // IMG_LIGHTS_H diff --git a/src/map/IMG/mapdata.h b/src/map/IMG/mapdata.h index 4f591e98..3920ef24 100644 --- a/src/map/IMG/mapdata.h +++ b/src/map/IMG/mapdata.h @@ -14,7 +14,7 @@ #include "map/matrix.h" #include "label.h" #include "raster.h" -#include "lights.h" +#include "light.h" #include "zoom.h" namespace IMG { @@ -56,7 +56,7 @@ public: Coordinates coordinates; Label label; - Lights lights; + QVector lights; quint64 id; quint32 type; quint32 flags; diff --git a/src/map/IMG/rastertile.cpp b/src/map/IMG/rastertile.cpp index 82664113..236dfcbd 100644 --- a/src/map/IMG/rastertile.cpp +++ b/src/map/IMG/rastertile.cpp @@ -19,6 +19,7 @@ using namespace IMG; #define TEXT_EXTENT 160 #define ICON_PADDING 2 #define RANGE_FACTOR 4 +#define RANGE_MIN 6 #define ROAD 0 #define WATER 1 @@ -228,8 +229,8 @@ void RasterTile::drawTextItems(QPainter *painter, static QRect lightRect(const QPoint &pos, quint32 range) { - return QRect(pos.x() - range * RANGE_FACTOR, pos.y() - range * RANGE_FACTOR, - 2*range * RANGE_FACTOR, 2*range * RANGE_FACTOR); + quint32 r = qMin(range * RANGE_FACTOR, (quint32)TEXT_EXTENT); + return QRect(pos.x() - r, pos.y() - r, 2 * r, 2 * r); } void RasterTile::drawSectorLights(QPainter *painter, @@ -239,48 +240,54 @@ void RasterTile::drawSectorLights(QPainter *painter, const MapData::Point *p = lights.at(i); QPoint pos(p->coordinates.lon(), p->coordinates.lat()); - if (p->lights.sectors.size()) { - for (int j = 0; j < p->lights.sectors.size(); j++) { - const Lights::Sector &start = p->lights.sectors.at(j); - const Lights::Sector &end = (j == p->lights.sectors.size() - 1) - ? p->lights.sectors.at(0) : p->lights.sectors.at(j+1); + for (int j = 0; j < p->lights.size(); j++) { + const Light &l = p->lights.at(j); - if (start.color) { - double a1 = -(end.angle / 10.0 + 90.0); - double a2 = -(start.angle / 10.0 + 90.0); - if (a1 > a2) - a2 += 360; - double as = (a2 - a1); - if (as == 0) - as = 360; + if (l.sectors().size()) { + for (int k = 0; k < l.sectors().size(); k++) { + const Light::Sector &start = l.sectors().at(k); + const Light::Sector &end = (k == l.sectors().size() - 1) + ? l.sectors().at(0) : l.sectors().at(k+1); - QRect rect(lightRect(pos, start.range ? start.range : 6)); + if (start.color()) { + double a1 = -(end.angle() / 10.0 + 90.0); + double a2 = -(start.angle() / 10.0 + 90.0); + if (a1 > a2) + a2 += 360; + double as = (a2 - a1); + if (as == 0) + as = 360; - painter->setPen(QPen(Qt::black, 6, Qt::SolidLine, - Qt::FlatCap)); - painter->drawArc(rect, a1 * 16, as * 16); - painter->setPen(QPen(Style::color(start.color), 4, - Qt::SolidLine, Qt::FlatCap)); - painter->drawArc(rect, a1 * 16, as * 16); + QRect rect(lightRect(pos, start.range() + ? start.range() : RANGE_MIN)); - if (a2 - a1 != 0) { - QLineF ln(pos, QPointF(pos.x() + rect.width(), pos.y())); - ln.setAngle(a1); - painter->setPen(QPen(Qt::black, 1, Qt::DashLine)); - painter->drawLine(ln); - ln.setAngle(a2); - painter->drawLine(ln); + painter->setPen(QPen(Qt::black, 6, Qt::SolidLine, + Qt::FlatCap)); + painter->drawArc(rect, a1 * 16, as * 16); + painter->setPen(QPen(Style::color(start.color()), 4, + Qt::SolidLine, Qt::FlatCap)); + painter->drawArc(rect, a1 * 16, as * 16); + + if (a2 - a1 != 0) { + QLineF ln(pos, QPointF(pos.x() + rect.width(), + pos.y())); + ln.setAngle(a1); + painter->setPen(QPen(Qt::black, 1, Qt::DashLine)); + painter->drawLine(ln); + ln.setAngle(a2); + painter->drawLine(ln); + } } } - } - } else { - QRect rect(lightRect(pos, p->lights.range)); + } else if (l.range() > RANGE_MIN) { + QRect rect(lightRect(pos, l.range())); - painter->setPen(QPen(Qt::black, 6, Qt::SolidLine, Qt::FlatCap)); - painter->drawArc(rect, 0, 360 * 16); - painter->setPen(QPen(Style::color(p->lights.color), 4, - Qt::SolidLine, Qt::FlatCap)); - painter->drawArc(rect, 0, 360 * 16); + painter->setPen(QPen(Qt::black, 6, Qt::SolidLine, Qt::FlatCap)); + painter->drawArc(rect, 0, 360 * 16); + painter->setPen(QPen(Style::color(l.color()), 4, Qt::SolidLine, + Qt::FlatCap)); + painter->drawArc(rect, 0, 360 * 16); + } } } } @@ -456,6 +463,17 @@ void RasterTile::processShields(const QList &lines, } } +static bool showAsSector(const QVector &lights) +{ + for (int i = 0; i < lights.size(); i++) { + const Light &l = lights.at(i); + if ((l.color() && l.range() > RANGE_MIN) || !l.sectors().isEmpty()) + return true; + } + + return false; +} + void RasterTile::processPoints(QList &points, QList &textItems, QList &lights) { @@ -466,8 +484,9 @@ void RasterTile::processPoints(QList &points, const Style *style = _data->style(); const Style::Point &ps = style->point(point.type); bool poi = Style::isPOI(point.type); + bool sl = showAsSector(point.lights); - if (point.lights.isSectorLight()) + if (sl) lights.append(&point); const QString *label = point.label.text().isEmpty() @@ -490,12 +509,16 @@ void RasterTile::processPoints(QList &points, TextPointItem *item = new TextPointItem(pos + offset, label, fnt, img, color, hcolor, 0, ICON_PADDING); - if (item->isValid() && (point.lights.isSectorLight() - || !item->collides(textItems))) { + if (item->isValid() && (sl || !item->collides(textItems))) { textItems.append(item); - if (point.lights.color && !point.lights.isSectorLight()) - textItems.append(new TextPointItem(pos + style->lightOffset(), - 0, 0, style->light(point.lights.color), 0, 0, 0, 0)); + for (int j = 0; j < point.lights.size(); j++) { + const Light &l = point.lights.at(j); + if (l.color() && l.range() <= RANGE_MIN) { + textItems.append(new TextPointItem(pos + style->lightOffset(), + 0, 0, style->light(l.color()), 0, 0, 0, 0)); + break; + } + } } else delete item; } diff --git a/src/map/IMG/rgnfile.cpp b/src/map/IMG/rgnfile.cpp index 06760059..495ed69f 100644 --- a/src/map/IMG/rgnfile.cpp +++ b/src/map/IMG/rgnfile.cpp @@ -13,6 +13,7 @@ using namespace Garmin; using namespace IMG; #define MASK(bits) ((1U << (bits)) - 1U) +#define COLOR(color) static_cast(color) static quint64 pointId(const QPoint &pos, quint32 type, const QString &label) { @@ -124,7 +125,7 @@ bool RGNFile::readBuoyInfo(Handle &hdl, quint8 flags, quint32 size, lc = (val >> 6) & 7; if (lc) - point->lights.color = (Lights::Color)lc; + point->lights.append(Light(COLOR(lc), 0)); return true; } @@ -190,6 +191,7 @@ bool RGNFile::readLightInfo(Handle &hdl, quint8 flags, quint32 size, if (flags1 & 0x800) { quint16 la; quint8 cf, range = 0; + QVector sectors; do { if (!(size >= 2 && readUInt16(hdl, la))) @@ -197,14 +199,16 @@ bool RGNFile::readLightInfo(Handle &hdl, quint8 flags, quint32 size, size -= 2; cf = la >> 8; - Lights::Color c = (Lights::Color)(cf >> 4 & 7); + Light::Color c = COLOR(cf >> 4 & 7); if (c) { if (!(size >= 1 && readUInt8(hdl, range))) return false; size--; } - point->lights.sectors.append(Lights::Sector(c, la & 0xfff, range)); + sectors.append(Light::Sector(c, la & 0xfff, range)); } while (!(cf >> 7)); + + point->lights.append(Light(sectors)); } else { quint8 v1, v2, range; @@ -220,8 +224,7 @@ bool RGNFile::readLightInfo(Handle &hdl, quint8 flags, quint32 size, range += v2; } - point->lights.color = (Lights::Color)(v1 >> 5); - point->lights.range = range; + point->lights.append(Light(COLOR(v1 >> 5), range)); } return true; @@ -289,8 +292,176 @@ bool RGNFile::readClassFields(Handle &hdl, SegmentType segmentType, return seek(hdl, off + rs); } -bool RGNFile::skipLclFields(Handle &hdl, const quint32 flags[3]) const +bool RGNFile::readLclSectors(Handle &hdl, quint32 &size, quint32 flags, + Light &light) const { + quint32 unused, cnt = flags & 0x1f; + QVector sectors; + + for (quint32 j = 0; j < cnt; j++) { + quint32 cf, range = 0; + + if (!(size >= 1 && readUInt8(hdl, cf))) + return false; + size--; + if (cf >> 6) { + if (!(size >= (cf >> 6) && readVUInt32(hdl, cf >> 6, range))) + return false; + size -= (cf >> 6); + } + + if (cnt > 1) { + quint32 angle; + + if (!(size >= 2 && readUInt16(hdl, angle))) + return false; + size -= 2; + if ((flags >> 0x13) & 1) { + quint32 sflags; + + if (!(size >= 1 && readUInt8(hdl, sflags))) + return false; + size--; + if (0x3f < sflags) { + if (sflags & 0x80) { + if (!(size >= 1 && readUInt8(hdl, unused))) + return false; + size--; + unused |= (sflags & 0x40) << 2; + } else { + if (!(size >= 2 && readUInt16(hdl, unused))) + return false; + size -= 2; + } + } + } + + sectors.append(Light::Sector(COLOR(cf & 0x7), angle, range)); + } else { + light = Light(COLOR(cf & 0x7), range); + return true; + } + } + + light = Light(sectors); + + return true; +} + +bool RGNFile::readLclLights(Handle &hdl, quint32 &size, quint32 lights, + MapData::Point *point) const +{ + quint32 unused; + + for (quint32 i = 0; i < lights; i++) { + quint32 fs, vs, flags; + Light light; + + if (!(readVUInt32(hdl, fs, &vs) && size >= vs)) + return false; + size -= vs; + if (!(size >= 4 && readUInt32(hdl, flags))) + return false; + size -= 4; + if (flags >> 0x11 & 3) { + if (!(size >= (flags >> 0x11 & 3) + && readVUInt32(hdl, flags >> 0x11 & 3, unused))) + return false; + size -= (flags >> 0x11 & 3); + } + if (flags & 0x3000) { + if (flags & 0x2000) { + if (!(size >= 1 && readUInt8(hdl, unused))) + return false; + size--; + unused |= (flags >> 4) & 0x100; + } else { + if (!(size >= 2 && readUInt16(hdl, unused))) + return false; + size -= 2; + } + } + if (flags & 0x100000) { + if (!(size >= 1 && readUInt8(hdl, unused))) + return false; + size--; + if (unused & 0x80) { + if (!(size >= 1 && readUInt8(hdl, unused))) + return false; + size--; + } + } + + if (!readLclSectors(hdl, size, flags, light)) + return false; + + point->lights.append(light); + } + + return true; +} + +bool RGNFile::readLclNavaid(Handle &hdl, quint32 size, + MapData::Point *point) const +{ + quint32 unused, flags; + + // Discard the class lights info if any (marine points may have both!) + point->lights.clear(); + + if (!(size >= 4 && readUInt32(hdl, flags))) + return false; + size -= 4; + if (flags & 1) { + if (!(size >= 1 && readUInt8(hdl, unused))) + return false; + size--; + } + if (flags & 2) { + if (!(size >= 1 && readUInt8(hdl, unused))) + return false; + size--; + } + if (flags & 4) { + if (!(size >= 1 && readUInt8(hdl, unused))) + return false; + size--; + } + if (flags & 8) { + if (!(size >= 3 && readUInt24(hdl, unused))) + return false; + size -= 3; + } + if (flags & 0x10) { + if (!(size >= 3 && readUInt24(hdl, unused))) + return false; + size -= 3; + } + if (flags & 0x20) { + if (!(size >= 3 && readUInt24(hdl, unused))) + return false; + size -= 3; + } + if (flags & 0x200) { + quint8 b; + do { + if (!(size >= 1 && readUInt8(hdl, b))) + return false; + size--; + } while (b & 0x80); + } + + if (!readLclLights(hdl, size, (flags >> 6) & 7, point)) + return false; + + return (size == 0); +} + +bool RGNFile::readLclFields(Handle &hdl, const quint32 flags[3], + SegmentType segmentType, void *object) const +{ + MapData::Point *point = (segmentType == Point) + ? (MapData::Point *) object : 0; quint32 bitfield = 0xFFFFFFFF; if (flags[0] & 0x20000000) @@ -302,14 +473,20 @@ bool RGNFile::skipLclFields(Handle &hdl, const quint32 flags[3]) const if (bitfield & 1) { quint32 m = flags[(j >> 4) + 1] >> ((j * 2) & 0x1e) & 3; - quint32 skip = 0; + quint32 size = 0; if (m == 3) { - if (!readVUInt32(hdl, skip)) + if (!readVUInt32(hdl, size)) return false; } else - skip = m + 1; - if (!seek(hdl, pos(hdl) + skip)) - return false; + size = m + 1; + + if (i == 2 && point) { + if (!readLclNavaid(hdl, size, point)) + return false; + } else { + if (!seek(hdl, pos(hdl) + size)) + return false; + } } bitfield >>= 1; j++; @@ -563,8 +740,8 @@ bool RGNFile::extPolyObjects(Handle &hdl, const SubDiv *subdiv, quint32 shift, if (subtype & 0x80 && !readClassFields(hdl, segmentType, &poly, lbl, lblHdl)) return false; - if (subtype & 0x40 && !skipLclFields(hdl, segmentType == Line - ? _linesLclFlags : _polygonsLclFlags)) + if (subtype & 0x40 && !readLclFields(hdl, segmentType == Line + ? _linesLclFlags : _polygonsLclFlags, segmentType, &poly)) return false; quint32 gblFlags = (segmentType == Line) ? _linesGblFlags : _polygonsGblFlags; @@ -654,7 +831,7 @@ bool RGNFile::extPointObjects(Handle &hdl, const SubDiv *subdiv, return false; if (subtype & 0x80 && !readClassFields(hdl, Point, &point, lbl, lblHdl)) return false; - if (subtype & 0x40 && !skipLclFields(hdl, _pointsLclFlags)) + if (subtype & 0x40 && !readLclFields(hdl, _pointsLclFlags, Point, &point)) return false; if (_pointsGblFlags && !skipGblFields(hdl, _pointsGblFlags)) return false; diff --git a/src/map/IMG/rgnfile.h b/src/map/IMG/rgnfile.h index fc739010..47558dc8 100644 --- a/src/map/IMG/rgnfile.h +++ b/src/map/IMG/rgnfile.h @@ -56,7 +56,8 @@ private: bool segments(Handle &hdl, SubDiv *subdiv, SubDiv::Segment seg[5]) const; bool readClassFields(Handle &hdl, SegmentType segmentType, void *object, LBLFile *lbl, Handle &lblHdl) const; - bool skipLclFields(Handle &hdl, const quint32 flags[3]) const; + bool readLclFields(Handle &hdl, const quint32 flags[3], + SegmentType segmentType, void *object) const; bool skipGblFields(Handle &hdl, quint32 flags) const; bool readRasterInfo(Handle &hdl, const LBLFile *lbl, quint32 size, MapData::Poly *poly) const; @@ -70,6 +71,12 @@ private: MapData::Point *point) const; bool readLabel(Handle &hdl, LBLFile *lbl, Handle &lblHdl, quint8 flags, quint32 size, MapData::Point *point) const; + bool readLclNavaid(Handle &hdl, quint32 size, + MapData::Point *point) const; + bool readLclSectors(Handle &hdl, quint32 &size, quint32 flags, + Light &light) const; + bool readLclLights(Handle &hdl, quint32 &size, quint32 lights, + MapData::Point *point) const; HuffmanTable *_huffmanTable; Section _base, _dict, _polygons, _lines, _points; diff --git a/src/map/IMG/style.cpp b/src/map/IMG/style.cpp index 1c215dce..85d7e6a2 100644 --- a/src/map/IMG/style.cpp +++ b/src/map/IMG/style.cpp @@ -323,6 +323,8 @@ void Style::defaultPolygonStyle() Qt::FDiagPattern)); _polygons[0x10503] = Polygon(QBrush(QColor(0xff, 0x40, 0x40), Qt::FDiagPattern)); + _polygons[0x10504] = Polygon(QBrush(QColor(0xff, 0x40, 0x40), + Qt::FDiagPattern)); _polygons[0x10601] = Polygon(QBrush(QColor(0xaa, 0xaa, 0xaa))); _polygons[0x1060a] = Polygon(QBrush(QColor(0xfc, 0xb4, 0xfc))); _polygons[0x10614] = Polygon(QBrush(QColor(0xff, 0xff, 0xff))); @@ -348,7 +350,8 @@ void Style::defaultPolygonStyle() << TYPE(0x0a) << 0x10907 << TYPE(0x0b) << 0x10908 << TYPE(0x0c) << 0x10909 << TYPE(0x26) << TYPE(0x0d) << 0x1090a << TYPE(0x0e) << 0x1090b << TYPE(0x0f) << TYPE(0x10) << TYPE(0x11) << TYPE(0x12) << TYPE(0x19) << 0x1090d - << TYPE(0x13) << 0x10900 << 0x10613 << 0x10409 << 0x10503 << 0x1060a; + << TYPE(0x13) << 0x10900 << 0x10613 << 0x10409 << 0x10503 << 0x10504 + << 0x1060a; } void Style::defaultLineStyle(qreal ratio) @@ -464,7 +467,9 @@ void Style::defaultLineStyle(qreal ratio) _lines[0x10409] = Line(QPen(QColor(0, 0, 0), 1, Qt::DotLine)); _lines[0x10501] = Line(QImage(":/marine/noanchor-line.png")); _lines[0x10503] = Line(QPen(QColor(0xe7, 0x28, 0xe7), 1, Qt::DashLine)); + _lines[0x10504] = Line(QPen(QColor(0xe7, 0x28, 0xe7), 1, Qt::DashLine)); _lines[0x10505] = Line(QImage(":/marine/safety-zone-line.png")); + _lines[0x10506] = Line(QImage(":/marine/nature-reserve-line.png")); _lines[0x10507] = Line(QPen(QColor(0xe7, 0x28, 0xe7), 1, Qt::DashLine)); _lines[0x10601] = Line(QPen(QColor(0, 0, 0), 1, Qt::SolidLine)); _lines[0x10603] = Line(QPen(QColor(0xe7, 0x28, 0xe7), 2, Qt::DashDotLine)); @@ -1377,34 +1382,34 @@ const QFont *Style::font(Style::FontSize size, Style::FontSize defaultSize) cons } } -const QImage *Style::light(Lights::Color color) const +const QImage *Style::light(Light::Color color) const { switch (color) { - case Lights::Red: + case Light::Red: return &_lightRed; - case Lights::Green: + case Light::Green: return &_lightGreen; - case Lights::White: + case Light::White: return &_lightWhite; - case Lights::Yellow: - case Lights::Amber: + case Light::Yellow: + case Light::Amber: return &_lightYellow; default: return &_light; } } -QColor Style::color(Lights::Color c) +QColor Style::color(Light::Color c) { switch (c) { - case Lights::Red: + case Light::Red: return Qt::red; - case Lights::Green: + case Light::Green: return Qt::green; - case Lights::White: + case Light::White: return Qt::white; - case Lights::Yellow: - case Lights::Amber: + case Light::Yellow: + case Light::Amber: return Qt::yellow; default: return Qt::magenta; diff --git a/src/map/IMG/style.h b/src/map/IMG/style.h index a3f21fad..944f5468 100644 --- a/src/map/IMG/style.h +++ b/src/map/IMG/style.h @@ -5,7 +5,7 @@ #include #include #include -#include "lights.h" +#include "light.h" #include "subfile.h" #define TYPE(t) ((t)<<8) @@ -113,7 +113,7 @@ public: const QList &drawOrder() const {return _drawOrder;} const QFont *font(Style::FontSize size, Style::FontSize defaultSize = Style::Normal) const; - const QImage *light(Lights::Color color) const; + const QImage *light(Light::Color color) const; const QPoint &lightOffset() const {return _lightOffset;} static bool isPOI(quint32 type) @@ -157,7 +157,7 @@ public: static bool isMarina(quint32 type) {return type == 0x10703;} - static QColor color(Lights::Color c); + static QColor color(Light::Color c); private: struct Section { diff --git a/src/map/IMG/subfile.cpp b/src/map/IMG/subfile.cpp index 90576774..2c4d71a1 100644 --- a/src/map/IMG/subfile.cpp +++ b/src/map/IMG/subfile.cpp @@ -41,7 +41,7 @@ bool SubFile::seek(Handle &handle, quint32 pos) const return true; } -bool SubFile::readVUInt32(Handle &hdl, quint32 &val) const +bool SubFile::readVUInt32(Handle &hdl, quint32 &val, quint32 *size) const { quint8 bytes, shift, b; @@ -69,6 +69,9 @@ bool SubFile::readVUInt32(Handle &hdl, quint32 &val) const val |= (((quint32)b) << (i * 8)) >> (8 - shift); } + if (size) + *size = 1 + bytes; + return true; } diff --git a/src/map/IMG/subfile.h b/src/map/IMG/subfile.h index 167fee74..5bea6058 100644 --- a/src/map/IMG/subfile.h +++ b/src/map/IMG/subfile.h @@ -169,7 +169,7 @@ public: return true; } - bool readVUInt32(Handle &hdl, quint32 &val) const; + bool readVUInt32(Handle &hdl, quint32 &val, quint32 *size = 0) const; bool readVUInt32(Handle &hdl, quint32 bytes, quint32 &val) const; bool readVBitfield32(Handle &hdl, quint32 &bitfield) const; diff --git a/src/map/downloader.cpp b/src/map/downloader.cpp index 64c13415..dd06ee71 100644 --- a/src/map/downloader.cpp +++ b/src/map/downloader.cpp @@ -27,14 +27,6 @@ #define MAX_REDIRECT_LEVEL 5 #define RETRIES 3 - -#define ATTR_REDIRECT_POLICY QNetworkRequest::RedirectPolicyAttribute -#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) -#define ATTR_HTTP2_ALLOWED QNetworkRequest::HTTP2AllowedAttribute -#else // QT 5.15 -#define ATTR_HTTP2_ALLOWED QNetworkRequest::Http2AllowedAttribute -#endif // QT 5.15 - #define TMP_SUFFIX ".download" // QNetworkReply::errorString() returns bullshit, use our own reporting @@ -129,31 +121,6 @@ Authorization::Authorization(const QString &username, const QString &password) _header = HTTPHeader("Authorization", "Basic " + data); } -#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) -NetworkTimeout::NetworkTimeout(int timeout, QNetworkReply *reply) - : QObject(reply), _timeout(timeout) -{ - connect(reply, &QIODevice::readyRead, this, &NetworkTimeout::reset); - _timer.start(timeout * 1000, this); -} - -void NetworkTimeout::reset() -{ - _timer.start(_timeout * 1000, this); -} - -void NetworkTimeout::timerEvent(QTimerEvent *ev) -{ - if (!_timer.isActive() || ev->timerId() != _timer.timerId()) - return; - QNetworkReply *reply = static_cast(parent()); - if (reply->isRunning()) - reply->abort(); - _timer.stop(); -} -#endif // QT 5.15 - - QNetworkAccessManager *Downloader::_manager = 0; int Downloader::_timeout = 30; bool Downloader::_http2 = true; @@ -176,12 +143,10 @@ bool Downloader::doDownload(const Download &dl, const QList &headers QNetworkRequest request(url); request.setMaximumRedirectsAllowed(MAX_REDIRECT_LEVEL); - request.setAttribute(ATTR_REDIRECT_POLICY, + request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy); - request.setAttribute(ATTR_HTTP2_ALLOWED, QVariant(_http2)); -#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) + request.setAttribute(QNetworkRequest::Http2AllowedAttribute, QVariant(_http2)); request.setTransferTimeout(_timeout * 1000); -#endif // QT 5.15 for (int i = 0; i < headers.size(); i++) { const HTTPHeader &hdr = headers.at(i); @@ -208,9 +173,6 @@ bool Downloader::doDownload(const Download &dl, const QList &headers _currentDownloads.insert(url, file); if (reply->isRunning()) { -#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) - new NetworkTimeout(_timeout, reply); -#endif // QT 5.15 connect(reply, &QIODevice::readyRead, this, &Downloader::emitReadReady); connect(reply, &QNetworkReply::finished, this, &Downloader::emitFinished); } else { diff --git a/src/map/downloader.h b/src/map/downloader.h index dd8a49d0..02ee2a8e 100644 --- a/src/map/downloader.h +++ b/src/map/downloader.h @@ -3,9 +3,6 @@ #include #include -#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) -#include -#endif // QT 5.15 #include #include #include @@ -41,25 +38,6 @@ private: HTTPHeader _header; }; -#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) -class NetworkTimeout : public QObject -{ - Q_OBJECT - -public: - NetworkTimeout(int timeout, QNetworkReply *reply); - -private slots: - void reset(); - -private: - void timerEvent(QTimerEvent *ev); - - QBasicTimer _timer; - int _timeout; -}; -#endif // QT 5.15 - class Downloader : public QObject { Q_OBJECT diff --git a/src/map/mapsforge/rastertile.cpp b/src/map/mapsforge/rastertile.cpp index 5ed23625..fb72e8c4 100644 --- a/src/map/mapsforge/rastertile.cpp +++ b/src/map/mapsforge/rastertile.cpp @@ -39,9 +39,7 @@ static QPainterPath parallelPath(const QPainterPath &p, double dy) QVector u(n); QPainterPath h; -#if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0) h.reserve(p.elementCount()); -#endif // QT 5.13 for (int k = 0; k < n; k++) { qreal c = p.elementAt(k + 1).x - p.elementAt(k).x; @@ -251,12 +249,10 @@ QPainterPath RasterTile::painterPath(const Polygon &polygon, bool curve) const QPainterPath path; if (curve) { -#if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0) int size = 0; for (int i = 0; i < polygon.size(); i++) size += polygon.at(i).size(); path.reserve(size); -#endif // QT 5.13 for (int i = 0; i < polygon.size(); i++) { const QVector &subpath = polygon.at(i); diff --git a/src/map/textpathitem.cpp b/src/map/textpathitem.cpp index 55866117..9b4afa9d 100644 --- a/src/map/textpathitem.cpp +++ b/src/map/textpathitem.cpp @@ -6,16 +6,8 @@ #define MAX_ANGLE 30 #define PADDING 2 -#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) -#define INTERSECTS intersect -#else // QT 5.15 -#define INTERSECTS intersects -#endif // QT 5.15 - - static void swap(const QLineF &line, QPointF *p1, QPointF *p2) { - QPointF lp1(line.p1()); QPointF lp2(line.p2()); @@ -34,10 +26,10 @@ static bool intersection(const QLineF &line, const QRectF &rect, QPointF *p1, { QPointF *p = p1; - if (line.INTERSECTS(QLineF(rect.topLeft(), rect.topRight()), p) + if (line.intersects(QLineF(rect.topLeft(), rect.topRight()), p) == QLineF::BoundedIntersection) p = p2; - if (line.INTERSECTS(QLineF(rect.topLeft(), rect.bottomLeft()), p) + if (line.intersects(QLineF(rect.topLeft(), rect.bottomLeft()), p) == QLineF::BoundedIntersection) { if (p == p2) { swap(line, p1, p2); @@ -45,7 +37,7 @@ static bool intersection(const QLineF &line, const QRectF &rect, QPointF *p1, } p = p2; } - if (line.INTERSECTS(QLineF(rect.bottomRight(), rect.bottomLeft()), p) + if (line.intersects(QLineF(rect.bottomRight(), rect.bottomLeft()), p) == QLineF::BoundedIntersection) { if (p == p2) { swap(line, p1, p2); @@ -53,7 +45,7 @@ static bool intersection(const QLineF &line, const QRectF &rect, QPointF *p1, } p = p2; } - if (line.INTERSECTS(QLineF(rect.bottomRight(), rect.topRight()), p) + if (line.intersects(QLineF(rect.bottomRight(), rect.topRight()), p) == QLineF::BoundedIntersection) { if (p == p2) { swap(line, p1, p2); @@ -68,16 +60,16 @@ static bool intersection(const QLineF &line, const QRectF &rect, QPointF *p1, static bool intersection(const QLineF &line, const QRectF &rect, QPointF *p) { - if (line.INTERSECTS(QLineF(rect.topLeft(), rect.topRight()), p) + if (line.intersects(QLineF(rect.topLeft(), rect.topRight()), p) == QLineF::BoundedIntersection) return true; - if (line.INTERSECTS(QLineF(rect.topLeft(), rect.bottomLeft()), p) + if (line.intersects(QLineF(rect.topLeft(), rect.bottomLeft()), p) == QLineF::BoundedIntersection) return true; - if (line.INTERSECTS(QLineF(rect.bottomRight(), rect.bottomLeft()), p) + if (line.intersects(QLineF(rect.bottomRight(), rect.bottomLeft()), p) == QLineF::BoundedIntersection) return true; - if (line.INTERSECTS(QLineF(rect.bottomRight(), rect.topRight()), p) + if (line.intersects(QLineF(rect.bottomRight(), rect.topRight()), p) == QLineF::BoundedIntersection) return true;