1
0
mirror of https://github.com/tumic0/GPXSee.git synced 2024-11-28 05:34:47 +01:00

Rotate JPEG thumbnails acording to EXIF data

fixes #385
This commit is contained in:
Martin Tůma 2021-08-04 08:57:42 +02:00
parent 6e4cc406ab
commit 933f2c3837
40 changed files with 467 additions and 139 deletions

View File

@ -20,6 +20,7 @@ INCLUDEPATH += ./src
HEADERS += src/common/config.h \ HEADERS += src/common/config.h \
src/GUI/axislabelitem.h \ src/GUI/axislabelitem.h \
src/GUI/dirselectwidget.h \ src/GUI/dirselectwidget.h \
src/GUI/flowlayout.h \
src/GUI/graphicsscene.h \ src/GUI/graphicsscene.h \
src/GUI/mapaction.h \ src/GUI/mapaction.h \
src/GUI/mapitem.h \ src/GUI/mapitem.h \
@ -28,6 +29,7 @@ HEADERS += src/common/config.h \
src/GUI/planeitem.h \ src/GUI/planeitem.h \
src/GUI/poiaction.h \ src/GUI/poiaction.h \
src/GUI/popup.h \ src/GUI/popup.h \
src/GUI/thumbnail.h \
src/common/garmin.h \ src/common/garmin.h \
src/common/coordinates.h \ src/common/coordinates.h \
src/common/range.h \ src/common/range.h \
@ -230,10 +232,12 @@ HEADERS += src/common/config.h \
SOURCES += src/main.cpp \ SOURCES += src/main.cpp \
src/GUI/axislabelitem.cpp \ src/GUI/axislabelitem.cpp \
src/GUI/dirselectwidget.cpp \ src/GUI/dirselectwidget.cpp \
src/GUI/flowlayout.cpp \
src/GUI/mapitem.cpp \ src/GUI/mapitem.cpp \
src/GUI/marginswidget.cpp \ src/GUI/marginswidget.cpp \
src/GUI/markerinfoitem.cpp \ src/GUI/markerinfoitem.cpp \
src/GUI/popup.cpp \ src/GUI/popup.cpp \
src/GUI/thumbnail.cpp \
src/common/coordinates.cpp \ src/common/coordinates.cpp \
src/common/rectc.cpp \ src/common/rectc.cpp \
src/common/range.cpp \ src/common/range.cpp \
@ -261,7 +265,6 @@ SOURCES += src/main.cpp \
src/GUI/fileselectwidget.cpp \ src/GUI/fileselectwidget.cpp \
src/GUI/temperaturegraph.cpp \ src/GUI/temperaturegraph.cpp \
src/GUI/trackitem.cpp \ src/GUI/trackitem.cpp \
src/GUI/tooltip.cpp \
src/GUI/routeitem.cpp \ src/GUI/routeitem.cpp \
src/GUI/graphitem.cpp \ src/GUI/graphitem.cpp \
src/GUI/pathitem.cpp \ src/GUI/pathitem.cpp \

View File

@ -9,7 +9,7 @@
#include "areaitem.h" #include "areaitem.h"
QString AreaItem::info() const ToolTip AreaItem::info() const
{ {
ToolTip tt; ToolTip tt;
@ -19,7 +19,7 @@ QString AreaItem::info() const
tt.insert(qApp->translate("PolygonItem", "Description"), tt.insert(qApp->translate("PolygonItem", "Description"),
_area.description()); _area.description());
return tt.toString(); return tt;
} }
AreaItem::AreaItem(const Area &area, Map *map, GraphicsItem *parent) AreaItem::AreaItem(const Area &area, Map *map, GraphicsItem *parent)

View File

@ -25,7 +25,7 @@ public:
void setStyle(Qt::PenStyle style); void setStyle(Qt::PenStyle style);
void setDigitalZoom(int zoom); void setDigitalZoom(int zoom);
QString info() const; ToolTip info() const;
protected: protected:
void hoverEnterEvent(QGraphicsSceneHoverEvent *event); void hoverEnterEvent(QGraphicsSceneHoverEvent *event);

View File

