File indexing completed on 2024-04-14 04:43:18
0001 /* AUDEX CDDA EXTRACTOR 0002 * SPDX-FileCopyrightText: Copyright (C) 2007 Marco Nelles 0003 * <https://userbase.kde.org/Audex> 0004 * 0005 * SPDX-License-Identifier: GPL-3.0-or-later 0006 */ 0007 0008 #include "cddaheaderwidget.h" 0009 0010 #include <QDebug> 0011 #include <QFileDialog> 0012 #include <QMenu> 0013 #include <qnamespace.h> 0014 0015 CDDAHeaderWidget ::CDDAHeaderWidget(CDDAModel *cddaModel, QWidget *parent, const int cover_size_min, const int cover_size_max, const int padding) 0016 : QWidget(parent) 0017 { 0018 cdda_model = cddaModel; 0019 if (!cdda_model) { 0020 qDebug() << "CDDAModel is NULL!"; 0021 return; 0022 } 0023 connect(cdda_model, SIGNAL(modelReset()), this, SLOT(update())); 0024 0025 setup_actions(); 0026 0027 this->cover_size_min = cover_size_min; 0028 this->cover_size_max = cover_size_max; 0029 this->padding = padding; 0030 0031 setMouseTracking(true); 0032 cursor_on_cover = false; 0033 cursor_on_artist_label = false; 0034 cursor_on_title_label = false; 0035 cursor_on_year_label = false; 0036 0037 connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(context_menu(const QPoint &))); 0038 0039 setContextMenuPolicy(Qt::CustomContextMenu); 0040 0041 setMinimumSize(QSize(cover_size_min + (padding * 2), cover_size_min + (padding * 2))); 0042 0043 update(); 0044 } 0045 0046 CDDAHeaderWidget::~CDDAHeaderWidget() 0047 { 0048 delete action_collection; 0049 } 0050 0051 QSize CDDAHeaderWidget::sizeHint() const 0052 { 0053 return QSize(cover_size_min + (padding * 2), cover_size_min + (padding * 2)); 0054 } 0055 0056 bool CDDAHeaderWidget::isEnabled() const 0057 { 0058 return enabled; 0059 } 0060 0061 void CDDAHeaderWidget::setEnabled(bool enabled) 0062 { 0063 this->enabled = enabled; 0064 repaint(); 0065 } 0066 0067 void CDDAHeaderWidget::paintEvent(QPaintEvent *event) 0068 { 0069 Q_UNUSED(event); 0070 0071 QPainter painter; 0072 0073 painter.begin(this); 0074 0075 if (enabled) { 0076 const bool vertical = this->frameGeometry().width() > this->frameGeometry().height(); 0077 QImage scaled_cd_case = 0078 cd_case.scaled(vertical ? QSize(qMin(rect().height() - padding * 2, cover_size_max), qMin(rect().height() - padding * 2, cover_size_max)) 0079 : QSize(qMin(rect().width() - padding * 2, cover_size_max), qMin(rect().width() - padding * 2, cover_size_max)), 0080 Qt::KeepAspectRatio, 0081 Qt::SmoothTransformation); 0082 0083 int xOffset = padding; 0084 int yOffset = padding; 0085 0086 // QImage scaled_cover = cover.scaled(cover_size, cover_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); 0087 cover_rect = QRect(xOffset, yOffset, scaled_cd_case.width(), scaled_cd_case.height()); 0088 painter.drawImage(QPoint(xOffset, yOffset), scaled_cd_case); 0089 0090 if (vertical) { 0091 xOffset += scaled_cd_case.width() + padding; 0092 } else { 0093 yOffset += scaled_cd_case.height(); 0094 } 0095 0096 painter.setBrush(palette().text()); 0097 0098 QFont font(QApplication::font()); 0099 0100 int pixelSize = font.pixelSize() == -1 ? (font.pointSize() * qApp->devicePixelRatio() + 36) / 72 : font.pixelSize(); 0101 int width = rect().width() - (xOffset + 1); 0102 font.setPixelSize((int)((((double)pixelSize) * 2) + 0.5)); 0103 font.setBold(true); 0104 font.setUnderline(cursor_on_artist_label || cursor_on_title_label || cursor_on_year_label); 0105 painter.setFont(font); 0106 artist_label_rect = QRect(QPoint(xOffset, yOffset + 10), painter.fontMetrics().boundingRect(cdda_model->artist()).size()); 0107 yOffset += painter.fontMetrics().lineSpacing() + 10; 0108 painter.drawText(xOffset, yOffset, painter.fontMetrics().elidedText(cdda_model->artist(), Qt::ElideRight, width)); 0109 0110 font.setPixelSize((int)((((double)pixelSize) * 1.6) + 0.5)); 0111 font.setBold(true); 0112 font.setItalic(true); 0113 painter.setFont(font); 0114 title_label_rect = QRect(QPoint(xOffset, yOffset + 10), painter.fontMetrics().boundingRect(cdda_model->title()).size()); 0115 yOffset += (int)((((double)pixelSize) * 1.6) + 0.5) + 10; 0116 painter.drawText(xOffset, yOffset, painter.fontMetrics().elidedText(cdda_model->title(), Qt::ElideRight, width)); 0117 0118 if (!cdda_model->year().isEmpty()) { 0119 font.setItalic(false); 0120 font.setUnderline(false); 0121 painter.setFont(font); 0122 painter.drawText(xOffset + title_label_rect.width(), yOffset, QString(" (%1)").arg(cdda_model->year())); 0123 year_label_rect = QRect(QPoint(xOffset + title_label_rect.width(), title_label_rect.y()), 0124 painter.fontMetrics().boundingRect(QString(" (%1)").arg(cdda_model->year())).size()); 0125 } 0126 0127 if (cdda_model->cdNum() > 0) { 0128 font.setItalic(false); 0129 font.setUnderline(false); 0130 font.setPixelSize((int)((((double)pixelSize)) + 0.5)); 0131 painter.setFont(font); 0132 yOffset += painter.fontMetrics().lineSpacing() + 10; 0133 painter.drawText(xOffset, yOffset, QString("[%1%2]").arg(i18n("CD Number: ")).arg(cdda_model->cdNum())); 0134 } 0135 0136 } else { // disabled 0137 0138 QFont font(QApplication::font()); 0139 if (font.pixelSize() == -1) { 0140 font.setPointSizeF(font.pointSizeF() * 1.6); 0141 } else { 0142 font.setPixelSize(font.pixelSize() * 1.6); 0143 } 0144 font.setBold(true); 0145 font.setItalic(true); 0146 painter.setFont(font); 0147 painter.drawText(rect(), Qt::AlignCenter | Qt::AlignVCenter, i18n("No audio CD detected")); 0148 } 0149 0150 painter.end(); 0151 } 0152 0153 void CDDAHeaderWidget::construct_cd_case() 0154 { 0155 QImage cdcase_wo_latches = QImage(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QString("audex/images/cdcase_wo_latches.png"))); 0156 QImage latches = QImage(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QString("audex/images/latches.png"))); 0157 QImage cdcase_covered = QImage(cdcase_wo_latches.size(), QImage::Format_ARGB32_Premultiplied); 0158 0159 QPainter cover_painter(&cdcase_covered); 0160 cover_painter.setRenderHint(QPainter::Antialiasing); 0161 0162 cover_painter.setCompositionMode(QPainter::CompositionMode_Source); 0163 cover_painter.fillRect(cdcase_covered.rect(), Qt::transparent); 0164 0165 cover_painter.setCompositionMode(QPainter::CompositionMode_SourceOver); 0166 cover_painter.drawImage(0, 0, cdcase_wo_latches); 0167 0168 cover_painter.setCompositionMode(QPainter::CompositionMode_SourceOver); 0169 if (!cdda_model->cover().isNull()) 0170 cover_painter.drawImage(125, 15, cdda_model->cover().scaled(QSize(1110, 1080), Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); 0171 0172 cover_painter.setCompositionMode(QPainter::CompositionMode_SourceOver); 0173 cover_painter.drawImage(259, 0, latches); 0174 0175 cover_painter.end(); 0176 0177 this->cd_case = cdcase_covered; 0178 } 0179 0180 void CDDAHeaderWidget::mouseMoveEvent(QMouseEvent *event) 0181 { 0182 if (cover_rect.contains(event->pos())) { 0183 if (!cursor_on_cover) { 0184 QApplication::setOverrideCursor(QCursor(Qt::PointingHandCursor)); 0185 cursor_on_cover = true; 0186 } 0187 } else if (artist_label_rect.contains(event->pos())) { 0188 if (!cursor_on_artist_label) { 0189 QApplication::setOverrideCursor(QCursor(Qt::PointingHandCursor)); 0190 cursor_on_artist_label = true; 0191 repaint(); 0192 } 0193 } else if (title_label_rect.contains(event->pos())) { 0194 if (!cursor_on_title_label) { 0195 QApplication::setOverrideCursor(QCursor(Qt::PointingHandCursor)); 0196 cursor_on_title_label = true; 0197 repaint(); 0198 } 0199 } else if (year_label_rect.contains(event->pos())) { 0200 if (!cursor_on_year_label) { 0201 QApplication::setOverrideCursor(QCursor(Qt::PointingHandCursor)); 0202 cursor_on_year_label = true; 0203 repaint(); 0204 } 0205 } else { 0206 QApplication::restoreOverrideCursor(); 0207 if (cursor_on_cover) { 0208 cursor_on_cover = false; 0209 } else if (cursor_on_artist_label) { 0210 cursor_on_artist_label = false; 0211 repaint(); 0212 } else if (cursor_on_title_label) { 0213 cursor_on_title_label = false; 0214 repaint(); 0215 } else if (cursor_on_year_label) { 0216 cursor_on_year_label = false; 0217 repaint(); 0218 } 0219 } 0220 } 0221 0222 void CDDAHeaderWidget::mousePressEvent(QMouseEvent *event) 0223 { 0224 if (event->button() == Qt::LeftButton) { 0225 if (cursor_on_cover) { 0226 QApplication::restoreOverrideCursor(); 0227 if (cdda_model->isCoverEmpty()) { 0228 load(); 0229 } else { 0230 view_cover(); 0231 } 0232 } 0233 if (cursor_on_artist_label || cursor_on_title_label || cursor_on_year_label) { 0234 QApplication::restoreOverrideCursor(); 0235 edit_data(); 0236 } 0237 } 0238 } 0239 0240 void CDDAHeaderWidget::update() 0241 { 0242 // action_collection->action("fetch")->setEnabled(!cdda_model->empty()); 0243 0244 bool activate = false; 0245 if (!cdda_model->cover().isNull()) { 0246 activate = true; 0247 } 0248 0249 action_collection->action("save")->setEnabled(activate); 0250 action_collection->action("view")->setEnabled(activate); 0251 action_collection->action("remove")->setEnabled(activate); 0252 0253 construct_cd_case(); 0254 repaint(); 0255 } 0256 0257 void CDDAHeaderWidget::load() 0258 { 0259 qDebug() << "Supported cover image file MIME types:" << cdda_model->coverSupportedMimeTypeList(); 0260 QString filename = QFileDialog::getOpenFileName(this, i18n("Load Cover"), QDir::homePath(), cdda_model->coverSupportedMimeTypeList()); 0261 if (!filename.isEmpty()) { 0262 if (!cdda_model->setCover(filename)) { 0263 ErrorDialog::show(this, cdda_model->lastError().message(), cdda_model->lastError().details()); 0264 } 0265 } 0266 } 0267 0268 void CDDAHeaderWidget::save() 0269 { 0270 QString filename = QFileDialog::getSaveFileName(this, i18n("Save Cover"), QDir::homePath() + '/' + cdda_model->title() + ".jpg"); 0271 if (!filename.isEmpty()) { 0272 if (!cdda_model->saveCoverToFile(filename)) { 0273 ErrorDialog::show(this, cdda_model->lastError().message(), cdda_model->lastError().details()); 0274 } 0275 } 0276 } 0277 0278 void CDDAHeaderWidget::view_cover() 0279 { 0280 QString filename = tmp_dir.path() + QString("%1.jpeg").arg(cdda_model->cover().cacheKey(), 8, 16); 0281 cdda_model->saveCoverToFile(filename); 0282 0283 qDebug() << "Open" << filename; 0284 0285 QDesktopServices::openUrl(QUrl(filename)); 0286 } 0287 0288 void CDDAHeaderWidget::remove() 0289 { 0290 cdda_model->clearCover(); 0291 update(); 0292 } 0293 0294 void CDDAHeaderWidget::edit_data() 0295 { 0296 QApplication::restoreOverrideCursor(); 0297 cursor_on_artist_label = false; 0298 cursor_on_title_label = false; 0299 cursor_on_year_label = false; 0300 0301 auto *dialog = new CDDAHeaderDataDialog(cdda_model, this); 0302 0303 if (dialog->exec() != QDialog::Accepted) { 0304 delete dialog; 0305 return; 0306 } 0307 delete dialog; 0308 update(); 0309 Q_EMIT headerDataChanged(); 0310 } 0311 0312 void CDDAHeaderWidget::set_cover(const QByteArray &cover) 0313 { 0314 if (!cover.isEmpty()) 0315 cdda_model->setCover(cover); 0316 // action_collection->action("fetch")->setEnabled(true); 0317 update(); 0318 } 0319 0320 void CDDAHeaderWidget::context_menu(const QPoint &point) 0321 { 0322 qDebug() << "context menu requested at point" << point; 0323 if (cursor_on_cover) { 0324 QApplication::restoreOverrideCursor(); 0325 cursor_on_cover = false; 0326 QMenu contextMenu(this); 0327 auto *mevent = new QMouseEvent(QEvent::MouseButtonPress, point, Qt::RightButton, Qt::RightButton, Qt::NoModifier); 0328 contextMenu.clear(); 0329 // contextMenu.addAction(action_collection->action("fetch")); 0330 contextMenu.addAction(action_collection->action("load")); 0331 contextMenu.addAction(action_collection->action("save")); 0332 contextMenu.addSeparator(); 0333 contextMenu.addAction(action_collection->action("view")); 0334 contextMenu.addSeparator(); 0335 contextMenu.addAction(action_collection->action("remove")); 0336 contextMenu.exec(mevent->globalPos()); 0337 } 0338 } 0339 0340 void CDDAHeaderWidget::setup_actions() 0341 { 0342 action_collection = new KActionCollection(this); 0343 0344 // auto *fetchCoverAction = new QAction(this); 0345 // fetchCoverAction->setText(i18n("Fetch cover from Google...")); 0346 // action_collection->addAction("fetch", fetchCoverAction); 0347 // connect(fetchCoverAction, SIGNAL(triggered(bool)), this, SLOT(google())); 0348 0349 auto *loadCoverAction = new QAction(this); 0350 loadCoverAction->setText(i18n("Set Custom Cover...")); 0351 action_collection->addAction("load", loadCoverAction); 0352 connect(loadCoverAction, SIGNAL(triggered(bool)), this, SLOT(load())); 0353 0354 auto *saveCoverAction = new QAction(this); 0355 saveCoverAction->setText(i18n("Save Cover To File...")); 0356 action_collection->addAction("save", saveCoverAction); 0357 connect(saveCoverAction, SIGNAL(triggered(bool)), this, SLOT(save())); 0358 0359 auto *viewCoverAction = new QAction(this); 0360 viewCoverAction->setText(i18n("Show Full Size Cover...")); 0361 action_collection->addAction("view", viewCoverAction); 0362 connect(viewCoverAction, SIGNAL(triggered(bool)), this, SLOT(view_cover())); 0363 0364 auto *removeCoverAction = new QAction(this); 0365 removeCoverAction->setText(i18n("Remove Cover")); 0366 action_collection->addAction("remove", removeCoverAction); 0367 connect(removeCoverAction, SIGNAL(triggered(bool)), this, SLOT(remove())); 0368 }