File indexing completed on 2025-02-23 04:34:22

0001 /**
0002  * \file taggedfileiconprovider.cpp
0003  * Provides icons for tagged files.
0004  *
0005  * \b Project: Kid3
0006  * \author Urs Fleisch
0007  * \date 13 Jul 2019
0008  *
0009  * Copyright (C) 2019-2024  Urs Fleisch
0010  *
0011  * This file is part of Kid3.
0012  *
0013  * Kid3 is free software; you can redistribute it and/or modify
0014  * it under the terms of the GNU General Public License as published by
0015  * the Free Software Foundation; either version 2 of the License, or
0016  * (at your option) any later version.
0017  *
0018  * Kid3 is distributed in the hope that it will be useful,
0019  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0020  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0021  * GNU General Public License for more details.
0022  *
0023  * You should have received a copy of the GNU General Public License
0024  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
0025  */
0026 
0027 #include "taggedfileiconprovider.h"
0028 #include <QGuiApplication>
0029 #include <QPalette>
0030 #include <QIcon>
0031 #include <QPixmap>
0032 #include <QColor>
0033 #include <QPainter>
0034 #include "taggedfile.h"
0035 #include "tagconfig.h"
0036 
0037 /**
0038  * Constructor.
0039  */
0040 TaggedFileIconProvider::TaggedFileIconProvider()
0041   : m_requestedSize(16, 16), m_markedColor(QBrush(Qt::gray))
0042 {
0043   if (qobject_cast<QGuiApplication*>(QCoreApplication::instance()) != nullptr) {
0044     QPalette palette = QGuiApplication::palette();
0045     int h1, s1, l1, h2, s2, l2;
0046     palette.window().color().getHsl(&h1, &s1, &l1);
0047     palette.windowText().color().getHsl(&h2, &s2, &l2);
0048     m_markedColor = QColor::fromHsl((h1 + h2) / 2, (s1 + s2) / 2, (l1 + l2) / 2);
0049   }
0050 }
0051 
0052 /**
0053  * Set icon to be used for modified files.
0054  * @param icon modified icon
0055  */
0056 void TaggedFileIconProvider::setModifiedIcon(const QVariant& icon) {
0057   m_modifiedIcon = icon;
0058 }
0059 
0060 /**
0061  * Set the requested size for icons.
0062  *
0063  * The size set with this method will be used to create icons.
0064  *
0065  * @param size icon size, the default is 16x16.
0066  */
0067 void TaggedFileIconProvider::setRequestedSize(const QSize& size)
0068 {
0069   if (size.isValid() && size.height() > m_requestedSize.height()) {
0070     m_requestedSize = size;
0071     m_iconMap.clear();
0072     m_pixmapMap.clear();
0073   }
0074 }
0075 
0076 /**
0077  * Create icons using requested size.
0078  */
0079 void TaggedFileIconProvider::createIcons()
0080 {
0081   static const struct {
0082     const char* id;
0083     const char* text1;
0084     const char* text2;
0085   } idTexts[] = {
0086     {"null", nullptr, nullptr},
0087     {"notag", "NO", "TAG" },
0088     {"v1v2", "V1", "V2"},
0089     {"v1", "V1", nullptr},
0090     {"v2", nullptr, "V2"},
0091     {"v3", nullptr, "V3"},
0092     {"v1v3", "V1", "V3"},
0093     {"v2v3", "V2", "V3"},
0094     {"v1v2v3", "V1", "23"}
0095 };
0096 
0097   const int height = m_requestedSize.height();
0098   const int halfHeight = height / 2;
0099   QFont font(QLatin1String("helvetica"));
0100   font.setPixelSize(halfHeight);
0101   QFont smallFont(font);
0102   smallFont.setStretch(QFont::Condensed);
0103   for (const auto& [id, text1, text2] : idTexts) {
0104     QPixmap pixmap(m_requestedSize);
0105     pixmap.fill(Qt::transparent);
0106     QPainter painter(&pixmap);
0107     painter.setFont(font);
0108     if (text1) {
0109       painter.setPen(Qt::white);
0110       painter.drawText(QPoint(2, halfHeight - 1), QLatin1String(text1));
0111       painter.setPen(Qt::black);
0112       painter.drawText(QPoint(3, halfHeight), QLatin1String(text1));
0113     }
0114     if (text2) {
0115       if (qstrlen(text2) > 2) {
0116         painter.setFont(smallFont);
0117       }
0118       painter.setPen(Qt::white);
0119       painter.drawText(QPoint(2, height - 2), QLatin1String(text2));
0120       painter.setPen(Qt::black);
0121       painter.drawText(QPoint(3, height - 1), QLatin1String(text2));
0122     }
0123 
0124     m_pixmapMap.insert(id, pixmap);
0125   }
0126 
0127   for (auto it = m_pixmapMap.constBegin(); it != m_pixmapMap.constEnd(); ++it) {
0128     m_iconMap.insert(it.key(), QIcon(it.value().value<QPixmap>()));
0129   }
0130 
0131   if (m_modifiedIcon.isNull()) {
0132     // Some KDE styles do not provide a QStyle::SP_DriveFDIcon icon.
0133     // Create a fallback icon for such cases.
0134     static const char* const modifiedXpm[] = {
0135       "16 16 33 1",
0136       ". c None",
0137       "B c None",
0138       "A c None",
0139       "C c None",
0140       "D c None",
0141       "E c None",
0142       "# c #000000",
0143       "b c #006562",
0144       "j c #414041",
0145       "x c #525552",
0146       "f c #529594",
0147       "e c #52959c",
0148       "w c #5a555a",
0149       "v c #626162",
0150       "u c #626562",
0151       "r c #737173",
0152       "p c #737573",
0153       "q c #7b757b",
0154       "o c #838183",
0155       "m c #838583",
0156       "z c #8b8d8b",
0157       "l c #949194",
0158       "k c #9c959c",
0159       "i c #a4a1a4",
0160       "h c #a4a5a4",
0161       "y c #b4b6b4",
0162       "g c #bdb6bd",
0163       "a c #c5c2c5",
0164       "s c #c5c6c5",
0165       "c c #cdc6cd",
0166       "t c #dedade",
0167       "n c #eeeaee",
0168       "d c #ffffff",
0169       ".......##.......",
0170       "......#ab#......",
0171       ".....#cbde#.....",
0172       "....#abdddf#....",
0173       "...#gbddddde#...",
0174       "..#hijddddddf#..",
0175       ".#kjkljdddddd##.",
0176       "#mjnjmojddddjma#",
0177       "#jnpnjqrjddjqs#.",
0178       "#drtttjuvjjua#..",
0179       ".#dasajjwxws#...",
0180       "..#dyjzljxa#...A",
0181       "...#jrrjws#...AB",
0182       "....#cjxa#...ACB",
0183       ".....#cs#...ADE.",
0184       "......##...ABB.."
0185     };
0186     m_modifiedIcon = QIcon(QPixmap(modifiedXpm));
0187   }
0188   if (!m_modifiedIcon.isNull()) {
0189     m_iconMap.insert("modified", m_modifiedIcon);
0190     m_pixmapMap.insert("modified",
0191                        m_modifiedIcon.value<QIcon>().pixmap(m_requestedSize));
0192   }
0193 }
0194 
0195 /**
0196  * Get an icon for a tagged file.
0197  *
0198  * @param taggedFile tagged file
0199  *
0200  * @return icon for tagged file
0201  */
0202 QVariant TaggedFileIconProvider::iconForTaggedFile(const TaggedFile* taggedFile)
0203 {
0204   if (taggedFile) {
0205     if (m_iconMap.isEmpty()) {
0206       createIcons();
0207     }
0208     return m_iconMap.value(iconIdForTaggedFile(taggedFile));
0209   }
0210   return QVariant();
0211 }
0212 
0213 /**
0214  * Get pixmap for an icon ID.
0215  * @param id icon ID as returned by iconIdForTaggedFile(), or data for image
0216  * set with setImageData()
0217  * @return pixmap for @a id.
0218  */
0219 QVariant TaggedFileIconProvider::pixmapForIconId(const QByteArray& id)
0220 {
0221   if (m_pixmapMap.isEmpty()) {
0222     createIcons();
0223   }
0224   return m_pixmapMap.value(id);
0225 }
0226 
0227 /**
0228  * Get background color for a tagged file.
0229  *
0230  * @param taggedFile tagged file
0231  *
0232  * @return background color for tagged file, invalid color if background
0233  * should not be set
0234  */
0235 QVariant TaggedFileIconProvider::backgroundForTaggedFile(
0236     const TaggedFile* taggedFile) {
0237   if (taggedFile &&
0238       ((TagConfig::instance().markTruncations() &&
0239         taggedFile->getTruncationFlags(Frame::Tag_Id3v1) != 0) ||
0240        taggedFile->isMarked()))
0241     return QColor(Qt::red);
0242   return QVariant();
0243 }
0244 
0245 /**
0246  * Get brush with color for a context.
0247  * @param context color context
0248  * @return brush.
0249  */
0250 QVariant TaggedFileIconProvider::colorForContext(ColorContext context) const
0251 {
0252   switch (context) {
0253   case ColorContext::None:
0254     break;
0255   case ColorContext::Marked:
0256     return m_markedColor;
0257   case ColorContext::Error:
0258     return QBrush(Qt::red);
0259   }
0260   return QBrush(Qt::NoBrush);
0261 }
0262 
0263 /**
0264  * Get context for a brush.
0265  * @param color brush
0266  * @return color context.
0267  */
0268 ColorContext TaggedFileIconProvider::contextForColor(const QVariant& color) const
0269 {
0270 #if QT_VERSION >= 0x060000
0271   if (color.typeId() == QMetaType::QBrush) {
0272 #else
0273   if (color.type() == QVariant::Brush) {
0274 #endif
0275     if (auto brush = color.value<QBrush>(); brush == Qt::red) {
0276       return ColorContext::Error;
0277     } else if (brush != Qt::NoBrush) {
0278       return ColorContext::Marked;
0279     }
0280   }
0281   return ColorContext::None;
0282 }