@ -9,7 +9,7 @@ CadenceGraphItem::CadenceGraphItem(const Graph &graph, GraphType type,
{ {
} }
QString CadenceGraphItem::info() const ToolTip CadenceGraphItem::info() const
{ {
ToolTip tt; ToolTip tt;
QLocale l(QLocale::system()); QLocale l(QLocale::system());
@ -19,5 +19,5 @@ QString CadenceGraphItem::info() const
tt.insert(tr("Average"), l.toString(avg(), 'f', 1) tt.insert(tr("Average"), l.toString(avg(), 'f', 1)
+ UNIT_SPACE + tr("rpm")); + UNIT_SPACE + tr("rpm"));
return tt.toString(); return tt;
} }

View File

@ -11,7 +11,7 @@ public:
CadenceGraphItem(const Graph &graph, GraphType type, int width, CadenceGraphItem(const Graph &graph, GraphType type, int width,
const QColor &color, QGraphicsItem *parent = 0); const QColor &color, QGraphicsItem *parent = 0);
QString info() const; ToolTip info() const;
}; };
#endif // CADENCEGRAPHITEM_H #endif // CADENCEGRAPHITEM_H

View File

@ -26,7 +26,7 @@ ElevationGraphItem::ElevationGraphItem(const Graph &graph, GraphType type,
} }
} }
QString ElevationGraphItem::info() const ToolTip ElevationGraphItem::info() const
{ {
ToolTip tt; ToolTip tt;
qreal scale = (_units == Metric) ? 1.0 : M2FT; 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) tt.insert(tr("Minimum"), l.toString(min() * scale, 'f', 0)
+ UNIT_SPACE + su); + UNIT_SPACE + su);
return tt;
return tt.toString();
} }

View File

@ -18,7 +18,7 @@ public:
qreal max() const {return _max;} qreal max() const {return _max;}
qreal min() const {return _min;} qreal min() const {return _min;}
QString info() const; ToolTip info() const;
private: private:
qreal _ascent, _descent, _min, _max; qreal _ascent, _descent, _min, _max;

181
src/GUI/flowlayout.cpp Normal file
View File

@ -0,0 +1,181 @@
#include <QtWidgets>
#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<QVector<FlowLayoutItem>> rows;
if (!_items.isEmpty())
rows.append(QVector<FlowLayoutItem>());
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<FlowLayoutItem>());
}
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<QWidget *>(parent);
return pw->style()->pixelMetric(pm, 0, pw);
} else
return static_cast<QLayout *>(parent)->spacing();
}

38
src/GUI/flowlayout.h Normal file
View File

@ -0,0 +1,38 @@
#ifndef FLOWLAYOUT_H
#define FLOWLAYOUT_H
#include <QLayout>
#include <QRect>
#include <QStyle>
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<QLayoutItem *> _items;
int _hSpace;
int _vSpace;
};
#endif // FLOWLAYOUT_H

View File

@ -27,7 +27,7 @@ GearRatioGraphItem::GearRatioGraphItem(const Graph &graph, GraphType type,
_top = key; _top = key;
} }
QString GearRatioGraphItem::info() const ToolTip GearRatioGraphItem::info() const
{ {
ToolTip tt; ToolTip tt;
QLocale l(QLocale::system()); QLocale l(QLocale::system());
@ -36,5 +36,5 @@ QString GearRatioGraphItem::info() const
tt.insert(tr("Maximum"), l.toString(max(), 'f', 2)); tt.insert(tr("Maximum"), l.toString(max(), 'f', 2));
tt.insert(tr("Most used"), l.toString(top(), 'f', 2)); tt.insert(tr("Most used"), l.toString(top(), 'f', 2));
return tt.toString(); return tt;
} }

View File

@ -15,7 +15,7 @@ public:
qreal top() const {return _top;} qreal top() const {return _top;}
const QMap<qreal, qreal> &map() const {return _map;} const QMap<qreal, qreal> &map() const {return _map;}
QString info() const; ToolTip info() const;
private: private:
QMap<qreal, qreal> _map; QMap<qreal, qreal> _map;

View File

