From 933f2c3837414c815ee084f4a8f692d122ffdcae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20T=C5=AFma?= Date: Wed, 4 Aug 2021 08:57:42 +0200 Subject: [PATCH] Rotate JPEG thumbnails acording to EXIF data fixes #385 --- gpxsee.pro | 5 +- src/GUI/areaitem.cpp | 4 +- src/GUI/areaitem.h | 2 +- src/GUI/cadencegraphitem.cpp | 4 +- src/GUI/cadencegraphitem.h | 2 +- src/GUI/elevationgraphitem.cpp | 5 +- src/GUI/elevationgraphitem.h | 2 +- src/GUI/flowlayout.cpp | 181 +++++++++++++++++++++++++++++++ src/GUI/flowlayout.h | 38 +++++++ src/GUI/gearratiographitem.cpp | 4 +- src/GUI/gearratiographitem.h | 2 +- src/GUI/graphicsscene.h | 3 +- src/GUI/graphitem.h | 2 +- src/GUI/heartrategraphitem.cpp | 4 +- src/GUI/heartrategraphitem.h | 2 +- src/GUI/infoitem.cpp | 16 ++- src/GUI/infoitem.h | 1 + src/GUI/mapitem.cpp | 4 +- src/GUI/mapitem.h | 2 +- src/GUI/pathtickitem.h | 5 +- src/GUI/popup.cpp | 114 +++++++++++++------ src/GUI/popup.h | 4 +- src/GUI/powergraphitem.cpp | 4 +- src/GUI/powergraphitem.h | 2 +- src/GUI/routeitem.cpp | 4 +- src/GUI/routeitem.h | 2 +- src/GUI/speedgraphitem.cpp | 4 +- src/GUI/speedgraphitem.h | 2 +- src/GUI/temperaturegraphitem.cpp | 4 +- src/GUI/temperaturegraphitem.h | 2 +- src/GUI/thumbnail.cpp | 43 ++++++++ src/GUI/thumbnail.h | 20 ++++ src/GUI/tooltip.cpp | 52 --------- src/GUI/tooltip.h | 28 ++++- src/GUI/trackitem.cpp | 4 +- src/GUI/trackitem.h | 2 +- src/GUI/waypointitem.cpp | 5 +- src/GUI/waypointitem.h | 2 +- src/common/kv.h | 15 ++- src/data/imageinfo.h | 5 + 40 files changed, 467 insertions(+), 139 deletions(-) create mode 100644 src/GUI/flowlayout.cpp create mode 100644 src/GUI/flowlayout.h create mode 100644 src/GUI/thumbnail.cpp create mode 100644 src/GUI/thumbnail.h delete mode 100644 src/GUI/tooltip.cpp diff --git a/gpxsee.pro b/gpxsee.pro index 9891f84d..21c253e8 100644 --- a/gpxsee.pro +++ b/gpxsee.pro @@ -20,6 +20,7 @@ INCLUDEPATH += ./src HEADERS += src/common/config.h \ src/GUI/axislabelitem.h \ src/GUI/dirselectwidget.h \ + src/GUI/flowlayout.h \ src/GUI/graphicsscene.h \ src/GUI/mapaction.h \ src/GUI/mapitem.h \ @@ -28,6 +29,7 @@ HEADERS += src/common/config.h \ src/GUI/planeitem.h \ src/GUI/poiaction.h \ src/GUI/popup.h \ + src/GUI/thumbnail.h \ src/common/garmin.h \ src/common/coordinates.h \ src/common/range.h \ @@ -230,10 +232,12 @@ HEADERS += src/common/config.h \ SOURCES += src/main.cpp \ src/GUI/axislabelitem.cpp \ src/GUI/dirselectwidget.cpp \ + src/GUI/flowlayout.cpp \ src/GUI/mapitem.cpp \ src/GUI/marginswidget.cpp \ src/GUI/markerinfoitem.cpp \ src/GUI/popup.cpp \ + src/GUI/thumbnail.cpp \ src/common/coordinates.cpp \ src/common/rectc.cpp \ src/common/range.cpp \ @@ -261,7 +265,6 @@ SOURCES += src/main.cpp \ src/GUI/fileselectwidget.cpp \ src/GUI/temperaturegraph.cpp \ src/GUI/trackitem.cpp \ - src/GUI/tooltip.cpp \ src/GUI/routeitem.cpp \ src/GUI/graphitem.cpp \ src/GUI/pathitem.cpp \ diff --git a/src/GUI/areaitem.cpp b/src/GUI/areaitem.cpp index 5479725c..7f1f547c 100644 --- a/src/GUI/areaitem.cpp +++ b/src/GUI/areaitem.cpp @@ -9,7 +9,7 @@ #include "areaitem.h" -QString AreaItem::info() const +ToolTip AreaItem::info() const { ToolTip tt; @@ -19,7 +19,7 @@ QString AreaItem::info() const tt.insert(qApp->translate("PolygonItem", "Description"), _area.description()); - return tt.toString(); + return tt; } AreaItem::AreaItem(const Area &area, Map *map, GraphicsItem *parent) diff --git a/src/GUI/areaitem.h b/src/GUI/areaitem.h index 84837429..bf351ec6 100644 --- a/src/GUI/areaitem.h +++ b/src/GUI/areaitem.h @@ -25,7 +25,7 @@ public: void setStyle(Qt::PenStyle style); void setDigitalZoom(int zoom); - QString info() const; + ToolTip info() const; protected: void hoverEnterEvent(QGraphicsSceneHoverEvent *event); diff --git a/src/GUI/cadencegraphitem.cpp b/src/GUI/cadencegraphitem.cpp index 6416fac5..112b3041 100644 --- a/src/GUI/cadencegraphitem.cpp +++ b/src/GUI/cadencegraphitem.cpp @@ -9,7 +9,7 @@ CadenceGraphItem::CadenceGraphItem(const Graph &graph, GraphType type, { } -QString CadenceGraphItem::info() const +ToolTip CadenceGraphItem::info() const { ToolTip tt; QLocale l(QLocale::system()); @@ -19,5 +19,5 @@ QString CadenceGraphItem::info() const tt.insert(tr("Average"), l.toString(avg(), 'f', 1) + UNIT_SPACE + tr("rpm")); - return tt.toString(); + return tt; } diff --git a/src/GUI/cadencegraphitem.h b/src/GUI/cadencegraphitem.h index 3b8db7de..fe96aabf 100644 --- a/src/GUI/cadencegraphitem.h +++ b/src/GUI/cadencegraphitem.h @@ -11,7 +11,7 @@ public: CadenceGraphItem(const Graph &graph, GraphType type, int width, const QColor &color, QGraphicsItem *parent = 0); - QString info() const; + ToolTip info() const; }; #endif // CADENCEGRAPHITEM_H diff --git a/src/GUI/elevationgraphitem.cpp b/src/GUI/elevationgraphitem.cpp index ee36e4f7..9d0263d6 100644 --- a/src/GUI/elevationgraphitem.cpp +++ b/src/GUI/elevationgraphitem.cpp @@ -26,7 +26,7 @@ ElevationGraphItem::ElevationGraphItem(const Graph &graph, GraphType type, } } -QString ElevationGraphItem::info() const +ToolTip ElevationGraphItem::info() const { ToolTip tt; qreal scale = (_units == Metric) ? 1.0 : M2FT; @@ -42,6 +42,5 @@ QString ElevationGraphItem::info() const tt.insert(tr("Minimum"), l.toString(min() * scale, 'f', 0) + UNIT_SPACE + su); - - return tt.toString(); + return tt; } diff --git a/src/GUI/elevationgraphitem.h b/src/GUI/elevationgraphitem.h index c1928acb..0a15ce23 100644 --- a/src/GUI/elevationgraphitem.h +++ b/src/GUI/elevationgraphitem.h @@ -18,7 +18,7 @@ public: qreal max() const {return _max;} qreal min() const {return _min;} - QString info() const; + ToolTip info() const; private: qreal _ascent, _descent, _min, _max; diff --git a/src/GUI/flowlayout.cpp b/src/GUI/flowlayout.cpp new file mode 100644 index 00000000..39938b8c --- /dev/null +++ b/src/GUI/flowlayout.cpp @@ -0,0 +1,181 @@ +#include +#include "flowlayout.h" + +struct FlowLayoutItem +{ + FlowLayoutItem() : item(0) {} + FlowLayoutItem(QLayoutItem *item, int x, int y) : item(item), pos(x, y) {} + + QLayoutItem *item; + QPoint pos; +}; + +FlowLayout::FlowLayout(QWidget *parent, int margin, int hSpacing, int vSpacing) + : QLayout(parent), _hSpace(hSpacing), _vSpace(vSpacing) +{ + setContentsMargins(margin, margin, margin, margin); +} + +FlowLayout::FlowLayout(int margin, int hSpacing, int vSpacing) + : _hSpace(hSpacing), _vSpace(vSpacing) +{ + setContentsMargins(margin, margin, margin, margin); +} + +FlowLayout::~FlowLayout() +{ + qDeleteAll(_items); +} + +void FlowLayout::addItem(QLayoutItem *item) +{ + _items.append(item); +} + +int FlowLayout::horizontalSpacing() const +{ + return (_hSpace >= 0) + ? _hSpace + : smartSpacing(QStyle::PM_LayoutHorizontalSpacing); +} + +int FlowLayout::verticalSpacing() const +{ + return (_vSpace >= 0) + ? _vSpace + : smartSpacing(QStyle::PM_LayoutVerticalSpacing); +} + +int FlowLayout::count() const +{ + return _items.size(); +} + +QLayoutItem *FlowLayout::itemAt(int index) const +{ + return _items.value(index); +} + +QLayoutItem *FlowLayout::takeAt(int index) +{ + if (index >= 0 && index < _items.size()) + return _items.takeAt(index); + + return 0; +} + +Qt::Orientations FlowLayout::expandingDirections() const +{ + return {}; +} + +bool FlowLayout::hasHeightForWidth() const +{ + return true; +} + +int FlowLayout::heightForWidth(int width) const +{ + int height = doLayout(QRect(0, 0, width, 0), true); + return height; +} + +void FlowLayout::setGeometry(const QRect &rect) +{ + QLayout::setGeometry(rect); + doLayout(rect, false); +} + +QSize FlowLayout::sizeHint() const +{ + return minimumSize(); +} + +QSize FlowLayout::minimumSize() const +{ + QSize size; + + for (int i = 0; i < _items.size(); i++) + size = size.expandedTo(_items.at(i)->minimumSize()); + + const QMargins margins = contentsMargins(); + size += QSize(margins.left() + margins.right(), margins.top() + + margins.bottom()); + + return size; +} + +int FlowLayout::doLayout(const QRect &rect, bool testOnly) const +{ + int left, top, right, bottom; + getContentsMargins(&left, &top, &right, &bottom); + QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom); + int x = effectiveRect.x(); + int y = effectiveRect.y(); + int lineHeight = 0; + QVector> rows; + + if (!_items.isEmpty()) + rows.append(QVector()); + + for (int i = 0; i < _items.size(); i++) { + QLayoutItem *item = _items.at(i); + const QWidget *wid = item->widget(); + int spaceX = horizontalSpacing(); + if (spaceX == -1) + spaceX = wid->style()->layoutSpacing(QSizePolicy::PushButton, + QSizePolicy::PushButton, Qt::Horizontal); + int spaceY = verticalSpacing(); + if (spaceY == -1) + spaceY = wid->style()->layoutSpacing(QSizePolicy::PushButton, + QSizePolicy::PushButton, Qt::Vertical); + + int nextX = x + item->sizeHint().width() + spaceX; + if (nextX - spaceX > effectiveRect.right() && lineHeight > 0) { + x = effectiveRect.x(); + y = y + lineHeight + spaceY; + nextX = x + item->sizeHint().width() + spaceX; + lineHeight = 0; + rows.append(QVector()); + } + + rows.last().append(FlowLayoutItem(item, x, y)); + + x = nextX; + lineHeight = qMax(lineHeight, item->sizeHint().height()); + } + + if (!testOnly) { + for (int i = 0; i < rows.size(); i++) { + const FlowLayoutItem &li = rows.at(i).last(); + int width = li.item->sizeHint().width() + li.pos.x(); + int offset = (rect.width() - width) / 2; + + int height = 0; + for (int j = 0; j < rows.at(i).size(); j++) + height = qMax(rows.at(i).at(j).item->sizeHint().height(), height); + + for (int j = 0; j < rows.at(i).size(); j++) { + QLayoutItem *item = rows.at(i).at(j).item; + const QPoint &p = rows.at(i).at(j).pos; + QSize sh(item->sizeHint()); + item->setGeometry(QRect(QPoint(p.x() + offset, p.y() + height + - sh.height()), sh)); + } + } + } + + return y + lineHeight - rect.y() + bottom; +} + +int FlowLayout::smartSpacing(QStyle::PixelMetric pm) const +{ + QObject *parent = this->parent(); + if (!parent) + return -1; + else if (parent->isWidgetType()) { + QWidget *pw = static_cast(parent); + return pw->style()->pixelMetric(pm, 0, pw); + } else + return static_cast(parent)->spacing(); +} diff --git a/src/GUI/flowlayout.h b/src/GUI/flowlayout.h new file mode 100644 index 00000000..c7bf7aa9 --- /dev/null +++ b/src/GUI/flowlayout.h @@ -0,0 +1,38 @@ +#ifndef FLOWLAYOUT_H +#define FLOWLAYOUT_H + +#include +#include +#include + +class FlowLayout : public QLayout +{ +public: + FlowLayout(QWidget *parent, int margin = -1, int hSpacing = -1, + int vSpacing = -1); + FlowLayout(int margin = -1, int hSpacing = -1, int vSpacing = -1); + ~FlowLayout(); + + void addItem(QLayoutItem *item); + int horizontalSpacing() const; + int verticalSpacing() const; + Qt::Orientations expandingDirections() const; + bool hasHeightForWidth() const; + int heightForWidth(int) const; + int count() const; + QLayoutItem *itemAt(int index) const; + QSize minimumSize() const; + void setGeometry(const QRect &rect); + QSize sizeHint() const; + QLayoutItem *takeAt(int index); + +private: + int doLayout(const QRect &rect, bool testOnly) const; + int smartSpacing(QStyle::PixelMetric pm) const; + + QList _items; + int _hSpace; + int _vSpace; +}; + +#endif // FLOWLAYOUT_H diff --git a/src/GUI/gearratiographitem.cpp b/src/GUI/gearratiographitem.cpp index acf2a844..cb116c0f 100644 --- a/src/GUI/gearratiographitem.cpp +++ b/src/GUI/gearratiographitem.cpp @@ -27,7 +27,7 @@ GearRatioGraphItem::GearRatioGraphItem(const Graph &graph, GraphType type, _top = key; } -QString GearRatioGraphItem::info() const +ToolTip GearRatioGraphItem::info() const { ToolTip tt; QLocale l(QLocale::system()); @@ -36,5 +36,5 @@ QString GearRatioGraphItem::info() const tt.insert(tr("Maximum"), l.toString(max(), 'f', 2)); tt.insert(tr("Most used"), l.toString(top(), 'f', 2)); - return tt.toString(); + return tt; } diff --git a/src/GUI/gearratiographitem.h b/src/GUI/gearratiographitem.h index 0998671d..6f44c463 100644 --- a/src/GUI/gearratiographitem.h +++ b/src/GUI/gearratiographitem.h @@ -15,7 +15,7 @@ public: qreal top() const {return _top;} const QMap &map() const {return _map;} - QString info() const; + ToolTip info() const; private: QMap _map; diff --git a/src/GUI/graphicsscene.h b/src/GUI/graphicsscene.h index e558f0bc..f5b32d67 100644 --- a/src/GUI/graphicsscene.h +++ b/src/GUI/graphicsscene.h @@ -3,13 +3,14 @@ #include #include +#include "tooltip.h" class GraphicsItem : public QGraphicsItem { public: GraphicsItem(QGraphicsItem *parent = 0) : QGraphicsItem(parent) {} - virtual QString info() const = 0; + virtual ToolTip info() const = 0; int type() const {return QGraphicsItem::UserType + 1;} }; diff --git a/src/GUI/graphitem.h b/src/GUI/graphitem.h index 03879bce..2f602ea7 100644 --- a/src/GUI/graphitem.h +++ b/src/GUI/graphitem.h @@ -16,7 +16,7 @@ public: const QColor &color, Qt::PenStyle style, QGraphicsItem *parent = 0); virtual ~GraphItem() {} - virtual QString info() const = 0; + virtual ToolTip info() const = 0; QPainterPath shape() const {return _shape;} QRectF boundingRect() const {return _shape.boundingRect();} diff --git a/src/GUI/heartrategraphitem.cpp b/src/GUI/heartrategraphitem.cpp index 1ff252fd..cde85d37 100644 --- a/src/GUI/heartrategraphitem.cpp +++ b/src/GUI/heartrategraphitem.cpp @@ -9,7 +9,7 @@ HeartRateGraphItem::HeartRateGraphItem(const Graph &graph, GraphType type, { } -QString HeartRateGraphItem::info() const +ToolTip HeartRateGraphItem::info() const { ToolTip tt; QLocale l(QLocale::system()); @@ -19,5 +19,5 @@ QString HeartRateGraphItem::info() const tt.insert(tr("Average"), l.toString(avg(), 'f', 0) + UNIT_SPACE + tr("bpm")); - return tt.toString(); + return tt; } diff --git a/src/GUI/heartrategraphitem.h b/src/GUI/heartrategraphitem.h index cfc60340..35d54641 100644 --- a/src/GUI/heartrategraphitem.h +++ b/src/GUI/heartrategraphitem.h @@ -11,7 +11,7 @@ public: HeartRateGraphItem(const Graph &graph, GraphType type, int width, const QColor &color, QGraphicsItem *parent = 0); - QString info() const; + ToolTip info() const; }; #endif // HEARTRATEGRAPHITEM_H diff --git a/src/GUI/infoitem.cpp b/src/GUI/infoitem.cpp index 2904ee74..4f7bb0eb 100644 --- a/src/GUI/infoitem.cpp +++ b/src/GUI/infoitem.cpp @@ -56,17 +56,25 @@ void InfoItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, //painter->drawRect(boundingRect()); } +int InfoItem::indexOf(const QString &key) const +{ + for (int i = 0; i < _list.size(); i++) + if (_list.at(i).key() == key) + return i; + + return -1; +} + void InfoItem::insert(const QString &key, const QString &value) { - KV kv(key, value); int i; prepareGeometryChange(); - if ((i = _list.indexOf(kv)) < 0) - _list.append(kv); + if ((i = indexOf(key)) < 0) + _list.append(KV(key, value)); else - _list[i] = kv; + _list[i] = KV(key, value); updateBoundingRect(); update(); diff --git a/src/GUI/infoitem.h b/src/GUI/infoitem.h index fbfaf3fd..774a014c 100644 --- a/src/GUI/infoitem.h +++ b/src/GUI/infoitem.h @@ -22,6 +22,7 @@ public: private: void updateBoundingRect(); + int indexOf(const QString &key) const; QList > _list; QRectF _boundingRect; diff --git a/src/GUI/mapitem.cpp b/src/GUI/mapitem.cpp index 57eb55c6..770e19f2 100644 --- a/src/GUI/mapitem.cpp +++ b/src/GUI/mapitem.cpp @@ -69,7 +69,7 @@ static QRectF bbox(const RectC &rect, Map *map, int samples = 100) return prect; } -QString MapItem::info() const +ToolTip MapItem::info() const { ToolTip tt; @@ -78,7 +78,7 @@ QString MapItem::info() const if (!_fileName.isEmpty()) tt.insert(tr("File"), _fileName); - return tt.toString(); + return tt; } MapItem::MapItem(MapAction *action, Map *map, GraphicsItem *parent) diff --git a/src/GUI/mapitem.h b/src/GUI/mapitem.h index d78039f0..8186e0a0 100644 --- a/src/GUI/mapitem.h +++ b/src/GUI/mapitem.h @@ -26,7 +26,7 @@ public: void setStyle(Qt::PenStyle style); void setDigitalZoom(int zoom); - QString info() const; + ToolTip info() const; signals: void triggered(); diff --git a/src/GUI/pathtickitem.h b/src/GUI/pathtickitem.h index 6eb13fe9..d47da2ae 100644 --- a/src/GUI/pathtickitem.h +++ b/src/GUI/pathtickitem.h @@ -19,7 +19,10 @@ public: void setDigitalZoom(int zoom) {setScale(pow(2, -zoom));} int type() const {return parentItem()->type();} - QString info() const {return static_cast(parentItem())->info();} + ToolTip info() const + { + return static_cast(parentItem())->info(); + } static QRect tickRect(int value); diff --git a/src/GUI/popup.cpp b/src/GUI/popup.cpp index 5ae6a553..43c2e69c 100644 --- a/src/GUI/popup.cpp +++ b/src/GUI/popup.cpp @@ -6,25 +6,31 @@ #include #include #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" #include "popup.h" -class PopupLabel : public QLabel +class PopupFrame : public QFrame { public: - PopupLabel(const QString &text, QWidget *parent = 0); - ~PopupLabel(); + PopupFrame(const ToolTip &toolTip, QWidget *parent = 0); + ~PopupFrame(); + + const ToolTip &toolTip() const {return _toolTip;} bool eventFilter(QObject *o, QEvent *ev); void place(const QPoint &pos, QWidget *w); void deleteAfterTimer(); void stopTimer() {_timer.stop();} - static PopupLabel *_instance; + static PopupFrame *_instance; protected: void paintEvent(QPaintEvent *event); @@ -32,14 +38,17 @@ protected: void contextMenuEvent(QContextMenuEvent *) {} private: + void createLayout(const ToolTip &content); + QBasicTimer _timer; + ToolTip _toolTip; }; -PopupLabel *PopupLabel::_instance = 0; +PopupFrame *PopupFrame::_instance = 0; -PopupLabel::PopupLabel(const QString &text, QWidget *parent) - : QLabel(text, parent, Qt::ToolTip | Qt::BypassGraphicsProxyWidget - | Qt::WindowDoesNotAcceptFocus) +PopupFrame::PopupFrame(const ToolTip &toolTip, QWidget *parent) + : QFrame(parent, Qt::ToolTip | Qt::BypassGraphicsProxyWidget + | Qt::WindowDoesNotAcceptFocus), _toolTip(toolTip) { delete _instance; _instance = this; @@ -48,39 +57,73 @@ PopupLabel::PopupLabel(const QString &text, QWidget *parent) setBackgroundRole(QPalette::ToolTipBase); setPalette(QToolTip::palette()); ensurePolished(); - setMargin(1 + style()->pixelMetric(QStyle::PM_ToolTipLabelFrameWidth, 0, - this)); + setFrameStyle(QFrame::NoFrame); - setAlignment(Qt::AlignLeft); - setIndent(1); setWindowOpacity(style()->styleHint(QStyle::SH_ToolTipLabel_Opacity, 0, this) / 255.0); - setTextInteractionFlags(Qt::TextBrowserInteraction); - setOpenExternalLinks(true); - setWordWrap(true); + createLayout(toolTip); setMouseTracking(true); qApp->installEventFilter(this); } -PopupLabel::~PopupLabel() +PopupFrame::~PopupFrame() { _instance = 0; } -void PopupLabel::paintEvent(QPaintEvent *event) +void PopupFrame::createLayout(const ToolTip &content) +{ + QVBoxLayout *layout = new QVBoxLayout(); + int margin = 1 + style()->pixelMetric(QStyle::PM_ToolTipLabelFrameWidth, 0, + this); + layout->setContentsMargins(margin, margin, margin, margin); + layout->setSpacing(0); + + if (!content.images().isEmpty()) { + FlowLayout *imagesLayout = new FlowLayout(0, 2, 2); + int size = qMin(960/content.images().size(), 240); + + for (int i = 0; i < content.images().size(); i++) + imagesLayout->addWidget(new Thumbnail(content.images().at(i), size)); + + layout->addLayout(imagesLayout); + } + + if (!content.list().isEmpty()) { + QString html = ""; + for (int i = 0; i < content.list().count(); i++) + html += ""; + html += "
" + content.list().at(i).key() + + ": " + content.list().at(i).value() + + "
"; + + QLabel *label = new QLabel(html); + label->setAlignment(Qt::AlignLeft); + label->setIndent(1); + label->setTextInteractionFlags(Qt::TextBrowserInteraction); + label->setOpenExternalLinks(true); + label->setWordWrap(true); + + layout->addWidget(label); + } + + setLayout(layout); +} + +void PopupFrame::paintEvent(QPaintEvent *event) { QStylePainter p(this); QStyleOptionFrame opt; opt.initFrom(this); p.drawPrimitive(QStyle::PE_PanelTipLabel, opt); p.end(); - QLabel::paintEvent(event); + QFrame::paintEvent(event); } -void PopupLabel::timerEvent(QTimerEvent *event) +void PopupFrame::timerEvent(QTimerEvent *event) { if (event->timerId() == _timer.timerId()) { _timer.stop(); @@ -88,7 +131,7 @@ void PopupLabel::timerEvent(QTimerEvent *event) } } -bool PopupLabel::eventFilter(QObject *o, QEvent *ev) +bool PopupFrame::eventFilter(QObject *o, QEvent *ev) { Q_UNUSED(o); @@ -123,7 +166,7 @@ bool PopupLabel::eventFilter(QObject *o, QEvent *ev) return false; } -void PopupLabel::place(const QPoint &pos, QWidget *w) +void PopupFrame::place(const QPoint &pos, QWidget *w) { #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) QRect screen = QApplication::desktop()->screenGeometry(w); @@ -148,31 +191,34 @@ void PopupLabel::place(const QPoint &pos, QWidget *w) this->move(p); } -void PopupLabel::deleteAfterTimer() +void PopupFrame::deleteAfterTimer() { if (!_timer.isActive()) _timer.start(300, this); } - -void Popup::show(const QPoint &pos, const QString &text, QWidget *w) +void Popup::show(const QPoint &pos, const ToolTip &toolTip, QWidget *w) { - if (text.isEmpty()) + if (toolTip.isEmpty()) return; - if (PopupLabel::_instance) { - PopupLabel::_instance->stopTimer(); - PopupLabel::_instance->setText(text); + if (PopupFrame::_instance) { + if (toolTip == PopupFrame::_instance->toolTip()) + PopupFrame::_instance->stopTimer(); + else { + delete PopupFrame::_instance; + PopupFrame::_instance = new PopupFrame(toolTip); + } } else - PopupLabel::_instance = new PopupLabel(text); + PopupFrame::_instance = new PopupFrame(toolTip); - PopupLabel::_instance->resize(PopupLabel::_instance->sizeHint()); - PopupLabel::_instance->place(pos, w); - PopupLabel::_instance->showNormal(); + PopupFrame::_instance->resize(PopupFrame::_instance->sizeHint()); + PopupFrame::_instance->place(pos, w); + PopupFrame::_instance->showNormal(); } void Popup::clear() { - if (PopupLabel::_instance) - delete PopupLabel::_instance; + if (PopupFrame::_instance) + delete PopupFrame::_instance; } diff --git a/src/GUI/popup.h b/src/GUI/popup.h index 1d132ebe..d9b94986 100644 --- a/src/GUI/popup.h +++ b/src/GUI/popup.h @@ -2,13 +2,13 @@ #define POPUP_H class QPoint; -class QString; class QWidget; +class ToolTip; class Popup { public: - static void show(const QPoint &pos, const QString &text, QWidget *w); + static void show(const QPoint &pos, const ToolTip &toolTip, QWidget *w); static void clear(); }; diff --git a/src/GUI/powergraphitem.cpp b/src/GUI/powergraphitem.cpp index 0333170c..efc22c71 100644 --- a/src/GUI/powergraphitem.cpp +++ b/src/GUI/powergraphitem.cpp @@ -9,7 +9,7 @@ PowerGraphItem::PowerGraphItem(const Graph &graph, GraphType type, int width, { } -QString PowerGraphItem::info() const +ToolTip PowerGraphItem::info() const { ToolTip tt; QLocale l(QLocale::system()); @@ -19,5 +19,5 @@ QString PowerGraphItem::info() const tt.insert(tr("Average"), l.toString(avg(), 'f', 1) + UNIT_SPACE + tr("W")); - return tt.toString(); + return tt; } diff --git a/src/GUI/powergraphitem.h b/src/GUI/powergraphitem.h index 6d2e6800..e30e8853 100644 --- a/src/GUI/powergraphitem.h +++ b/src/GUI/powergraphitem.h @@ -11,7 +11,7 @@ public: PowerGraphItem(const Graph &graph, GraphType type, int width, const QColor &color, QGraphicsItem *parent = 0); - QString info() const; + ToolTip info() const; }; #endif // POWERGRAPHITEM_H diff --git a/src/GUI/routeitem.cpp b/src/GUI/routeitem.cpp index b13b8565..f7f21ed3 100644 --- a/src/GUI/routeitem.cpp +++ b/src/GUI/routeitem.cpp @@ -8,7 +8,7 @@ #include "routeitem.h" -QString RouteItem::info() const +ToolTip RouteItem::info() const { ToolTip tt; @@ -32,7 +32,7 @@ QString RouteItem::info() const tt.insert(tr("Links"), links); } - return tt.toString(); + return tt; } RouteItem::RouteItem(const Route &route, Map *map, QGraphicsItem *parent) diff --git a/src/GUI/routeitem.h b/src/GUI/routeitem.h index 6143de5d..16d72a9e 100644 --- a/src/GUI/routeitem.h +++ b/src/GUI/routeitem.h @@ -21,7 +21,7 @@ public: void showWaypoints(bool show); void showWaypointLabels(bool show); - QString info() const; + ToolTip info() const; QDateTime date() const {return QDateTime();} private: diff --git a/src/GUI/speedgraphitem.cpp b/src/GUI/speedgraphitem.cpp index 57a682b1..268505f4 100644 --- a/src/GUI/speedgraphitem.cpp +++ b/src/GUI/speedgraphitem.cpp @@ -15,7 +15,7 @@ SpeedGraphItem::SpeedGraphItem(const Graph &graph, GraphType type, int width, _mavg = graph.last().last().s() / movingTime; } -QString SpeedGraphItem::info() const +ToolTip SpeedGraphItem::info() const { ToolTip tt; qreal scale = (_units == Imperial) ? MS2MIH : (_units == Nautical) @@ -34,7 +34,7 @@ QString SpeedGraphItem::info() const ? avg() * scale : mavg() * scale, 'f', 1) + UNIT_SPACE + su); tt.insert(tr("Pace"), pace + UNIT_SPACE + pu); - return tt.toString(); + return tt; } void SpeedGraphItem::setTimeType(TimeType type) diff --git a/src/GUI/speedgraphitem.h b/src/GUI/speedgraphitem.h index 649ca41b..d5bbfb64 100644 --- a/src/GUI/speedgraphitem.h +++ b/src/GUI/speedgraphitem.h @@ -17,7 +17,7 @@ public: qreal mavg() const {return _mavg;} qreal max() const {return _max;} - QString info() const; + ToolTip info() const; void setTimeType(TimeType type); diff --git a/src/GUI/temperaturegraphitem.cpp b/src/GUI/temperaturegraphitem.cpp index 4844cf1d..2a76b346 100644 --- a/src/GUI/temperaturegraphitem.cpp +++ b/src/GUI/temperaturegraphitem.cpp @@ -12,7 +12,7 @@ TemperatureGraphItem::TemperatureGraphItem(const Graph &graph, GraphType type, _avg = GraphItem::avg(); } -QString TemperatureGraphItem::info() const +ToolTip TemperatureGraphItem::info() const { ToolTip tt; qreal scale = (_units == Metric) ? 1.0 : C2FS; @@ -28,5 +28,5 @@ QString TemperatureGraphItem::info() const tt.insert(tr("Minimum"), l.toString(min() * scale + offset, 'f', 1) + UNIT_SPACE + su); - return tt.toString(); + return tt; } diff --git a/src/GUI/temperaturegraphitem.h b/src/GUI/temperaturegraphitem.h index 516af4d2..1600cd84 100644 --- a/src/GUI/temperaturegraphitem.h +++ b/src/GUI/temperaturegraphitem.h @@ -15,7 +15,7 @@ public: qreal min() const {return _min;} qreal avg() const {return _avg;} - QString info() const; + ToolTip info() const; private: qreal _min, _max, _avg; diff --git a/src/GUI/thumbnail.cpp b/src/GUI/thumbnail.cpp new file mode 100644 index 00000000..03f1bc77 --- /dev/null +++ b/src/GUI/thumbnail.cpp @@ -0,0 +1,43 @@ +#include +#include +#include +#include +#include "data/imageinfo.h" +#include "thumbnail.h" + +static QSize thumbnailSize(const ImageInfo &img, int limit) +{ + int width, height; + if (img.size().width() > img.size().height()) { + width = qMin(img.size().width(), limit); + qreal ratio = img.size().width() / (qreal)img.size().height(); + height = (int)(width / ratio); + } else { + height = qMin(img.size().height(), limit); + qreal ratio = img.size().height() / (qreal)img.size().width(); + width = (int)(height / ratio); + } + + return QSize(width, height); +} + +Thumbnail::Thumbnail(const ImageInfo &img, int size, QWidget *parent) + : QLabel(parent) +{ + QImageReader reader(img.path()); + reader.setAutoTransform(true); + reader.setScaledSize(thumbnailSize(img, size)); + setPixmap(QPixmap::fromImage(reader.read())); + + setCursor(Qt::PointingHandCursor); + + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + + _path = QFileInfo(img.path()).absoluteFilePath(); +} + +void Thumbnail::mousePressEvent(QMouseEvent *event) +{ + if (event->button() == Qt::LeftButton) + QDesktopServices::openUrl(QUrl::fromLocalFile(_path)); +} diff --git a/src/GUI/thumbnail.h b/src/GUI/thumbnail.h new file mode 100644 index 00000000..89197402 --- /dev/null +++ b/src/GUI/thumbnail.h @@ -0,0 +1,20 @@ +#ifndef THUMBNAIL_H +#define THUMBNAIL_H + +#include + +class ImageInfo; + +class Thumbnail : public QLabel +{ +public: + Thumbnail(const ImageInfo &img, int size, QWidget *parent = 0); + +protected: + void mousePressEvent(QMouseEvent *event); + +private: + QString _path; +}; + +#endif // THUMBNAIL_H diff --git a/src/GUI/tooltip.cpp b/src/GUI/tooltip.cpp deleted file mode 100644 index a8464a6a..00000000 --- a/src/GUI/tooltip.cpp +++ /dev/null @@ -1,52 +0,0 @@ -#include "popup.h" -#include "tooltip.h" - -static QSize thumbnailSize(const ImageInfo &img, int limit) -{ - int width, height; - if (img.size().width() > img.size().height()) { - width = qMin(img.size().width(), limit); - qreal ratio = img.size().width() / (qreal)img.size().height(); - height = (int)(width / ratio); - } else { - height = qMin(img.size().height(), limit); - qreal ratio = img.size().height() / (qreal)img.size().width(); - width = (int)(height / ratio); - } - - return QSize(width, height); -} - -void ToolTip::insert(const QString &key, const QString &value) -{ - _list.append(KV(key, value)); -} - -QString ToolTip::toString() const -{ - QString html; - - if (_images.size()) { - html = "
"; - for (int i = 0; i < _images.size(); i++) { - const ImageInfo &img = _images.at(i); - QSize size(thumbnailSize(img, qMin(960/_images.size(), 240))); - - html += QString("" - "") - .arg(img.path(), QString::number(size.width()), - QString::number(size.height())); - } - html += "
"; - } - - if (!_list.isEmpty()) { - html += ""; - for (int i = 0; i < _list.count(); i++) - html += ""; - html += "
" + _list.at(i).key() - + ": " + _list.at(i).value() + "
"; - } - - return html; -} diff --git a/src/GUI/tooltip.h b/src/GUI/tooltip.h index 2748fe02..bcec0d8a 100644 --- a/src/GUI/tooltip.h +++ b/src/GUI/tooltip.h @@ -10,9 +10,31 @@ class ToolTip { public: - void insert(const QString &key, const QString &value); - void setImages(const QVector &images) {_images = images;} - QString toString() const; + const QList > &list() const {return _list;} + const QVector &images() const {return _images;} + + bool isEmpty() const + { + return _list.isEmpty() && _images.isEmpty(); + } + + bool operator==(const ToolTip &other) const + { + return (_list == other._list && _images == other._images); + } + bool operator!=(const ToolTip &other) const + { + return (_list != other._list || _images != other._images); + } + + void insert(const QString &key, const QString &value) + { + _list.append(KV(key, value)); + } + void setImages(const QVector &images) + { + _images = images; + } private: QList > _list; diff --git a/src/GUI/trackitem.cpp b/src/GUI/trackitem.cpp index 95f6ac89..81cbce4a 100644 --- a/src/GUI/trackitem.cpp +++ b/src/GUI/trackitem.cpp @@ -6,7 +6,7 @@ #include "trackitem.h" -QString TrackItem::info() const +ToolTip TrackItem::info() const { ToolTip tt; QLocale l; @@ -37,7 +37,7 @@ QString TrackItem::info() const tt.insert(tr("Links"), links); } - return tt.toString(); + return tt; } TrackItem::TrackItem(const Track &track, Map *map, QGraphicsItem *parent) diff --git a/src/GUI/trackitem.h b/src/GUI/trackitem.h index 34f94f55..b6026750 100644 --- a/src/GUI/trackitem.h +++ b/src/GUI/trackitem.h @@ -15,7 +15,7 @@ class TrackItem : public PathItem public: TrackItem(const Track &track, Map *map, QGraphicsItem *parent = 0); - QString info() const; + ToolTip info() const; QDateTime date() const {return _date;} private: diff --git a/src/GUI/waypointitem.cpp b/src/GUI/waypointitem.cpp index d0669a48..23edff1b 100644 --- a/src/GUI/waypointitem.cpp +++ b/src/GUI/waypointitem.cpp @@ -3,7 +3,6 @@ #include #include #include "font.h" -#include "tooltip.h" #include "popup.h" #include "waypointitem.h" @@ -18,7 +17,7 @@ Units WaypointItem::_units = Metric; CoordinatesFormat WaypointItem::_format = DecimalDegrees; QTimeZone WaypointItem::_timeZone = QTimeZone::utc(); -QString WaypointItem::info() const +ToolTip WaypointItem::info() const { ToolTip tt; QLocale l; @@ -66,7 +65,7 @@ QString WaypointItem::info() const } tt.setImages(_waypoint.images()); - return tt.toString(); + return tt; } WaypointItem::WaypointItem(const Waypoint &waypoint, Map *map, diff --git a/src/GUI/waypointitem.h b/src/GUI/waypointitem.h index 546ef17a..68bec205 100644 --- a/src/GUI/waypointitem.h +++ b/src/GUI/waypointitem.h @@ -30,7 +30,7 @@ public: void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); - QString info() const; + ToolTip info() const; static void setUnits(Units units) {_units = units;} static void setCoordinatesFormat(CoordinatesFormat format) diff --git a/src/common/kv.h b/src/common/kv.h index fe6fed4a..71a42f80 100644 --- a/src/common/kv.h +++ b/src/common/kv.h @@ -9,8 +9,19 @@ public: const KEY &key() const {return _key;} const VALUE &value() const {return _value;} - bool operator==(const KV &other) const {return _key == other._key;} - bool operator<(const KV &other) const {return _key < other._key;} + bool operator==(const KV &other) const + { + return (_key == other._key && _value == other._value); + } + bool operator<(const KV &other) const + { + if (_key < other._key) + return true; + else if (_key > other._key) + return false; + else + return _value < other._value; + } private: KEY _key; diff --git a/src/data/imageinfo.h b/src/data/imageinfo.h index 3de68769..f7658762 100644 --- a/src/data/imageinfo.h +++ b/src/data/imageinfo.h @@ -16,6 +16,11 @@ public: bool isValid() const {return _size.isValid() && !_path.isEmpty();} + bool operator==(const ImageInfo &other) const + { + return (_path == other._path); + } + private: QString _path; QSize _size;