2009-04-09 18:55:47 +00:00
|
|
|
/*
|
2015-06-10 21:27:11 +00:00
|
|
|
* This file Copyright (C) 2009-2015 Mnemosyne LLC
|
2009-04-09 18:55:47 +00:00
|
|
|
*
|
2014-12-21 23:49:39 +00:00
|
|
|
* It may be used under the GNU GPL versions 2 or 3
|
2014-01-19 01:09:44 +00:00
|
|
|
* or any future license endorsed by Mnemosyne LLC.
|
2009-04-09 18:55:47 +00:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2014-07-04 00:00:07 +00:00
|
|
|
#ifdef _WIN32
|
2017-04-19 12:04:45 +00:00
|
|
|
#include <windows.h>
|
|
|
|
#include <shellapi.h>
|
2013-09-08 18:53:11 +00:00
|
|
|
#endif
|
|
|
|
|
2015-06-15 21:07:46 +00:00
|
|
|
#include <QAbstractItemView>
|
2009-04-09 18:55:47 +00:00
|
|
|
#include <QApplication>
|
2015-06-10 21:27:11 +00:00
|
|
|
#include <QColor>
|
2009-04-09 18:55:47 +00:00
|
|
|
#include <QDataStream>
|
|
|
|
#include <QFile>
|
2010-06-04 01:00:27 +00:00
|
|
|
#include <QFileDialog>
|
2009-04-09 18:55:47 +00:00
|
|
|
#include <QFileInfo>
|
2015-06-15 21:07:46 +00:00
|
|
|
#include <QHeaderView>
|
2015-06-12 22:12:12 +00:00
|
|
|
#include <QIcon>
|
2010-06-04 01:00:27 +00:00
|
|
|
#include <QInputDialog>
|
2017-02-11 10:44:34 +00:00
|
|
|
#include <QMimeDatabase>
|
|
|
|
#include <QMimeType>
|
2009-04-09 18:55:47 +00:00
|
|
|
#include <QObject>
|
2013-09-08 18:53:11 +00:00
|
|
|
#include <QPixmapCache>
|
2009-04-09 18:55:47 +00:00
|
|
|
#include <QSet>
|
|
|
|
#include <QStyle>
|
|
|
|
|
2017-02-11 17:48:13 +00:00
|
|
|
#ifdef _WIN32
|
|
|
|
#include <QtWin>
|
|
|
|
#endif
|
|
|
|
|
2009-04-09 18:55:47 +00:00
|
|
|
#include <libtransmission/transmission.h>
|
2010-06-22 04:34:16 +00:00
|
|
|
#include <libtransmission/utils.h> // tr_formatter
|
2009-04-09 18:55:47 +00:00
|
|
|
|
2015-06-10 21:27:11 +00:00
|
|
|
#include "Utils.h"
|
2009-04-09 18:55:47 +00:00
|
|
|
|
2010-07-03 00:25:22 +00:00
|
|
|
/***
|
|
|
|
****
|
|
|
|
***/
|
|
|
|
|
2013-09-08 18:53:11 +00:00
|
|
|
namespace
|
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2016-10-03 19:22:25 +00:00
|
|
|
#ifdef _WIN32
|
2019-07-14 21:30:14 +00:00
|
|
|
|
2017-04-20 16:02:19 +00:00
|
|
|
void addAssociatedFileIcon(QFileInfo const& fileInfo, UINT iconSize, QIcon& icon)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
QString const pixmapCacheKey = QLatin1String("tr_file_ext_") + QString::number(iconSize) + QLatin1Char('_') +
|
|
|
|
fileInfo.suffix();
|
2013-09-14 22:45:04 +00:00
|
|
|
|
2013-09-08 18:53:11 +00:00
|
|
|
QPixmap pixmap;
|
2017-04-19 12:04:45 +00:00
|
|
|
|
|
|
|
if (!QPixmapCache::find(pixmapCacheKey, &pixmap))
|
|
|
|
{
|
2017-04-20 16:02:19 +00:00
|
|
|
QString const filename = fileInfo.fileName();
|
2013-09-08 18:53:11 +00:00
|
|
|
|
|
|
|
SHFILEINFO shellFileInfo;
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2017-04-20 16:02:19 +00:00
|
|
|
if (::SHGetFileInfoW(reinterpret_cast<wchar_t const*>(filename.utf16()), FILE_ATTRIBUTE_NORMAL, &shellFileInfo,
|
2017-04-19 12:04:45 +00:00
|
|
|
sizeof(shellFileInfo), SHGFI_ICON | iconSize | SHGFI_USEFILEATTRIBUTES) != 0)
|
|
|
|
{
|
2017-04-30 09:29:58 +00:00
|
|
|
if (shellFileInfo.hIcon != nullptr)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
pixmap = QtWin::fromHICON(shellFileInfo.hIcon);
|
|
|
|
::DestroyIcon(shellFileInfo.hIcon);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
QPixmapCache::insert(pixmapCacheKey, pixmap);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!pixmap.isNull())
|
|
|
|
{
|
|
|
|
icon.addPixmap(pixmap);
|
|
|
|
}
|
|
|
|
}
|
2019-07-14 21:30:14 +00:00
|
|
|
|
2013-09-08 18:53:11 +00:00
|
|
|
#endif
|
|
|
|
|
2017-04-20 16:02:19 +00:00
|
|
|
bool isSlashChar(QChar const& c)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
return c == QLatin1Char('/') || c == QLatin1Char('\\');
|
|
|
|
}
|
|
|
|
|
2016-10-03 19:22:25 +00:00
|
|
|
} // namespace
|
|
|
|
|
2017-04-20 16:02:19 +00:00
|
|
|
QIcon Utils::guessMimeIcon(QString const& filename)
|
2009-04-09 18:55:47 +00:00
|
|
|
{
|
2017-04-20 16:02:19 +00:00
|
|
|
static QIcon const fallback = qApp->style()->standardIcon(QStyle::SP_FileIcon);
|
2015-01-01 02:28:18 +00:00
|
|
|
|
2014-07-04 00:00:07 +00:00
|
|
|
#ifdef _WIN32
|
2015-01-01 02:28:18 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
QIcon icon;
|
2013-09-08 18:53:11 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
if (!filename.isEmpty())
|
2013-09-08 18:53:11 +00:00
|
|
|
{
|
2017-04-20 16:02:19 +00:00
|
|
|
QFileInfo const fileInfo(filename);
|
2013-09-08 18:53:11 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
addAssociatedFileIcon(fileInfo, SHGFI_SMALLICON, icon);
|
|
|
|
addAssociatedFileIcon(fileInfo, 0, icon);
|
|
|
|
addAssociatedFileIcon(fileInfo, SHGFI_LARGEICON, icon);
|
2013-09-08 18:53:11 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
if (!icon.isNull())
|
|
|
|
{
|
|
|
|
return icon;
|
|
|
|
}
|
2015-01-01 02:28:18 +00:00
|
|
|
|
2017-02-11 17:48:13 +00:00
|
|
|
#endif
|
2015-01-01 02:28:18 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
QMimeDatabase mimeDb;
|
|
|
|
QMimeType mimeType = mimeDb.mimeTypeForFile(filename, QMimeDatabase::MatchExtension);
|
|
|
|
|
|
|
|
if (mimeType.isValid())
|
|
|
|
{
|
|
|
|
return QIcon::fromTheme(mimeType.iconName(), QIcon::fromTheme(mimeType.genericIconName(), fallback));
|
|
|
|
}
|
2013-09-08 18:53:11 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
return fallback;
|
2009-04-09 18:55:47 +00:00
|
|
|
}
|
2012-08-19 16:12:20 +00:00
|
|
|
|
2017-04-20 16:02:19 +00:00
|
|
|
QIcon Utils::getIconFromIndex(QModelIndex const& index)
|
2017-02-07 22:06:28 +00:00
|
|
|
{
|
2017-04-20 16:02:19 +00:00
|
|
|
QVariant const variant = index.data(Qt::DecorationRole);
|
2017-04-19 12:04:45 +00:00
|
|
|
|
|
|
|
switch (variant.type())
|
2017-02-07 22:06:28 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
case QVariant::Icon:
|
|
|
|
return qvariant_cast<QIcon>(variant);
|
2017-02-07 22:06:28 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
case QVariant::Pixmap:
|
|
|
|
return QIcon(qvariant_cast<QPixmap>(variant));
|
2017-02-07 22:06:28 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
default:
|
|
|
|
return QIcon();
|
2017-02-07 22:06:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-20 16:02:19 +00:00
|
|
|
bool Utils::isValidUtf8(char const* s)
|
2012-08-19 16:12:20 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
int n; // number of bytes in a UTF-8 sequence
|
2012-08-19 16:12:20 +00:00
|
|
|
|
2017-04-30 16:25:26 +00:00
|
|
|
for (char const* c = s; *c != '\0'; c += n)
|
2012-08-19 16:12:20 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
if ((*c & 0x80) == 0x00)
|
|
|
|
{
|
|
|
|
n = 1; // ASCII
|
|
|
|
}
|
|
|
|
else if ((*c & 0xc0) == 0x80)
|
|
|
|
{
|
|
|
|
return false; // not valid
|
|
|
|
}
|
|
|
|
else if ((*c & 0xe0) == 0xc0)
|
|
|
|
{
|
|
|
|
n = 2;
|
|
|
|
}
|
|
|
|
else if ((*c & 0xf0) == 0xe0)
|
|
|
|
{
|
|
|
|
n = 3;
|
|
|
|
}
|
|
|
|
else if ((*c & 0xf8) == 0xf0)
|
|
|
|
{
|
|
|
|
n = 4;
|
|
|
|
}
|
|
|
|
else if ((*c & 0xfc) == 0xf8)
|
|
|
|
{
|
|
|
|
n = 5;
|
|
|
|
}
|
|
|
|
else if ((*c & 0xfe) == 0xfc)
|
|
|
|
{
|
|
|
|
n = 6;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int m = 1; m < n; m++)
|
|
|
|
{
|
|
|
|
if ((c[m] & 0xc0) != 0x80)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2015-01-02 11:15:31 +00:00
|
|
|
}
|
2013-09-14 22:45:04 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
return true;
|
2012-08-19 16:12:20 +00:00
|
|
|
}
|
2013-09-08 19:59:47 +00:00
|
|
|
|
2017-04-20 16:02:19 +00:00
|
|
|
QString Utils::removeTrailingDirSeparator(QString const& path)
|
2013-09-08 19:59:47 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
int i = path.size();
|
|
|
|
|
|
|
|
while (i > 1 && isSlashChar(path[i - 1]))
|
|
|
|
{
|
|
|
|
--i;
|
|
|
|
}
|
|
|
|
|
|
|
|
return path.left(i);
|
2013-09-08 19:59:47 +00:00
|
|
|
}
|
2015-06-10 21:27:11 +00:00
|
|
|
|
2017-04-20 16:02:19 +00:00
|
|
|
int Utils::measureViewItem(QAbstractItemView* view, QString const& text)
|
2015-06-15 21:07:46 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
QStyleOptionViewItem option;
|
|
|
|
option.initFrom(view);
|
|
|
|
option.features = QStyleOptionViewItem::HasDisplay;
|
|
|
|
option.text = text;
|
|
|
|
option.textElideMode = Qt::ElideNone;
|
|
|
|
option.font = view->font();
|
|
|
|
|
|
|
|
return view->style()->sizeFromContents(QStyle::CT_ItemViewItem, &option, QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX), view).
|
2019-02-10 11:05:16 +00:00
|
|
|
width();
|
2015-06-15 21:07:46 +00:00
|
|
|
}
|
|
|
|
|
2017-04-20 16:02:19 +00:00
|
|
|
int Utils::measureHeaderItem(QHeaderView* view, QString const& text)
|
2015-06-15 21:07:46 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
QStyleOptionHeader option;
|
|
|
|
option.initFrom(view);
|
|
|
|
option.text = text;
|
|
|
|
option.sortIndicator = view->isSortIndicatorShown() ? QStyleOptionHeader::SortDown : QStyleOptionHeader::None;
|
2015-06-15 21:07:46 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
return view->style()->sizeFromContents(QStyle::CT_HeaderSection, &option, QSize(), view).width();
|
2015-06-15 21:07:46 +00:00
|
|
|
}
|
|
|
|
|
2017-04-20 16:02:19 +00:00
|
|
|
QColor Utils::getFadedColor(QColor const& color)
|
2015-06-10 21:27:11 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
QColor fadedColor(color);
|
|
|
|
fadedColor.setAlpha(128);
|
|
|
|
return fadedColor;
|
2015-06-10 21:27:11 +00:00
|
|
|
}
|