@ -3,13 +3,14 @@
#include <QGraphicsScene> #include <QGraphicsScene>
#include <QGraphicsItem> #include <QGraphicsItem>
#include "tooltip.h"
class GraphicsItem : public QGraphicsItem class GraphicsItem : public QGraphicsItem
{ {
public: public:
GraphicsItem(QGraphicsItem *parent = 0) : QGraphicsItem(parent) {} GraphicsItem(QGraphicsItem *parent = 0) : QGraphicsItem(parent) {}
virtual QString info() const = 0; virtual ToolTip info() const = 0;
int type() const {return QGraphicsItem::UserType + 1;} int type() const {return QGraphicsItem::UserType + 1;}
}; };

View File

@ -16,7 +16,7 @@ public:
const QColor &color, Qt::PenStyle style, QGraphicsItem *parent = 0); const QColor &color, Qt::PenStyle style, QGraphicsItem *parent = 0);
virtual ~GraphItem() {} virtual ~GraphItem() {}
virtual QString info() const = 0; virtual ToolTip info() const = 0;
QPainterPath shape() const {return _shape;} QPainterPath shape() const {return _shape;}
QRectF boundingRect() const {return _shape.boundingRect();} QRectF boundingRect() const {return _shape.boundingRect();}

View File

@ -9,7 +9,7 @@ HeartRateGraphItem::HeartRateGraphItem(const Graph &graph, GraphType type,
{ {
} }
QString HeartRateGraphItem::info() const ToolTip HeartRateGraphItem::info() const
{ {
ToolTip tt; ToolTip tt;
QLocale l(QLocale::system()); QLocale l(QLocale::system());
@ -19,5 +19,5 @@ QString HeartRateGraphItem::info() const
tt.insert(tr("Average"), l.toString(avg(), 'f', 0) tt.insert(tr("Average"), l.toString(avg(), 'f', 0)
+ UNIT_SPACE + tr("bpm")); + UNIT_SPACE + tr("bpm"));
return tt.toString(); return tt;
} }

View File

@ -11,7 +11,7 @@ public:
HeartRateGraphItem(const Graph &graph, GraphType type, int width, HeartRateGraphItem(const Graph &graph, GraphType type, int width,
const QColor &color, QGraphicsItem *parent = 0); const QColor &color, QGraphicsItem *parent = 0);
QString info() const; ToolTip info() const;
}; };
#endif // HEARTRATEGRAPHITEM_H #endif // HEARTRATEGRAPHITEM_H

View File

@ -56,17 +56,25 @@ void InfoItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
//painter->drawRect(boundingRect()); //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) void InfoItem::insert(const QString &key, const QString &value)
{ {
KV<QString, QString> kv(key, value);
int i; int i;
prepareGeometryChange(); prepareGeometryChange();
if ((i = _list.indexOf(kv)) < 0) if ((i = indexOf(key)) < 0)
_list.append(kv); _list.append(KV<QString, QString>(key, value));
else else
_list[i] = kv; _list[i] = KV<QString, QString>(key, value);
updateBoundingRect(); updateBoundingRect();
update(); update();

View File

@ -22,6 +22,7 @@ public:
private: private:
void updateBoundingRect(); void updateBoundingRect();
int indexOf(const QString &key) const;
QList<KV<QString, QString> > _list; QList<KV<QString, QString> > _list;
QRectF _boundingRect; QRectF _boundingRect;

View File

@ -69,7 +69,7 @@ static QRectF bbox(const RectC &rect, Map *map, int samples = 100)
return prect; return prect;
} }
QString MapItem::info() const ToolTip MapItem::info() const
{ {
ToolTip tt; ToolTip tt;
@ -78,7 +78,7 @@ QString MapItem::info() const
if (!_fileName.isEmpty()) if (!_fileName.isEmpty())
tt.insert(tr("File"), _fileName); tt.insert(tr("File"), _fileName);
return tt.toString(); return tt;
} }
MapItem::MapItem(MapAction *action, Map *map, GraphicsItem *parent) MapItem::MapItem(MapAction *action, Map *map, GraphicsItem *parent)

View File

@ -26,7 +26,7 @@ public:
void setStyle(Qt::PenStyle style); void setStyle(Qt::PenStyle style);
void setDigitalZoom(int zoom); void setDigitalZoom(int zoom);
QString info() const; ToolTip info() const;
signals: signals:
void triggered(); void triggered();

View File

@ -19,7 +19,10 @@ public:
void setDigitalZoom(int zoom) {setScale(pow(2, -zoom));} void setDigitalZoom(int zoom) {setScale(pow(2, -zoom));}
int type() const {return parentItem()->type();} int type() const {return parentItem()->type();}
QString info() const {return static_cast<GraphicsItem*>(parentItem())->info();} ToolTip info() const
{
return static_cast<GraphicsItem*>(parentItem())->info();
}
static QRect tickRect(int value); static QRect tickRect(int value);

View File

@ -6,25 +6,31 @@
#include <QMouseEvent> #include <QMouseEvent>
#include <QBasicTimer> #include <QBasicTimer>
#include <QScreen> #include <QScreen>
#include <QVBoxLayout>
#include <QApplication> #include <QApplication>
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
#include <QDesktopWidget> #include <QDesktopWidget>
#endif // QT 5.15 #endif // QT 5.15
#include "tooltip.h"
#include "thumbnail.h"
#include "flowlayout.h"
#include "popup.h" #include "popup.h"
class PopupLabel : public QLabel class PopupFrame : public QFrame
{ {
public: public:
PopupLabel(const QString &text, QWidget *parent = 0); PopupFrame(const ToolTip &toolTip, QWidget *parent = 0);
~PopupLabel(); ~PopupFrame();
const ToolTip &toolTip() const {return _toolTip;}
bool eventFilter(QObject *o, QEvent *ev); bool eventFilter(QObject *o, QEvent *ev);
void place(const QPoint &pos, QWidget *w); void place(const QPoint &pos, QWidget *w);
void deleteAfterTimer(); void deleteAfterTimer();
void stopTimer() {_timer.stop();} void stopTimer() {_timer.stop();}
static PopupLabel *_instance; static PopupFrame *_instance;
protected: protected:
void paintEvent(QPaintEvent *event); void paintEvent(QPaintEvent *event);
@ -32,14 +38,17 @@ protected:
void contextMenuEvent(QContextMenuEvent *) {} void contextMenuEvent(QContextMenuEvent *) {}
private: private:
void createLayout(const ToolTip &content);
QBasicTimer _timer; QBasicTimer _timer;
ToolTip _toolTip;
}; };
PopupLabel *PopupLabel::_instance = 0; PopupFrame *PopupFrame::_instance = 0;
PopupLabel::PopupLabel(const QString &text, QWidget *parent) PopupFrame::PopupFrame(const ToolTip &toolTip, QWidget *parent)
: QLabel(text, parent, Qt::ToolTip | Qt::BypassGraphicsProxyWidget : QFrame(parent, Qt::ToolTip | Qt::BypassGraphicsProxyWidget
| Qt::WindowDoesNotAcceptFocus) | Qt::WindowDoesNotAcceptFocus), _toolTip(toolTip)
{ {
delete _instance; delete _instance;
_instance = this; _instance = this;
@ -48,39 +57,73 @@ PopupLabel::PopupLabel(const QString &text, QWidget *parent)
setBackgroundRole(QPalette::ToolTipBase); setBackgroundRole(QPalette::ToolTipBase);
setPalette(QToolTip::palette()); setPalette(QToolTip::palette());
ensurePolished(); ensurePolished();
setMargin(1 + style()->pixelMetric(QStyle::PM_ToolTipLabelFrameWidth, 0,
this));
setFrameStyle(QFrame::NoFrame); setFrameStyle(QFrame::NoFrame);
setAlignment(Qt::AlignLeft);
setIndent(1);
setWindowOpacity(style()->styleHint(QStyle::SH_ToolTipLabel_Opacity, 0, setWindowOpacity(style()->styleHint(QStyle::SH_ToolTipLabel_Opacity, 0,
this) / 255.0); this) / 255.0);
setTextInteractionFlags(Qt::TextBrowserInteraction); createLayout(toolTip);
setOpenExternalLinks(true);
setWordWrap(true);
setMouseTracking(true); setMouseTracking(true);
qApp->installEventFilter(this); qApp->installEventFilter(this);
} }
PopupLabel::~PopupLabel() PopupFrame::~PopupFrame()
{ {
_instance = 0; _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 = "<table>";
for (int i = 0; i < content.list().count(); i++)
html += "<tr><td align=\"right\"><b>" + content.list().at(i).key()
+ ":&nbsp;</b></td><td>" + content.list().at(i).value()
+ "</td></tr>";
html += "</table>";
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); QStylePainter p(this);
QStyleOptionFrame opt; QStyleOptionFrame opt;
opt.initFrom(this); opt.initFrom(this);
p.drawPrimitive(QStyle::PE_PanelTipLabel, opt); p.drawPrimitive(QStyle::PE_PanelTipLabel, opt);
p.end(); p.end();
QLabel::paintEvent(event); QFrame::paintEvent(event);
} }
void PopupLabel::timerEvent(QTimerEvent *event) void PopupFrame::timerEvent(QTimerEvent *event)
{ {
if (event->timerId() == _timer.timerId()) { if (event->timerId() == _timer.timerId()) {
_timer.stop(); _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); Q_UNUSED(o);
@ -123,7 +166,7 @@ bool PopupLabel::eventFilter(QObject *o, QEvent *ev)
return false; 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) #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
QRect screen = QApplication::desktop()->screenGeometry(w); QRect screen = QApplication::desktop()->screenGeometry(w);
@ -148,31 +191,34 @@ void PopupLabel::place(const QPoint &pos, QWidget *w)
this->move(p); this->move(p);
} }
void PopupLabel::deleteAfterTimer() void PopupFrame::deleteAfterTimer()
{ {
if (!_timer.isActive()) if (!_timer.isActive())
_timer.start(300, this); _timer.start(300, this);
} }
void Popup::show(const QPoint &pos, const ToolTip &toolTip, QWidget *w)
void Popup::show(const QPoint &pos, const QString &text, QWidget *w)
{ {
if (text.isEmpty()) if (toolTip.isEmpty())
return; return;
if (PopupLabel::_instance) { if (PopupFrame::_instance) {
PopupLabel::_instance->stopTimer(); if (toolTip == PopupFrame::_instance->toolTip())
PopupLabel::_instance->setText(text); PopupFrame::_instance->stopTimer();
else {
delete PopupFrame::_instance;
PopupFrame::_instance = new PopupFrame(toolTip);
}
} else } else
PopupLabel::_instance = new PopupLabel(text); PopupFrame::_instance = new PopupFrame(toolTip);
PopupLabel::_instance->resize(PopupLabel::_instance->sizeHint()); PopupFrame::_instance->resize(PopupFrame::_instance->sizeHint());
PopupLabel::_instance->place(pos, w); PopupFrame::_instance->place(pos, w);
PopupLabel::_instance->showNormal(); PopupFrame::_instance->showNormal();
} }
void Popup::clear() void Popup::clear()
{ {
if (PopupLabel::_instance) if (PopupFrame::_instance)
delete PopupLabel::_instance; delete PopupFrame::_instance;
} }

View File

@ -2,13 +2,13 @@
#define POPUP_H #define POPUP_H
class QPoint; class QPoint;
class QString;
class QWidget; class QWidget;
class ToolTip;
class Popup class Popup
{ {
public: 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(); static void clear();
}; };

View File

@ -9,7 +9,7 @@ PowerGraphItem::PowerGraphItem(const Graph &graph, GraphType type, int width,
{ {
} }
QString PowerGraphItem::info() const ToolTip PowerGraphItem::info() const
{ {
ToolTip tt; ToolTip tt;
QLocale l(QLocale::system()); QLocale l(QLocale::system());
@ -19,5 +19,5 @@ QString PowerGraphItem::info() const
tt.insert(tr("Average"), l.toString(avg(), 'f', 1) tt.insert(tr("Average"), l.toString(avg(), 'f', 1)
+ UNIT_SPACE + tr("W")); + UNIT_SPACE + tr("W"));
return tt.toString(); return tt;
} }

View File

@ -11,7 +11,7 @@ public:
PowerGraphItem(const Graph &graph, GraphType type, int width, PowerGraphItem(const Graph &graph, GraphType type, int width,
const QColor &color, QGraphicsItem *parent = 0); const QColor &color, QGraphicsItem *parent = 0);
QString info() const; ToolTip info() const;
}; };
#endif // POWERGRAPHITEM_H #endif // POWERGRAPHITEM_H

