File indexing completed on 2023-05-30 11:30:48
0001 /** 0002 * Copyright (C) 2004 Scott Wheeler <wheeler@kde.org> 0003 * Copyright (C) 2009 Michael Pyne <mpyne@kde.org> 0004 * 0005 * This program is free software; you can redistribute it and/or modify it under 0006 * the terms of the GNU General Public License as published by the Free Software 0007 * Foundation; either version 2 of the License, or (at your option) any later 0008 * version. 0009 * 0010 * This program is distributed in the hope that it will be useful, but WITHOUT ANY 0011 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 0012 * PARTICULAR PURPOSE. See the GNU General Public License for more details. 0013 * 0014 * You should have received a copy of the GNU General Public License along with 0015 * this program. If not, see <http://www.gnu.org/licenses/>. 0016 */ 0017 0018 #include "nowplaying.h" 0019 0020 #include <KIO/StoredTransferJob> 0021 #include <KJobWidgets> 0022 #include <KLocalizedString> 0023 #include <kiconloader.h> 0024 0025 #include <QApplication> 0026 #include <QDrag> 0027 #include <QDragEnterEvent> 0028 #include <QDropEvent> 0029 #include <QFontDatabase> 0030 #include <QFontMetrics> 0031 #include <QImage> 0032 #include <QLabel> 0033 #include <QLayout> 0034 #include <QList> 0035 #include <QMouseEvent> 0036 #include <QPoint> 0037 #include <QTimer> 0038 #include <QUrl> 0039 #include <QVBoxLayout> 0040 0041 #include "playlistcollection.h" 0042 #include "playlistitem.h" 0043 #include "coverinfo.h" 0044 #include "covermanager.h" 0045 #include "juktag.h" 0046 #include "collectionlist.h" 0047 #include "juk_debug.h" 0048 0049 //////////////////////////////////////////////////////////////////////////////// 0050 // NowPlaying 0051 //////////////////////////////////////////////////////////////////////////////// 0052 0053 NowPlaying::NowPlaying(QWidget *parent, PlaylistCollection *collection) : 0054 QWidget(parent), 0055 m_observer(this, collection), 0056 // Also watch the collection 0057 m_collectionListObserver(this, CollectionList::instance()), 0058 m_collection(collection) 0059 { 0060 setObjectName(QLatin1String("NowPlaying")); 0061 0062 QHBoxLayout *layout = new QHBoxLayout(this); 0063 setLayout(layout); 0064 0065 layout->setContentsMargins(0, 0, 0, 0); 0066 layout->setSpacing(3); 0067 0068 // With HiDPI the text might actually be bigger... try to account for 0069 // that. 0070 const QFont defaultLargeFont(QFontDatabase::systemFont(QFontDatabase::TitleFont)); 0071 const QFontMetrics fm(defaultLargeFont, this); 0072 0073 const int coverIconHeight = qMax(64, fm.lineSpacing() * 2); 0074 setFixedHeight(coverIconHeight + 4); 0075 0076 layout->addWidget(new CoverItem(this), 0); 0077 layout->addWidget(new TrackItem(this), 2); 0078 0079 hide(); 0080 } 0081 0082 void NowPlaying::addItem(NowPlayingItem *item) 0083 { 0084 m_items.append(item); 0085 } 0086 0087 PlaylistCollection *NowPlaying::collection() const 0088 { 0089 return m_collection; 0090 } 0091 0092 void NowPlaying::slotUpdate(const FileHandle &file) 0093 { 0094 m_file = file; 0095 if(file.isNull()) { 0096 hide(); 0097 emit nowPlayingHidden(); 0098 return; 0099 } 0100 else 0101 show(); 0102 0103 foreach(NowPlayingItem *item, m_items) 0104 item->update(file); 0105 } 0106 0107 void NowPlaying::slotReloadCurrentItem() 0108 { 0109 foreach(NowPlayingItem *item, m_items) 0110 item->update(m_file); 0111 } 0112 0113 //////////////////////////////////////////////////////////////////////////////// 0114 // CoverItem 0115 //////////////////////////////////////////////////////////////////////////////// 0116 0117 CoverItem::CoverItem(NowPlaying *parent) : 0118 QLabel(parent), 0119 NowPlayingItem(parent) 0120 { 0121 setObjectName(QLatin1String("CoverItem")); 0122 const QMargins margins = parent->layout()->contentsMargins(); 0123 0124 setFixedHeight(parent->height() - (margins.top() + margins.bottom())); 0125 setContentsMargins(1, 1, 1, 1); 0126 setAcceptDrops(true); 0127 } 0128 0129 void CoverItem::update(const FileHandle &file) 0130 { 0131 m_file = file; 0132 0133 if(!file.isNull() && file.coverInfo()->hasCover()) { 0134 show(); 0135 0136 const auto pixRatio = this->devicePixelRatioF(); 0137 const QSizeF logicalSize = QSizeF(this->height(), this->height()); 0138 const QSizeF scaledSize = logicalSize * pixRatio; 0139 QPixmap pix = 0140 file.coverInfo()->pixmap(CoverInfo::FullSize) 0141 .scaled(scaledSize.toSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation); 0142 0143 if (!qFuzzyCompare(pixRatio, 1.0)) { 0144 pix.setDevicePixelRatio(pixRatio); 0145 } 0146 0147 setPixmap(pix); 0148 } 0149 else 0150 hide(); 0151 } 0152 0153 void CoverItem::mouseReleaseEvent(QMouseEvent *event) 0154 { 0155 if(m_dragging) { 0156 m_dragging = false; 0157 return; 0158 } 0159 0160 if(event->x() >= 0 && event->y() >= 0 && 0161 event->x() < width() && event->y() < height() && 0162 event->button() == Qt::LeftButton && 0163 m_file.coverInfo()->hasCover()) 0164 { 0165 m_file.coverInfo()->popup(); 0166 } 0167 0168 QLabel::mousePressEvent(event); 0169 } 0170 0171 void CoverItem::mousePressEvent(QMouseEvent *e) 0172 { 0173 m_dragging = false; 0174 m_dragStart = e->globalPos(); 0175 } 0176 0177 void CoverItem::mouseMoveEvent(QMouseEvent *e) 0178 { 0179 if(m_dragging) 0180 return; 0181 0182 QPoint diff = m_dragStart - e->globalPos(); 0183 if(diff.manhattanLength() > QApplication::startDragDistance()) { 0184 0185 // Start a drag. 0186 0187 m_dragging = true; 0188 0189 QDrag *drag = new QDrag(this); 0190 CoverDrag *data = new CoverDrag(m_file.coverInfo()->coverId()); 0191 0192 drag->setMimeData(data); 0193 drag->exec(Qt::CopyAction); 0194 } 0195 } 0196 0197 void CoverItem::dragEnterEvent(QDragEnterEvent *e) 0198 { 0199 e->setAccepted(CoverDrag::isCover(e->mimeData()) || e->mimeData()->hasUrls()); 0200 } 0201 0202 void CoverItem::dropEvent(QDropEvent *e) 0203 { 0204 QImage image; 0205 QList<QUrl> urls; 0206 coverKey key; 0207 0208 if(e->source() == this) 0209 return; 0210 0211 key = CoverDrag::idFromData(e->mimeData()); 0212 if(key != CoverManager::NoMatch) { 0213 m_file.coverInfo()->setCoverId(key); 0214 update(m_file); 0215 } 0216 else if(e->mimeData()->hasImage()) { 0217 m_file.coverInfo()->setCover(qvariant_cast<QImage>(e->mimeData()->imageData())); 0218 update(m_file); 0219 } 0220 else { 0221 urls = e->mimeData()->urls(); 0222 if(urls.isEmpty()) 0223 return; 0224 0225 auto getJob = KIO::storedGet(urls.front()); 0226 KJobWidgets::setWindow(getJob, this); 0227 if(getJob->exec()) { 0228 if(image.loadFromData(getJob->data())) { 0229 m_file.coverInfo()->setCover(image); 0230 update(m_file); 0231 } 0232 else 0233 qCCritical(JUK_LOG) << "Unable to load image from " << urls.front(); 0234 } 0235 else 0236 qCCritical(JUK_LOG) << "Unable to download " << urls.front(); 0237 } 0238 } 0239 0240 //////////////////////////////////////////////////////////////////////////////// 0241 // TrackItem 0242 //////////////////////////////////////////////////////////////////////////////// 0243 0244 TrackItem::TrackItem(NowPlaying *parent) : 0245 QWidget(parent), 0246 NowPlayingItem(parent) 0247 { 0248 setObjectName(QLatin1String("TrackItem")); 0249 const QMargins margins = parent->layout()->contentsMargins(); 0250 setFixedHeight(parent->height() - (margins.top() + margins.bottom())); 0251 setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); 0252 0253 QVBoxLayout *layout = new QVBoxLayout(this); 0254 0255 m_label = new QLabel(this); 0256 m_label->setWordWrap(true); 0257 0258 m_label->setTextInteractionFlags(Qt::LinksAccessibleByMouse|Qt::LinksAccessibleByKeyboard|Qt::TextSelectableByMouse); 0259 0260 layout->addStretch(); 0261 layout->addWidget(m_label, 1); 0262 layout->addStretch(); 0263 0264 connect(m_label, SIGNAL(linkActivated(QString)), this, 0265 SLOT(slotOpenLink(QString))); 0266 0267 // Ensure that if we're filtering results, that the filtering is cleared if we 0268 // hide the now playing bar so that the user can select tracks normally. 0269 0270 connect(parent, SIGNAL(nowPlayingHidden()), SLOT(slotClearShowMore())); 0271 } 0272 0273 void TrackItem::update(const FileHandle &file) 0274 { 0275 m_file = file; 0276 QTimer::singleShot(0, this, SLOT(slotUpdate())); 0277 } 0278 0279 void TrackItem::slotOpenLink(const QString &link) 0280 { 0281 PlaylistCollection *collection = parentManager()->collection(); 0282 0283 if(link == "artist") 0284 collection->showMore(m_file.tag()->artist()); 0285 else if(link == "album") 0286 collection->showMore(QString(), m_file.tag()->album()); 0287 else if(link == "clear") 0288 collection->clearShowMore(); 0289 0290 update(m_file); 0291 } 0292 0293 void TrackItem::slotUpdate() 0294 { 0295 if(m_file.isNull()) { 0296 m_label->setText(QString()); 0297 return; 0298 } 0299 0300 const QString title = m_file.tag()->title().toHtmlEscaped(); 0301 const QString artist = m_file.tag()->artist().toHtmlEscaped(); 0302 const QString album = m_file.tag()->album().toHtmlEscaped(); 0303 const QString separator = 0304 (artist.isEmpty() || album.isEmpty()) 0305 ? QString() : QString(" - "); 0306 0307 // This block-o-nastiness makes the font smaller and smaller until it actually fits. 0308 0309 int size = 4; 0310 QString format = 0311 "<font size=\"+%1\"><b>%2</b></font>" 0312 "<br />" 0313 "<font size=\"+%3\"><b><a href=\"artist\">%4</a>%5<a href=\"album\">%6</a></b>"; 0314 0315 if(parentManager()->collection()->showMoreActive()) 0316 format.append(QString(" (<a href=\"clear\">%1</a>)").arg(i18n("back to playlist"))); 0317 0318 format.append("</font>"); 0319 int parentHeight = parentManager()->contentsRect().height(); 0320 int neededHeight = 0; 0321 0322 do { 0323 m_label->setText(format.arg(size).arg(title).arg(size - 2) 0324 .arg(artist, separator, album)); 0325 --size; 0326 neededHeight = m_label->heightForWidth(m_label->width()); 0327 } while(neededHeight > parentHeight && size >= -1); 0328 0329 m_label->setFixedHeight(qMin(neededHeight, parentHeight)); 0330 } 0331 0332 void TrackItem::slotClearShowMore() 0333 { 0334 PlaylistCollection *collection = parentManager()->collection(); 0335 Q_ASSERT(collection); 0336 collection->clearShowMore(); 0337 } 0338 0339 // vim: set et sw=4 tw=0 sta: