diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 10f3dd6fb..1b45cefe7 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -45,7 +45,6 @@ fspathedit.h fspathedit_p.h guiiconprovider.h hidabletabwidget.h -ico.h loglistwidget.h mainwindow.h messageboxraised.h @@ -87,7 +86,6 @@ executionlog.cpp fspathedit.cpp fspathedit_p.cpp guiiconprovider.cpp -ico.cpp loglistwidget.cpp mainwindow.cpp messageboxraised.cpp diff --git a/src/gui/gui.pri b/src/gui/gui.pri index f9f3fdf65..46abdac2e 100644 --- a/src/gui/gui.pri +++ b/src/gui/gui.pri @@ -19,7 +19,6 @@ HEADERS += \ $$PWD/torrentcontenttreeview.h \ $$PWD/deletionconfirmationdlg.h \ $$PWD/statusbar.h \ - $$PWD/ico.h \ $$PWD/speedlimitdlg.h \ $$PWD/about_imp.h \ $$PWD/previewselect.h \ @@ -63,7 +62,6 @@ HEADERS += \ SOURCES += \ $$PWD/mainwindow.cpp \ - $$PWD/ico.cpp \ $$PWD/transferlistwidget.cpp \ $$PWD/transferlistsortmodel.cpp \ $$PWD/transferlistdelegate.cpp \ diff --git a/src/gui/ico.cpp b/src/gui/ico.cpp deleted file mode 100644 index 5f30204bc..000000000 --- a/src/gui/ico.cpp +++ /dev/null @@ -1,460 +0,0 @@ -/* - * kimgio import filter for MS Windows .ico files - * - * Distributed under the terms of the LGPL - * Copyright (c) 2000 Malte Starostik - * - */ - -#include "ico.h" - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -namespace -{ - // Global header (see http://www.daubnet.com/formats/ICO.html) - struct IcoHeader - { - enum Type { Icon = 1, Cursor }; - quint16 reserved; - quint16 type; - quint16 count; - }; - - inline QDataStream& operator >>( QDataStream& s, IcoHeader& h ) - { - return s >> h.reserved >> h.type >> h.count; - } - - // Based on qt_read_dib et al. from qimage.cpp - // (c) 1992-2002 Trolltech AS. - struct BMP_INFOHDR - { - static const quint32 Size = 40; - quint32 biSize; // size of this struct - quint32 biWidth; // pixmap width - quint32 biHeight; // pixmap height - quint16 biPlanes; // should be 1 - quint16 biBitCount; // number of bits per pixel - enum Compression { RGB = 0 }; - quint32 biCompression; // compression method - quint32 biSizeImage; // size of image - quint32 biXPelsPerMeter; // horizontal resolution - quint32 biYPelsPerMeter; // vertical resolution - quint32 biClrUsed; // number of colors used - quint32 biClrImportant; // number of important colors - }; - const quint32 BMP_INFOHDR::Size; - - QDataStream& operator >>( QDataStream &s, BMP_INFOHDR &bi ) - { - s >> bi.biSize; - if ( bi.biSize == BMP_INFOHDR::Size ) - { - s >> bi.biWidth >> bi.biHeight >> bi.biPlanes >> bi.biBitCount; - s >> bi.biCompression >> bi.biSizeImage; - s >> bi.biXPelsPerMeter >> bi.biYPelsPerMeter; - s >> bi.biClrUsed >> bi.biClrImportant; - } - return s; - } - -#if 0 - QDataStream &operator<<( QDataStream &s, const BMP_INFOHDR &bi ) - { - s << bi.biSize; - s << bi.biWidth << bi.biHeight; - s << bi.biPlanes; - s << bi.biBitCount; - s << bi.biCompression; - s << bi.biSizeImage; - s << bi.biXPelsPerMeter << bi.biYPelsPerMeter; - s << bi.biClrUsed << bi.biClrImportant; - return s; - } -#endif - - // Header for every icon in the file - struct IconRec - { - unsigned char width; - unsigned char height; - quint16 colors; - quint16 hotspotX; - quint16 hotspotY; - quint32 size; - quint32 offset; - }; - - inline QDataStream& operator >>( QDataStream& s, IconRec& r ) - { - return s >> r.width >> r.height >> r.colors - >> r.hotspotX >> r.hotspotY >> r.size >> r.offset; - } - - struct LessDifference - { - LessDifference( unsigned s, unsigned c ) - : size( s ), colors( c ) {} - - bool operator ()( const IconRec& lhs, const IconRec& rhs ) const - { - // closest size match precedes everything else - if ( std::abs( int( lhs.width - size ) ) < - std::abs( int( rhs.width - size ) ) ) return true; - else if ( std::abs( int( lhs.width - size ) ) > - std::abs( int( rhs.width - size ) ) ) return false; - else if ( colors == 0 ) - { - // high/true color requested - if ( lhs.colors == 0 ) return true; - else if ( rhs.colors == 0 ) return false; - else return lhs.colors > rhs.colors; - } - else - { - // indexed icon requested - if ( lhs.colors == 0 && rhs.colors == 0 ) return false; - else if ( lhs.colors == 0 ) return false; - else return std::abs( int( lhs.colors - colors ) ) < - std::abs( int( rhs.colors - colors ) ); - } - } - unsigned size; - unsigned colors; - }; - - bool loadFromDIB( QDataStream& stream, const IconRec& rec, QImage& icon ) - { - BMP_INFOHDR header; - stream >> header; - if ( stream.atEnd() || header.biSize != BMP_INFOHDR::Size || - header.biSize > rec.size || - header.biCompression != BMP_INFOHDR::RGB || - ( header.biBitCount != 1 && header.biBitCount != 4 && - header.biBitCount != 8 && header.biBitCount != 24 && - header.biBitCount != 32 ) ) return false; - - unsigned paletteSize, paletteEntries; - - if (header.biBitCount > 8) - { - paletteEntries = 0; - paletteSize = 0; - } - else - { - paletteSize = (1 << header.biBitCount); - paletteEntries = paletteSize; - if (header.biClrUsed && header.biClrUsed < paletteSize) - paletteEntries = header.biClrUsed; - } - - // Always create a 32-bit image to get the mask right - // Note: this is safe as rec.width, rec.height are bytes - icon = QImage( rec.width, rec.height, QImage::Format_ARGB32 ); - if ( icon.isNull() ) return false; - - QVector< QRgb > colorTable( paletteSize ); - - colorTable.fill( QRgb( 0 ) ); - for ( unsigned i = 0; i < paletteEntries; ++i ) - { - unsigned char rgb[ 4 ]; - stream.readRawData( reinterpret_cast< char* >( &rgb ), - sizeof( rgb ) ); - colorTable[ i ] = qRgb( rgb[ 2 ], rgb[ 1 ], rgb[ 0 ] ); - } - - unsigned bpl = ( rec.width * header.biBitCount + 31 ) / 32 * 4; - - unsigned char* buf = new unsigned char[ bpl ]; - for ( unsigned y = rec.height; !stream.atEnd() && y--; ) - { - stream.readRawData( reinterpret_cast< char* >( buf ), bpl ); - unsigned char* pixel = buf; - QRgb* p = reinterpret_cast< QRgb* >( icon.scanLine(y)); - switch ( header.biBitCount ) - { - case 1: - for ( unsigned x = 0; x < rec.width; ++x ) - *p++ = colorTable[ - ( pixel[ x / 8 ] >> ( 7 - ( x & 0x07 ) ) ) & 1 ]; - break; - case 4: - for ( unsigned x = 0; x < rec.width; ++x ) - if ( x & 1 ) *p++ = colorTable[ pixel[ x / 2 ] & 0x0f ]; - else *p++ = colorTable[ pixel[ x / 2 ] >> 4 ]; - break; - case 8: - for ( unsigned x = 0; x < rec.width; ++x ) - *p++ = colorTable[ pixel[ x ] ]; - break; - case 24: - for ( unsigned x = 0; x < rec.width; ++x ) - *p++ = qRgb( pixel[ 3 * x + 2 ], - pixel[ 3 * x + 1 ], - pixel[ 3 * x ] ); - break; - case 32: - for ( unsigned x = 0; x < rec.width; ++x ) - *p++ = qRgba( pixel[ 4 * x + 2 ], - pixel[ 4 * x + 1 ], - pixel[ 4 * x ], - pixel[ 4 * x + 3] ); - break; - } - } - delete[] buf; - - if ( header.biBitCount < 32 ) - { - // Traditional 1-bit mask - bpl = ( rec.width + 31 ) / 32 * 4; - buf = new unsigned char[ bpl ]; - for ( unsigned y = rec.height; y--; ) - { - stream.readRawData( reinterpret_cast< char* >( buf ), bpl ); - QRgb* p = reinterpret_cast< QRgb* >(icon.scanLine(y)); - for ( unsigned x = 0; x < rec.width; ++x, ++p ) - if ( ( ( buf[ x / 8 ] >> ( 7 - ( x & 0x07 ) ) ) & 1 ) ) - *p &= RGB_MASK; - } - delete[] buf; - } - return true; - } -} - -ICOHandler::ICOHandler() -{ -} - -bool ICOHandler::canRead() const -{ - if (canRead(device())) { - setFormat("ico"); - return true; - } - return false; -} - -bool ICOHandler::read(QImage *outImage) -{ - - qint64 offset = device()->pos(); - - QDataStream stream( device() ); - stream.setByteOrder( QDataStream::LittleEndian ); - IcoHeader header; - stream >> header; - if ( stream.atEnd() || !header.count || - ( header.type != IcoHeader::Icon && header.type != IcoHeader::Cursor) ) - return false; - - unsigned requestedSize = 32; - unsigned requestedColors = QApplication::desktop()->depth() > 8 ? 0 : QApplication::desktop()->depth(); - int requestedIndex = -1; -#if 0 - if ( io->parameters() ) - { - QStringList params = QString(io->parameters()).split( ';', QString::SkipEmptyParts ); - QMap< QString, QString > options; - for ( QStringList::ConstIterator it = params.begin(); - it != params.end(); ++it ) - { - QStringList tmp = (*it).split( '=', QString::SkipEmptyParts ); - if ( tmp.count() == 2 ) options[ tmp[ 0 ] ] = tmp[ 1 ]; - } - if ( options[ "index" ].toUInt() ) - requestedIndex = options[ "index" ].toUInt(); - if ( options[ "size" ].toUInt() ) - requestedSize = options[ "size" ].toUInt(); - if ( options[ "colors" ].toUInt() ) - requestedColors = options[ "colors" ].toUInt(); - } -#endif - - typedef std::vector< IconRec > IconList; - IconList icons; - for ( unsigned i = 0; i < header.count; ++i ) - { - if ( stream.atEnd() ) - return false; - IconRec rec; - stream >> rec; - icons.push_back( rec ); - } - IconList::const_iterator selected; - if (requestedIndex >= 0) { - selected = std::min( icons.begin() + requestedIndex, icons.end() ); - } else { - selected = std::min_element( icons.begin(), icons.end(), - LessDifference( requestedSize, requestedColors ) ); - } - if ( stream.atEnd() || selected == icons.end() || - offset + selected->offset > device()->size() ) - return false; - - device()->seek( offset + selected->offset ); - QImage icon; - if ( loadFromDIB( stream, *selected, icon ) ) - { - icon.setText( "X-Index", QString::number( selected - icons.begin() ) ); - if ( header.type == IcoHeader::Cursor ) - { - icon.setText( "X-HotspotX", QString::number( selected->hotspotX ) ); - icon.setText( "X-HotspotY", QString::number( selected->hotspotY ) ); - } - - *outImage = icon; - return true; - } - return false; -} - -bool ICOHandler::write(const QImage &/*image*/) -{ -#if 0 - if (image.isNull()) - return; - - QByteArray dibData; - QDataStream dib(dibData, QIODevice::ReadWrite); - dib.setByteOrder(QDataStream::LittleEndian); - - QImage pixels = image; - QImage mask; - if (io->image().hasAlphaBuffer()) - mask = image.createAlphaMask(); - else - mask = image.createHeuristicMask(); - mask.invertPixels(); - for ( int y = 0; y < pixels.height(); ++y ) - for ( int x = 0; x < pixels.width(); ++x ) - if ( mask.pixel( x, y ) == 0 ) pixels.setPixel( x, y, 0 ); - - if (!qt_write_dib(dib, pixels)) - return; - - uint hdrPos = dib.device()->at(); - if (!qt_write_dib(dib, mask)) - return; - memmove(dibData.data() + hdrPos, dibData.data() + hdrPos + BMP_WIN + 8, dibData.size() - hdrPos - BMP_WIN - 8); - dibData.resize(dibData.size() - BMP_WIN - 8); - - QDataStream ico(device()); - ico.setByteOrder(QDataStream::LittleEndian); - IcoHeader hdr; - hdr.reserved = 0; - hdr.type = Icon; - hdr.count = 1; - ico << hdr.reserved << hdr.type << hdr.count; - IconRec rec; - rec.width = image.width(); - rec.height = image.height(); - if (image.numColors() <= 16) - rec.colors = 16; - else if (image.depth() <= 8) - rec.colors = 256; - else - rec.colors = 0; - rec.hotspotX = 0; - rec.hotspotY = 0; - rec.dibSize = dibData.size(); - ico << rec.width << rec.height << rec.colors - << rec.hotspotX << rec.hotspotY << rec.dibSize; - rec.dibOffset = ico.device()->at() + sizeof(rec.dibOffset); - ico << rec.dibOffset; - - BMP_INFOHDR dibHeader; - dib.device()->at(0); - dib >> dibHeader; - dibHeader.biHeight = image.height() << 1; - dib.device()->at(0); - dib << dibHeader; - - ico.writeRawBytes(dibData.data(), dibData.size()); - return true; -#endif - return false; -} - -QByteArray ICOHandler::name() const -{ - return "ico"; -} - -bool ICOHandler::canRead(QIODevice *device) -{ - if (!device) { - qWarning("ICOHandler::canRead() called with no device"); - return false; - } - - const qint64 oldPos = device->pos(); - - char head[8]; - qint64 readBytes = device->read(head, sizeof(head)); - const bool readOk = readBytes == sizeof(head); - - if (device->isSequential()) { - while (readBytes > 0) - device->ungetChar(head[readBytes-- - 1]); - } else { - device->seek(oldPos); - } - - if ( !readOk ) - return false; - - return head[2] == '\001' && head[3] == '\000' && // type should be 1 - ( head[6] == 16 || head[6] == 32 || head[6] == 64 ) && // width can only be one of those - ( head[7] == 16 || head[7] == 32 || head[7] == 64 ); // same for height -} - -class ICOPlugin : public QImageIOPlugin -{ - Q_PLUGIN_METADATA(IID "org.qbittorrent.ICOPlugin") -public: - QStringList keys() const; - Capabilities capabilities(QIODevice *device, const QByteArray &format) const; - QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const; -}; - -QStringList ICOPlugin::keys() const -{ - return QStringList() << "ico" << "ICO"; -} - -QImageIOPlugin::Capabilities ICOPlugin::capabilities(QIODevice *device, const QByteArray &format) const -{ - if (format == "ico" || format == "ICO") - return Capabilities(CanRead); - if (!format.isEmpty()) - return 0; - if (!device->isOpen()) - return 0; - - Capabilities cap; - if (device->isReadable() && ICOHandler::canRead(device)) - cap |= CanRead; - return cap; -} - -QImageIOHandler *ICOPlugin::create(QIODevice *device, const QByteArray &format) const -{ - QImageIOHandler *handler = new ICOHandler; - handler->setDevice(device); - handler->setFormat(format); - return handler; -} diff --git a/src/gui/ico.h b/src/gui/ico.h deleted file mode 100644 index 414afb7ef..000000000 --- a/src/gui/ico.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * ico.h - kimgio import filter for MS Windows .ico files - * - * Distributed under the terms of the LGPL - * Copyright (c) 2000 Malte Starostik - * - */ - -// You can use QImageIO::setParameters() to request a specific -// Icon out of an .ico file: -// -// Options consist of a name=value pair and are separated by a semicolon. -// Available options are: -// size= select the icon that most closely matches (pixels) -// default: 32 -// colors= select the icon that has colors (or comes closest) -// default: 1 << display depth or 0 (RGB) if display depth > 8 -// index= select the indexth icon from the file. If this option -// is present, the size and colors options will be ignored. -// default: none -// If both size and colors are given, size takes precedence. -// -// The old format is still supported: -// the parameters consist of a single string in the form -// "[:]" which correspond to the options above -// -// If an icon was returned (i.e. the file is valid and the index option -// if present was not out of range), the icon's index within the .ico -// file is returned in the text tag "X-Index" of the image. -// If the icon is in fact a cursor, its hotspot coordinates are returned -// in the text tags "X-HotspotX" and "X-HotspotY". - -#ifndef _ICO_H_ -#define _ICO_H_ - -#include - -class ICOHandler : public QImageIOHandler -{ -public: - ICOHandler(); - - bool canRead() const; - bool read(QImage *outImage); - bool write(const QImage &image); - - QByteArray name() const; - - static bool canRead(QIODevice *device); -}; - -#endif diff --git a/src/gui/search/pluginselectdlg.cpp b/src/gui/search/pluginselectdlg.cpp index f891a9fe7..581ac8a51 100644 --- a/src/gui/search/pluginselectdlg.cpp +++ b/src/gui/search/pluginselectdlg.cpp @@ -39,12 +39,12 @@ #include #include #include +#include #include "base/utils/fs.h" #include "base/utils/misc.h" #include "base/net/downloadmanager.h" #include "base/net/downloadhandler.h" -#include "ico.h" #include "searchwidget.h" #include "pluginsourcedlg.h" #include "guiiconprovider.h" @@ -364,18 +364,33 @@ void PluginSelectDlg::iconDownloaded(const QString &url, QString filePath) filePath = Utils::Fs::fromNativePath(filePath); // Icon downloaded - QImage fileIcon; - if (fileIcon.load(filePath)) { + QIcon icon(filePath); + // Detect a non-decodable icon + QList sizes = icon.availableSizes(); + bool invalid = (sizes.isEmpty() || icon.pixmap(sizes.first()).isNull()); + if (!invalid) { foreach (QTreeWidgetItem *item, findItemsWithUrl(url)) { QString id = item->text(PLUGIN_ID); PluginInfo *plugin = m_pluginManager->pluginInfo(id); if (!plugin) continue; - QFile icon(filePath); - icon.open(QIODevice::ReadOnly); - QString iconPath = QString("%1/%2.%3").arg(SearchEngine::pluginsLocation()).arg(id).arg(ICOHandler::canRead(&icon) ? "ico" : "png"); + QString iconPath = QString("%1/%2.%3") + .arg(SearchEngine::pluginsLocation()) + .arg(id) + .arg(url.endsWith(".ico", Qt::CaseInsensitive) ? "ico" : "png"); if (QFile::copy(filePath, iconPath)) { - item->setData(PLUGIN_NAME, Qt::DecorationRole, QVariant(QIcon(iconPath))); + // This 2nd check is necessary. Some favicons (eg from piratebay) + // decode fine without an ext, but fail to do so when appending the ext + // from the url. Probably a Qt bug. + QIcon iconWithExt(iconPath); + QList sizesExt = iconWithExt.availableSizes(); + bool invalidExt = (sizesExt.isEmpty() || iconWithExt.pixmap(sizesExt.first()).isNull()); + if (invalidExt) { + Utils::Fs::forceRemove(iconPath); + continue; + } + + item->setData(PLUGIN_NAME, Qt::DecorationRole, iconWithExt); m_pluginManager->updateIconPath(plugin); } }