View File

@ -8,7 +8,7 @@
#include "routeitem.h" #include "routeitem.h"
QString RouteItem::info() const ToolTip RouteItem::info() const
{ {
ToolTip tt; ToolTip tt;
@ -32,7 +32,7 @@ QString RouteItem::info() const
tt.insert(tr("Links"), links); tt.insert(tr("Links"), links);
} }
return tt.toString(); return tt;
} }
RouteItem::RouteItem(const Route &route, Map *map, QGraphicsItem *parent) RouteItem::RouteItem(const Route &route, Map *map, QGraphicsItem *parent)

View File

@ -21,7 +21,7 @@ public:
void showWaypoints(bool show); void showWaypoints(bool show);
void showWaypointLabels(bool show); void showWaypointLabels(bool show);
QString info() const; ToolTip info() const;
QDateTime date() const {return QDateTime();} QDateTime date() const {return QDateTime();}
private: private:

View File

@ -15,7 +15,7 @@ SpeedGraphItem::SpeedGraphItem(const Graph &graph, GraphType type, int width,
_mavg = graph.last().last().s() / movingTime; _mavg = graph.last().last().s() / movingTime;
} }
QString SpeedGraphItem::info() const ToolTip SpeedGraphItem::info() const
{ {
ToolTip tt; ToolTip tt;
qreal scale = (_units == Imperial) ? MS2MIH : (_units == Nautical) qreal scale = (_units == Imperial) ? MS2MIH : (_units == Nautical)
@ -34,7 +34,7 @@ QString SpeedGraphItem::info() const
? avg() * scale : mavg() * scale, 'f', 1) + UNIT_SPACE + su); ? avg() * scale : mavg() * scale, 'f', 1) + UNIT_SPACE + su);
tt.insert(tr("Pace"), pace + UNIT_SPACE + pu); tt.insert(tr("Pace"), pace + UNIT_SPACE + pu);
return tt.toString(); return tt;
} }
void SpeedGraphItem::setTimeType(TimeType type) void SpeedGraphItem::setTimeType(TimeType type)

View File

@ -17,7 +17,7 @@ public:
qreal mavg() const {return _mavg;} qreal mavg() const {return _mavg;}
qreal max() const {return _max;} qreal max() const {return _max;}
QString info() const; ToolTip info() const;
void setTimeType(TimeType type); void setTimeType(TimeType type);

View File

@ -12,7 +12,7 @@ TemperatureGraphItem::TemperatureGraphItem(const Graph &graph, GraphType type,
_avg = GraphItem::avg(); _avg = GraphItem::avg();
} }
QString TemperatureGraphItem::info() const ToolTip TemperatureGraphItem::info() const
{ {
ToolTip tt; ToolTip tt;
qreal scale = (_units == Metric) ? 1.0 : C2FS; 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) tt.insert(tr("Minimum"), l.toString(min() * scale + offset, 'f', 1)
+ UNIT_SPACE + su); + UNIT_SPACE + su);
return tt.toString(); return tt;
} }

View File

@ -15,7 +15,7 @@ public:
qreal min() const {return _min;} qreal min() const {return _min;}
qreal avg() const {return _avg;} qreal avg() const {return _avg;}
QString info() const; ToolTip info() const;
private: private:
qreal _min, _max, _avg; qreal _min, _max, _avg;

43
src/GUI/thumbnail.cpp Normal file
View File

@ -0,0 +1,43 @@
#include <QImageReader>
#include <QDesktopServices>
#include <QFileInfo>
#include <QMouseEvent>
#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));
}

20
src/GUI/thumbnail.h Normal file
View File

@ -0,0 +1,20 @@
#ifndef THUMBNAIL_H
#define THUMBNAIL_H
#include <QLabel>
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

View File

@ -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<QString, QString>(key, value));
}
QString ToolTip::toString() const
{
QString html;
if (_images.size()) {
html = "<div align=\"center\">";
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("<a href=\"file:%0\">"
"<img src=\"%0\" width=\"%1\" height=\"%2\"/></a>")
.arg(img.path(), QString::number(size.width()),
QString::number(size.height()));
}
html += "</div>";
}
if (!_list.isEmpty()) {
html += "<table>";
for (int i = 0; i < _list.count(); i++)
html += "<tr><td align=\"right\"><b>" + _list.at(i).key()
+ ":&nbsp;</b></td><td>" + _list.at(i).value() + "</td></tr>";
html += "</table>";
}
return html;
}

