344 lines
9.5 KiB
C++
344 lines
9.5 KiB
C++
/*
|
|
* Squawk messenger.
|
|
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "preview.h"
|
|
|
|
|
|
constexpr int margin = 6;
|
|
constexpr int maxAttachmentHeight = 500;
|
|
|
|
QFont Preview::font;
|
|
QFontMetrics Preview::metrics(Preview::font);
|
|
|
|
Preview::Preview(const QString& pPath, const QSize& pMaxSize, const QPoint& pos, QWidget* pParent):
|
|
info(Shared::Global::getFileInfo(pPath)),
|
|
maxSize(pMaxSize),
|
|
actualSize(constrainAttachSize(info.size, maxSize)),
|
|
cachedLabelSize(0, 0),
|
|
position(pos),
|
|
widget(0),
|
|
label(0),
|
|
parent(pParent),
|
|
movie(0),
|
|
fileReachable(true),
|
|
actualPreview(false)
|
|
{
|
|
|
|
initializeElements();
|
|
if (fileReachable) {
|
|
positionElements();
|
|
}
|
|
}
|
|
|
|
void Preview::initializeFont(const QFont& newFont)
|
|
{
|
|
font = newFont;
|
|
font.setBold(true);
|
|
metrics = QFontMetrics(font);
|
|
}
|
|
|
|
Preview::~Preview()
|
|
{
|
|
clean();
|
|
}
|
|
|
|
void Preview::clean()
|
|
{
|
|
if (fileReachable) {
|
|
if (info.preview == Shared::Global::FileInfo::Preview::animation) {
|
|
delete movie;
|
|
}
|
|
delete widget;
|
|
if (!actualPreview) {
|
|
delete label;
|
|
} else {
|
|
actualPreview = false;
|
|
}
|
|
} else {
|
|
fileReachable = true;
|
|
}
|
|
}
|
|
|
|
void Preview::actualize(const QString& newPath, const QSize& newSize, const QPoint& newPoint)
|
|
{
|
|
bool positionChanged = false;
|
|
bool sizeChanged = false;
|
|
bool maxSizeChanged = false;
|
|
|
|
if (maxSize != newSize) {
|
|
maxSize = newSize;
|
|
maxSizeChanged = true;
|
|
QSize ns = constrainAttachSize(info.size, maxSize);
|
|
if (actualSize != ns) {
|
|
sizeChanged = true;
|
|
actualSize = ns;
|
|
}
|
|
}
|
|
if (position != newPoint) {
|
|
position = newPoint;
|
|
positionChanged = true;
|
|
}
|
|
|
|
if (!setPath(newPath) && fileReachable) {
|
|
if (sizeChanged) {
|
|
applyNewSize();
|
|
if (maxSizeChanged && !actualPreview) {
|
|
applyNewMaxSize();
|
|
}
|
|
} else if (maxSizeChanged) {
|
|
applyNewMaxSize();
|
|
}
|
|
if (positionChanged || !actualPreview) {
|
|
positionElements();
|
|
}
|
|
}
|
|
}
|
|
|
|
void Preview::setSize(const QSize& newSize)
|
|
{
|
|
bool sizeChanged = false;
|
|
bool maxSizeChanged = false;
|
|
|
|
if (maxSize != newSize) {
|
|
maxSize = newSize;
|
|
maxSizeChanged = true;
|
|
QSize ns = constrainAttachSize(info.size, maxSize);
|
|
if (actualSize != ns) {
|
|
sizeChanged = true;
|
|
actualSize = ns;
|
|
}
|
|
}
|
|
|
|
if (fileReachable) {
|
|
if (sizeChanged) {
|
|
applyNewSize();
|
|
}
|
|
if (maxSizeChanged || !actualPreview) {
|
|
applyNewMaxSize();
|
|
}
|
|
}
|
|
}
|
|
|
|
void Preview::applyNewSize()
|
|
{
|
|
switch (info.preview) {
|
|
case Shared::Global::FileInfo::Preview::picture: {
|
|
QImageReader img(info.path);
|
|
if (!img.canRead()) {
|
|
delete widget;
|
|
fileReachable = false;
|
|
} else {
|
|
img.setScaledSize(actualSize);
|
|
widget->resize(actualSize);
|
|
widget->setPixmap(QPixmap::fromImage(img.read()));
|
|
}
|
|
}
|
|
break;
|
|
case Shared::Global::FileInfo::Preview::animation:{
|
|
movie->setScaledSize(actualSize);
|
|
widget->resize(actualSize);
|
|
}
|
|
break;
|
|
default: {
|
|
QIcon icon = QIcon::fromTheme(info.mime.iconName());
|
|
if (icon.isNull()) {
|
|
icon.addFile(QString::fromUtf8(":/images/fallback/dark/big/mail-attachment.svg"), QSize(), QIcon::Normal, QIcon::Off);
|
|
}
|
|
widget->setPixmap(icon.pixmap(actualSize));
|
|
widget->resize(actualSize);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Preview::applyNewMaxSize()
|
|
{
|
|
switch (info.preview) {
|
|
case Shared::Global::FileInfo::Preview::picture:
|
|
case Shared::Global::FileInfo::Preview::animation:
|
|
break;
|
|
default: {
|
|
int labelWidth = maxSize.width() - actualSize.width() - margin;
|
|
QString elidedName = metrics.elidedText(info.name, Qt::ElideMiddle, labelWidth);
|
|
cachedLabelSize = metrics.boundingRect(elidedName).size();
|
|
label->setText(elidedName);
|
|
label->resize(cachedLabelSize);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
QSize Preview::size() const
|
|
{
|
|
if (actualPreview) {
|
|
return actualSize;
|
|
} else {
|
|
return QSize(actualSize.width() + margin + cachedLabelSize.width(), actualSize.height());
|
|
}
|
|
}
|
|
|
|
bool Preview::isFileReachable() const
|
|
{
|
|
return fileReachable;
|
|
}
|
|
|
|
void Preview::setPosition(const QPoint& newPoint)
|
|
{
|
|
if (position != newPoint) {
|
|
position = newPoint;
|
|
if (fileReachable) {
|
|
positionElements();
|
|
}
|
|
}
|
|
}
|
|
|
|
bool Preview::setPath(const QString& newPath)
|
|
{
|
|
if (info.path != newPath) {
|
|
info = Shared::Global::getFileInfo(newPath);
|
|
actualSize = constrainAttachSize(info.size, maxSize);
|
|
clean();
|
|
initializeElements();
|
|
if (fileReachable) {
|
|
positionElements();
|
|
}
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void Preview::initializeElements()
|
|
{
|
|
switch (info.preview) {
|
|
case Shared::Global::FileInfo::Preview::picture: {
|
|
QImageReader img(info.path);
|
|
if (!img.canRead()) {
|
|
fileReachable = false;
|
|
} else {
|
|
actualPreview = true;
|
|
img.setScaledSize(actualSize);
|
|
widget = new QLabel(parent);
|
|
widget->setPixmap(QPixmap::fromImage(img.read()));
|
|
widget->show();
|
|
}
|
|
}
|
|
break;
|
|
case Shared::Global::FileInfo::Preview::animation:{
|
|
movie = new QMovie(info.path);
|
|
QObject::connect(movie, &QMovie::error,
|
|
std::bind(&Preview::handleQMovieError, this, std::placeholders::_1)
|
|
);
|
|
if (!movie->isValid()) {
|
|
fileReachable = false;
|
|
delete movie;
|
|
} else {
|
|
actualPreview = true;
|
|
movie->setScaledSize(actualSize);
|
|
widget = new QLabel(parent);
|
|
widget->setMovie(movie);
|
|
movie->start();
|
|
widget->show();
|
|
}
|
|
}
|
|
break;
|
|
default: {
|
|
QIcon icon = QIcon::fromTheme(info.mime.iconName());
|
|
if (icon.isNull()) {
|
|
icon.addFile(QString::fromUtf8(":/images/fallback/dark/big/mail-attachment.svg"), QSize(), QIcon::Normal, QIcon::Off);
|
|
}
|
|
|
|
widget = new QLabel(parent);
|
|
widget->setPixmap(icon.pixmap(actualSize));
|
|
widget->show();
|
|
|
|
label = new QLabel(parent);
|
|
label->setFont(font);
|
|
int labelWidth = maxSize.width() - actualSize.width() - margin;
|
|
QString elidedName = metrics.elidedText(info.name, Qt::ElideMiddle, labelWidth);
|
|
cachedLabelSize = metrics.boundingRect(elidedName).size();
|
|
label->setText(elidedName);
|
|
label->show();
|
|
}
|
|
}
|
|
}
|
|
|
|
void Preview::positionElements()
|
|
{
|
|
int start = position.x();
|
|
widget->move(start, position.y());
|
|
if (!actualPreview) {
|
|
int x = start + actualSize.width() + margin;
|
|
int y = position.y() + (actualSize.height() - cachedLabelSize.height()) / 2;
|
|
label->move(x, y);
|
|
}
|
|
}
|
|
|
|
bool Preview::canVisualize(const Shared::Global::FileInfo& info)
|
|
{
|
|
switch (info.preview) {
|
|
case Shared::Global::FileInfo::Preview::picture: {
|
|
QImageReader img(info.path);
|
|
return img.canRead();
|
|
}
|
|
break;
|
|
case Shared::Global::FileInfo::Preview::animation:{
|
|
QMovie movie(info.path);
|
|
return movie.isValid();
|
|
}
|
|
break;
|
|
default: {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
QSize Preview::calculateAttachSize(const QString& path, const QRect& bounds)
|
|
{
|
|
Shared::Global::FileInfo info = Shared::Global::getFileInfo(path);
|
|
QSize constrained = constrainAttachSize(info.size, bounds.size());
|
|
if (!canVisualize(info)) {
|
|
int maxLabelWidth = bounds.width() - info.size.width() - margin;
|
|
QString elidedName = metrics.elidedText(info.name, Qt::ElideMiddle, maxLabelWidth);
|
|
int labelWidth = metrics.boundingRect(elidedName).size().width();
|
|
constrained.rwidth() += margin + labelWidth;
|
|
}
|
|
return constrained;
|
|
}
|
|
|
|
QSize Preview::constrainAttachSize(QSize src, QSize bounds)
|
|
{
|
|
if (bounds.height() > maxAttachmentHeight) {
|
|
bounds.setHeight(maxAttachmentHeight);
|
|
}
|
|
|
|
if (src.width() > bounds.width() || src.height() > bounds.height()) {
|
|
src.scale(bounds, Qt::KeepAspectRatio);
|
|
}
|
|
|
|
return src;
|
|
}
|
|
|
|
void Preview::handleQMovieError(QImageReader::ImageReaderError error)
|
|
{
|
|
if (error == QImageReader::FileNotFoundError) {
|
|
fileReachable = false;
|
|
movie->deleteLater();
|
|
widget->deleteLater();
|
|
}
|
|
}
|