/* * Copyright © 2009 Fredrik Höglund * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "tooltipwidget.h" #include "abstractitemview.h" #include "proxymodel.h" #include #include #include #include #include #include #include #include #include ToolTipWidget::ToolTipWidget(AbstractItemView *parent) : QGraphicsWidget(parent), m_view(parent), m_previewJob(0) { Plasma::ToolTipManager::self()->registerWidget(this); } void ToolTipWidget::updateToolTip(const QModelIndex &index, const QRectF &rect) { if (!index.isValid()) { // Send a fake hover leave event to the widget to trick the tooltip // manager into doing a delayed hide. QGraphicsSceneHoverEvent event(QEvent::GraphicsSceneHoverLeave); QApplication::sendEvent(this, &event); m_preview = QPixmap(); m_item = KFileItem(); m_index = QModelIndex(); return; } setGeometry(rect); m_item = static_cast(m_view->model())->itemForIndex(index); m_index = index; m_preview = QPixmap(); // If a preview job is still running (from a previously hovered item), // wait 200 ms before starting a new one. This is done to throttle // the number of preview jobs that are started when the user moves // the cursor over the icon view. if (m_previewJob) { m_previewTimer.start(200, this); } else { if (m_previewTimer.isActive()) { m_previewTimer.stop(); } startPreviewJob(); } Plasma::ToolTipManager::self()->show(this); } static qreal convertToReal(const QString &string) { const int pos = string.indexOf('/'); if (pos != -1) { const int left = string.left(pos).toInt(); const int right = string.mid(pos + 1).toInt(); return right > 0 ? qreal(left) / qreal(right) : 0.0; } return qreal(string.toInt()); } QString ToolTipWidget::metaInfo() const { const QString mimetype = m_item.mimetype(); if (!mimetype.startsWith(QLatin1String("audio/")) && !mimetype.startsWith(QLatin1String("image/")) && !m_item.mimeTypePtr()->is("application/vnd.oasis.opendocument.text")) { return QString(); } KFileMetaInfo info = m_item.metaInfo(true, KFileMetaInfo::TechnicalInfo | KFileMetaInfo::ContentInfo); QString text; if (mimetype.startsWith(QLatin1String("audio/"))) { // ### Disabled because the strigi ID3 analyzer is broken #if 0 const QString title = info.item("http://www.semanticdesktop.org/ontologies/2007/01/19/nie#title").value().toString(); const QString artist = info.item("http://www.semanticdesktop.org/ontologies/2009/02/19/nmm#performer").value().toString(); const QString album = info.item("http://www.semanticdesktop.org/ontologies/2009/02/19/nmm#musicAlbum").value().toString(); if (!artist.isEmpty() || !title.isEmpty() || !album.isEmpty()) { text += "

"; if (!artist.isEmpty()) { text += QString(""); } if (!title.isEmpty()) { text += QString(""); } if (!album.isEmpty()) { text += QString(""); } text += "
") + i18nc("Music", "Artist:") + QString(" ") + artist + QString("
") + i18nc("Music", "Title:") + QString(" ") + title + QString("
") + i18nc("Music", "Album:") + QString(" ") + album + QString("
"; } #endif } else if (mimetype.startsWith(QLatin1String("image/"))) { int width = info.item("http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#width").value().toInt(); int height = info.item("http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#height").value().toInt(); const QString camera = info.item("http://www.semanticdesktop.org/ontologies/2007/05/10/nexif#model").value().toString(); const QString type = info.item("http://www.w3.org/1999/02/22-rdf-syntax-ns#type").value().toString(); QString exposureTime = info.item("http://www.semanticdesktop.org/ontologies/2007/05/10/nexif#exposureTime").value().toString(); QString focalLength = info.item("http://www.semanticdesktop.org/ontologies/2007/05/10/nexif#focalLength").value().toString(); QString focal35mm = info.item("http://www.semanticdesktop.org/ontologies/2007/05/10/nexif#focalLengthIn35mmFilm").value().toString(); QString aperture = info.item("http://www.semanticdesktop.org/ontologies/2007/05/10/nexif#apertureValue").value().toString(); QString iso = info.item("http://www.semanticdesktop.org/ontologies/2007/05/10/nexif#isoSpeedRatings").value().toString(); QString created = info.item("http://www.semanticdesktop.org/ontologies/2007/01/19/nie#contentCreated").value().toString(); text += "

"; if (width > 0 && height > 0) { QString size = QString::number(width) + 'x' + QString::number(height); // Add the megapixel count for photos if (type == "http://www.semanticdesktop.org/ontologies/2007/05/10/nexif#Photo") { const qreal pixels = qreal(width * height) / 1e6; size += QString(" (") + ki18n("%1 MPixels").subs(pixels, 0, 'f', 1).toString() + QString(")"); } text += QString(""); } if (!camera.isEmpty()) { text += QString(""); } if (!focalLength.isEmpty()) { const qreal length = convertToReal(focalLength); focalLength = i18nc("Length in millimeters", "%1 mm", qRound(length)); if (!focal35mm.isEmpty()) { const qreal length = convertToReal(focal35mm); focalLength += QString(" (") + i18nc("In photography", "35 mm equivalent: %1 mm", qRound(length)) + QString(")"); } text += QString(""); } if (!exposureTime.isEmpty()) { const qreal time = convertToReal(exposureTime); if (time < 1.0) { exposureTime = QString("1/") + QString::number(qRound((1.0 / time))); } else { exposureTime = QString::number(time, 'f', 1); } text += QString(""); } if (!aperture.isEmpty()) { // Convert the APEX value to the F number const qreal fnumber = std::sqrt(std::pow(2, convertToReal(aperture))); aperture = QString("f/") + QString::number(fnumber, 'f', 1); if (aperture.endsWith(QLatin1String(".0"))) { aperture = aperture.left(aperture.length() - 2); } text += QString(""); } if (!iso.isEmpty()) { text += QString(""); } if (!created.isEmpty()) { const QDateTime dateTime = QDateTime::fromString(created, "yyyy:MM:dd HH:mm:ss"); text += QString(""); } text += "
") + i18n("Size:") + QString(" ") + size + QString("
") + i18n("Camera:") + QString(" ") + camera + QString("
") + i18nc("On a camera", "Focal Length:") + QString(" ") + focalLength + QString("
") + i18nc("On a camera", "Exposure Time:") + QString(" ") + i18nc("Fraction of a second, or number of seconds", "%1 s", exposureTime) + QString("
") + i18nc("On a camera", "Aperture:") + QString(" ") + aperture + QString("
") + i18nc("On a camera", "ISO Speed:") + QString(" ") + iso + QString("
") + i18n("Time:") + QString(" ") + KGlobal::locale()->formatDateTime(dateTime, KLocale::ShortDate, true) + QString("
"; } else if (m_item.mimeTypePtr()->is("application/vnd.oasis.opendocument.text")) { int wordCount = info.item("http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#wordCount").value().toInt(); int pageCount = info.item("http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#pageCount").value().toInt(); const QString str1 = i18ncp("Inserted as %1 in the message below.", "1 page", "%1 pages", pageCount); const QString str2 = i18ncp("Inserted as %2 in the message below.", "1 word", "%1 words", wordCount); if (pageCount > 0) { text += QString("

") + i18nc("%1 and %2 are the messages translated above.", "%1, %2.", str1, str2); } } return text; } void ToolTipWidget::setContent() { Plasma::ToolTipContent content; content.setMainText(m_index.data(Qt::DisplayRole).toString()); if (m_preview.isNull()) { content.setImage(qvariant_cast(m_index.data(Qt::DecorationRole))); } else { content.setImage(m_preview); } QString subText; if (m_item.isDesktopFile()) { // Add the comment in the .desktop file to the subtext. // Note that we don't include the mime type for .desktop files, // since users will likely be confused about what will happen when // they click a "Desktop configuration file" on their desktop. KDesktopFile file(m_item.localPath()); subText = file.readComment(); } else { if (m_item.isMimeTypeKnown()) { subText = m_item.mimeComment(); } if (m_item.isDir()) { // Include information about the number of files and folders in the directory. const QVariant value = m_index.data(KDirModel::ChildCountRole); const int count = value.type() == QVariant::Int ? value.toInt() : KDirModel::ChildCountUnknown; if (count != KDirModel::ChildCountUnknown) { subText += QString("
") + i18ncp("Items in a folder", "1 item", "%1 items", count); } } else { // File size if (m_item.isFile()) { subText += QString("
") + KGlobal::locale()->formatByteSize(m_item.size()); } // Add meta info from the strigi analyzers subText += metaInfo(); } } content.setSubText(subText); content.setAutohide(false); Plasma::ToolTipManager::self()->setContent(this, content); } void ToolTipWidget::startPreviewJob() { QStringList plugins; plugins << "imagethumbnail" << "jpegthumbnail"; m_previewJob = KIO::filePreview(KFileItemList() << m_item, QSize(256, 256), &plugins); connect(m_previewJob, SIGNAL(gotPreview(KFileItem,QPixmap)), SLOT(gotPreview(KFileItem,QPixmap))); connect(m_previewJob, SIGNAL(finished(KJob*)), SLOT(previewJobFinished(KJob*))); } void ToolTipWidget::gotPreview(const KFileItem &item, const QPixmap &pixmap) { if (item == m_item) { m_preview = pixmap; setContent(); } else if (m_item.isNull()) { m_preview = QPixmap(); } } void ToolTipWidget::previewJobFinished(KJob *job) { if (job == m_previewJob) { m_previewJob = 0; } } void ToolTipWidget::toolTipAboutToShow() { if (m_index.isValid()) { setContent(); m_hideTimer.start(10000, this); } else { Plasma::ToolTipManager::self()->clearContent(this); } } void ToolTipWidget::timerEvent(QTimerEvent *event) { if (event->timerId() == m_previewTimer.timerId()) { m_previewTimer.stop(); if (m_index.isValid()) { startPreviewJob(); } } if (event->timerId() == m_hideTimer.timerId()) { m_hideTimer.stop(); Plasma::ToolTipManager::self()->hide(this); } } #include "moc_tooltipwidget.cpp"