View File

@ -10,9 +10,31 @@
class ToolTip class ToolTip
{ {
public: public:
void insert(const QString &key, const QString &value); const QList<KV<QString, QString> > &list() const {return _list;}
void setImages(const QVector<ImageInfo> &images) {_images = images;} const QVector<ImageInfo> &images() const {return _images;}
QString toString() const;
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<QString, QString>(key, value));
}
void setImages(const QVector<ImageInfo> &images)
{
_images = images;
}
private: private:
QList<KV<QString, QString> > _list; QList<KV<QString, QString> > _list;

View File

@ -6,7 +6,7 @@
#include "trackitem.h" #include "trackitem.h"
QString TrackItem::info() const ToolTip TrackItem::info() const
{ {
ToolTip tt; ToolTip tt;
QLocale l; QLocale l;
@ -37,7 +37,7 @@ QString TrackItem::info() const
tt.insert(tr("Links"), links); tt.insert(tr("Links"), links);
} }
return tt.toString(); return tt;
} }
TrackItem::TrackItem(const Track &track, Map *map, QGraphicsItem *parent) TrackItem::TrackItem(const Track &track, Map *map, QGraphicsItem *parent)

View File

@ -15,7 +15,7 @@ class TrackItem : public PathItem
public: public:
TrackItem(const Track &track, Map *map, QGraphicsItem *parent = 0); TrackItem(const Track &track, Map *map, QGraphicsItem *parent = 0);
QString info() const; ToolTip info() const;
QDateTime date() const {return _date;} QDateTime date() const {return _date;}
private: private:

View File

@ -3,7 +3,6 @@
#include <QGraphicsSceneMouseEvent> #include <QGraphicsSceneMouseEvent>
#include <QLabel> #include <QLabel>
#include "font.h" #include "font.h"
#include "tooltip.h"
#include "popup.h" #include "popup.h"
#include "waypointitem.h" #include "waypointitem.h"
@ -18,7 +17,7 @@ Units WaypointItem::_units = Metric;
CoordinatesFormat WaypointItem::_format = DecimalDegrees; CoordinatesFormat WaypointItem::_format = DecimalDegrees;
QTimeZone WaypointItem::_timeZone = QTimeZone::utc(); QTimeZone WaypointItem::_timeZone = QTimeZone::utc();
QString WaypointItem::info() const ToolTip WaypointItem::info() const
{ {
ToolTip tt; ToolTip tt;
QLocale l; QLocale l;
@ -66,7 +65,7 @@ QString WaypointItem::info() const
} }
tt.setImages(_waypoint.images()); tt.setImages(_waypoint.images());
return tt.toString(); return tt;
} }
WaypointItem::WaypointItem(const Waypoint &waypoint, Map *map, WaypointItem::WaypointItem(const Waypoint &waypoint, Map *map,

View File

@ -30,7 +30,7 @@ public:
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
QWidget *widget); QWidget *widget);
QString info() const; ToolTip info() const;
static void setUnits(Units units) {_units = units;} static void setUnits(Units units) {_units = units;}
static void setCoordinatesFormat(CoordinatesFormat format) static void setCoordinatesFormat(CoordinatesFormat format)

View File

@ -9,8 +9,19 @@ public:
const KEY &key() const {return _key;} const KEY &key() const {return _key;}
const VALUE &value() const {return _value;} const VALUE &value() const {return _value;}
bool operator==(const KV &other) const {return _key == other._key;} bool operator==(const KV &other) const
bool operator<(const KV &other) const {return _key < other._key;} {
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: private:
KEY _key; KEY _key;

View File

@ -16,6 +16,11 @@ public:
bool isValid() const {return _size.isValid() && !_path.isEmpty();} bool isValid() const {return _size.isValid() && !_path.isEmpty();}
bool operator==(const ImageInfo &other) const
{
return (_path == other._path);
}
private: private:
QString _path; QString _path;
QSize _size; QSize _size;