diff --git a/app/kipiinterface.cpp b/app/kipiinterface.cpp index 2ab02b31..d7b0d1d9 100644 --- a/app/kipiinterface.cpp +++ b/app/kipiinterface.cpp @@ -1,518 +1,518 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2000-2008 Aurélien Gâteau Copyright 2008 Angelo Naselli This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Cambridge, MA 02110-1301, USA. */ #include "kipiinterface.h" // Qt #include #include #include #include #include #include // KDE #include #include #include #include #include #include #include #include // KIPI #include #include #include #include #include // local #include "mainwindow.h" #include "kipiimagecollectionselector.h" #include "kipiuploadwidget.h" #include #include #include #include #include #define KIPI_PLUGINS_URL QStringLiteral("appstream://photolayoutseditor.desktop") namespace Gwenview { #undef ENABLE_LOG #undef LOG //#define ENABLE_LOG #ifdef ENABLE_LOG #define LOG(x) qDebug() << x #else #define LOG(x) ; #endif class KIPIImageInfo : public KIPI::ImageInfoShared { static const QRegExp sExtensionRE; public: KIPIImageInfo(KIPI::Interface* interface, const QUrl &url) : KIPI::ImageInfoShared(interface, url) { KFileItem item(url); mAttributes.insert("name", url.fileName()); mAttributes.insert("comment", comment()); mAttributes.insert("date", TimeUtils::dateTimeForFileItem(item)); mAttributes.insert("orientation", orientation()); mAttributes.insert("title", prettyFileName()); int size = item.size(); if (size > 0) { mAttributes.insert("filesize", size); } } QMap attributes() override { return mAttributes; } void delAttributes(const QStringList& attributeNames) override { for (const QString& name : attributeNames) { mAttributes.remove(name); } } void clearAttributes() override { mAttributes.clear(); } void addAttributes(const QVariantMap& attributes) override { QVariantMap::ConstIterator it = attributes.constBegin(), end = attributes.constEnd(); for (; it != end; ++it) { mAttributes.insert(it.key(), it.value()); } } private: QString prettyFileName() const { QString txt = _url.fileName(); txt.replace('_', ' '); txt.remove(sExtensionRE); return txt; } QString comment() const { if (!_url.isLocalFile()) return QString(); JpegContent content; bool ok = content.load(_url.toLocalFile()); if (!ok) return QString(); return content.comment(); } int orientation() const { #if 0 //PORT QT5 KFileMetaInfo metaInfo(_url); if (!metaInfo.isValid()) { return 0; } const KFileMetaInfoItem& mii = metaInfo.item("http://freedesktop.org/standards/xesam/1.0/core#orientation"); bool ok = false; const Orientation orientation = (Orientation)mii.value().toInt(&ok); if (!ok) { return 0; } switch (orientation) { case NOT_AVAILABLE: case NORMAL: return 0; case ROT_90: return 90; case ROT_180: return 180; case ROT_270: return 270; case HFLIP: case VFLIP: case TRANSPOSE: case TRANSVERSE: qWarning() << "Can't represent an orientation value of" << orientation << "as an angle (" << _url << ')'; return 0; } #endif //qWarning() << "Don't know how to handle an orientation value of" << orientation << '(' << _url << ')'; return 0; } QVariantMap mAttributes; }; const QRegExp KIPIImageInfo::sExtensionRE("\\.[a-z0-9]+$", Qt::CaseInsensitive); struct MenuInfo { QString mName; QList mActions; QString mIconName; MenuInfo() {} MenuInfo(const QString& name, QString iconName) : mName(name) , mIconName(iconName) {} }; typedef QMap MenuInfoMap; struct KIPIInterfacePrivate { KIPIInterface* q; MainWindow* mMainWindow; QMenu* mPluginMenu; KIPI::PluginLoader* mPluginLoader; KIPI::PluginLoader::PluginList mPluginQueue; MenuInfoMap mMenuInfoMap; QAction * mLoadingAction; QAction * mNoPluginAction; QAction * mInstallPluginAction; QFileSystemWatcher mPluginWatcher; QTimer mPluginLoadTimer; void setupPluginsMenu() { mPluginMenu = static_cast( mMainWindow->factory()->container("plugins", mMainWindow)); QObject::connect(mPluginMenu, &QMenu::aboutToShow, q, &KIPIInterface::loadPlugins); } QAction * createDummyPluginAction(const QString& text) { QAction * action = new QAction(q); action->setText(text); //PORT QT5 action->setShortcutConfigurable(false); action->setEnabled(false); return action; } }; KIPIInterface::KIPIInterface(MainWindow* mainWindow) : KIPI::Interface(mainWindow) , d(new KIPIInterfacePrivate) { d->q = this; d->mMainWindow = mainWindow; d->mPluginLoader = nullptr; d->mLoadingAction = d->createDummyPluginAction(i18n("Loading...")); d->mNoPluginAction = d->createDummyPluginAction(i18n("No Plugin Found")); d->mInstallPluginAction = d->createDummyPluginAction(i18nc("@item:inmenu", "Install Plugins")); connect(&d->mPluginLoadTimer, &QTimer::timeout, this, &KIPIInterface::loadPlugins); d->setupPluginsMenu(); QObject::connect(d->mMainWindow->contextManager(), &ContextManager::selectionChanged, this, &KIPIInterface::slotSelectionChanged); QObject::connect(d->mMainWindow->contextManager(), &ContextManager::currentDirUrlChanged, this, &KIPIInterface::slotDirectoryChanged); #if 0 //TODO instead of delaying can we load them all at start-up to use actions somewhere else? // delay a bit, so that it's called after loadPlugins() QTimer::singleShot(0, this, SLOT(init())); #endif } KIPIInterface::~KIPIInterface() { delete d; } static bool actionLessThan(QAction* a1, QAction* a2) { - QString a1Text = a1->text().replace('&', QString()); - QString a2Text = a2->text().replace('&', QString()); + QString a1Text = a1->text().remove('&'); + QString a2Text = a2->text().remove('&'); return QString::compare(a1Text, a2Text, Qt::CaseInsensitive) < 0; } void KIPIInterface::loadPlugins() { // Already done if (d->mPluginLoader) { return; } d->mMenuInfoMap[KIPI::ImagesPlugin] = MenuInfo(i18nc("@title:menu", "Images"), QStringLiteral("viewimage")); d->mMenuInfoMap[KIPI::ToolsPlugin] = MenuInfo(i18nc("@title:menu", "Tools"), QStringLiteral("tools")); d->mMenuInfoMap[KIPI::ImportPlugin] = MenuInfo(i18nc("@title:menu", "Import"), QStringLiteral("document-import")); d->mMenuInfoMap[KIPI::ExportPlugin] = MenuInfo(i18nc("@title:menu", "Export"), QStringLiteral("document-export")); d->mMenuInfoMap[KIPI::BatchPlugin] = MenuInfo(i18nc("@title:menu", "Batch Processing"), QStringLiteral("editimage")); d->mMenuInfoMap[KIPI::CollectionsPlugin] = MenuInfo(i18nc("@title:menu", "Collections"), QStringLiteral("view-list-symbolic")); d->mPluginLoader = new KIPI::PluginLoader(); d->mPluginLoader->setInterface(this); d->mPluginLoader->init(); d->mPluginQueue = d->mPluginLoader->pluginList(); d->mPluginMenu->addAction(d->mLoadingAction); loadOnePlugin(); } void KIPIInterface::loadOnePlugin() { while (!d->mPluginQueue.isEmpty()) { KIPI::PluginLoader::Info* pluginInfo = d->mPluginQueue.takeFirst(); if (!pluginInfo->shouldLoad()) { continue; } KIPI::Plugin* plugin = pluginInfo->plugin(); if (!plugin) { qWarning() << "Plugin from library" << pluginInfo->library() << "failed to load"; continue; } plugin->setup(d->mMainWindow); const QList actions = plugin->actions(); for (QAction * action : actions) { KIPI::Category category = plugin->category(action); if (!d->mMenuInfoMap.contains(category)) { qWarning() << "Unknown category '" << category; continue; } d->mMenuInfoMap[category].mActions << action; } // FIXME: Port //plugin->actionCollection()->readShortcutSettings(); // If we reach this point, we just loaded one plugin. Go back to the // event loop. We will come back to load the remaining plugins or create // the menu later QMetaObject::invokeMethod(this, "loadOnePlugin", Qt::QueuedConnection); return; } // If we reach this point, all plugins have been loaded. We can fill the // menu MenuInfoMap::Iterator it = d->mMenuInfoMap.begin(), end = d->mMenuInfoMap.end(); for (; it != end; ++it) { MenuInfo& info = it.value(); if (!info.mActions.isEmpty()) { QMenu* menu = d->mPluginMenu->addMenu(info.mName); menu->setIcon(QIcon::fromTheme(info.mIconName)); std::sort(info.mActions.begin(), info.mActions.end(), actionLessThan); for (QAction * action : qAsConst(info.mActions)) { menu->addAction(action); } } } d->mPluginMenu->removeAction(d->mLoadingAction); if (d->mPluginMenu->isEmpty()) { if (KIO::DesktopExecParser::hasSchemeHandler(QUrl(KIPI_PLUGINS_URL))) { d->mPluginMenu->addAction(d->mInstallPluginAction); d->mInstallPluginAction->setEnabled(true); QObject::connect(d->mInstallPluginAction, &QAction::triggered, this, [&](){ QDesktopServices::openUrl(QUrl(KIPI_PLUGINS_URL)); d->mPluginWatcher.addPaths(QCoreApplication::libraryPaths()); connect(&d->mPluginWatcher, &QFileSystemWatcher::directoryChanged, this, &KIPIInterface::packageFinished); }); } else { d->mPluginMenu->addAction(d->mNoPluginAction); } } loadingFinished(); } void KIPIInterface::packageFinished() { if (d->mPluginLoader) { delete d->mPluginLoader; d->mPluginLoader = nullptr; } d->mPluginMenu->removeAction(d->mInstallPluginAction); d->mPluginMenu->removeAction(d->mNoPluginAction); d->mPluginLoadTimer.start(1000); } QList KIPIInterface::pluginActions(KIPI::Category category) const { const_cast(this)->loadPlugins(); if (isLoadingFinished()) { QList list = d->mMenuInfoMap.value(category).mActions; if (list.isEmpty()) { if (KIO::DesktopExecParser::hasSchemeHandler(QUrl(KIPI_PLUGINS_URL))) { list << d->mInstallPluginAction; } else { list << d->mNoPluginAction; } } return list; } else { return QList() << d->mLoadingAction; } } bool KIPIInterface::isLoadingFinished() const { if (!d->mPluginLoader) { // Not even started return false; } return d->mPluginQueue.isEmpty(); } void KIPIInterface::init() { slotDirectoryChanged(); slotSelectionChanged(); } KIPI::ImageCollection KIPIInterface::currentAlbum() { LOG(""); const ContextManager* contextManager = d->mMainWindow->contextManager(); const QUrl url = contextManager->currentDirUrl(); const SortedDirModel* model = contextManager->dirModel(); QList list; const int count = model->rowCount(); for (int row = 0; row < count; ++row) { const QModelIndex& index = model->index(row, 0); const KFileItem item = model->itemForIndex(index); if (MimeTypeUtils::fileItemKind(item) == MimeTypeUtils::KIND_RASTER_IMAGE) { list << item.targetUrl(); } } return KIPI::ImageCollection(new ImageCollection(url, url.fileName(), list)); } KIPI::ImageCollection KIPIInterface::currentSelection() { LOG(""); KFileItemList fileList = d->mMainWindow->contextManager()->selectedFileItemList(); QList list = fileList.urlList(); QUrl url = d->mMainWindow->contextManager()->currentUrl(); return KIPI::ImageCollection(new ImageCollection(url, url.fileName(), list)); } QList KIPIInterface::allAlbums() { LOG(""); QList list; list << currentAlbum() << currentSelection(); return list; } KIPI::ImageInfo KIPIInterface::info(const QUrl &url) { LOG(""); return KIPI::ImageInfo(new KIPIImageInfo(this, url)); } int KIPIInterface::features() const { return KIPI::HostAcceptNewImages; } /** * KDirLister will pick up the image if necessary, so no updating is needed * here, it is however necessary to discard caches if the plugin preserves timestamp */ bool KIPIInterface::addImage(const QUrl&, QString&) { //TODO setContext(const QUrl ¤tUrl, const KFileItemList& selection)? //Cache::instance()->invalidate( url ); return true; } void KIPIInterface::delImage(const QUrl&) { //TODO } void KIPIInterface::refreshImages(const QList&) { // TODO } KIPI::ImageCollectionSelector* KIPIInterface::imageCollectionSelector(QWidget *parent) { return new KIPIImageCollectionSelector(this, parent); } KIPI::UploadWidget* KIPIInterface::uploadWidget(QWidget *parent) { return (new KIPIUploadWidget(this, parent)); } void KIPIInterface::slotSelectionChanged() { emit selectionChanged(!d->mMainWindow->contextManager()->selectedFileItemList().isEmpty()); } void KIPIInterface::slotDirectoryChanged() { emit currentAlbumChanged(true); } #ifdef GWENVIEW_KIPI_WITH_CREATE_METHODS KIPI::FileReadWriteLock* KIPIInterface::createReadWriteLock(const QUrl& url) const { Q_UNUSED(url); return NULL; } KIPI::MetadataProcessor* KIPIInterface::createMetadataProcessor() const { return NULL; } #ifdef GWENVIEW_KIPI_WITH_CREATE_RAW_PROCESSOR KIPI::RawProcessor* KIPIInterface::createRawProcessor() const { return NULL; } #endif #endif } //namespace diff --git a/lib/imagemetainfomodel.cpp b/lib/imagemetainfomodel.cpp index f30f5e55..699b5665 100644 --- a/lib/imagemetainfomodel.cpp +++ b/lib/imagemetainfomodel.cpp @@ -1,575 +1,575 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2007 Aurélien Gâteau This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // Self #include "imagemetainfomodel.h" #include "config-gwenview.h" // Qt #include #include #include // KDE #include #include #include // Exiv2 #include // Local #ifdef HAVE_FITS #include "imageformats/fitsformat/fitsdata.h" #include "urlutils.h" #endif namespace Gwenview { enum GroupRow { GeneralGroup, ExifGroup, #ifdef HAVE_FITS FitsGroup, #endif IptcGroup, XmpGroup, NoGroupSpace, // second last entry NoGroup // last entry }; class MetaInfoGroup { public: enum { InvalidRow = -1 }; class Entry { public: Entry(const QString& key, const QString& label, const QString& value) : mKey(key), mLabel(label.trimmed()), mValue(value.trimmed()) {} QString key() const { return mKey; } QString label() const { return mLabel; } QString value() const { return mValue; } void setValue(const QString& value) { mValue = value.trimmed(); } void appendValue(const QString& value) { if (!mValue.isEmpty()) { mValue += QLatin1Char('\n'); } mValue += value.trimmed(); } private: QString mKey; QString mLabel; QString mValue; }; MetaInfoGroup(const QString& label) : mLabel(label) {} ~MetaInfoGroup() { qDeleteAll(mList); } void clear() { qDeleteAll(mList); mList.clear(); mRowForKey.clear(); } void addEntry(const QString& key, const QString& label, const QString& value) { addEntry(new Entry(key, label, value)); } void addEntry(Entry* entry) { mList << entry; mRowForKey[entry->key()] = mList.size() - 1; } void getInfoForKey(const QString& key, QString* label, QString* value) const { Entry* entry = getEntryForKey(key); if (entry) { *label = entry->label(); *value = entry->value(); } } QString getKeyAt(int row) const { Q_ASSERT(row < mList.size()); return mList[row]->key(); } QString getLabelForKeyAt(int row) const { Q_ASSERT(row < mList.size()); return mList[row]->label(); } QString getValueForKeyAt(int row) const { Q_ASSERT(row < mList.size()); return mList[row]->value(); } void setValueForKeyAt(int row, const QString& value) { Q_ASSERT(row < mList.size()); mList[row]->setValue(value); } int getRowForKey(const QString& key) const { return mRowForKey.value(key, InvalidRow); } int size() const { return mList.size(); } QString label() const { return mLabel; } const QList& entryList() const { return mList; } private: Entry* getEntryForKey(const QString& key) const { int row = getRowForKey(key); if (row == InvalidRow) { return nullptr; } return mList[row]; } QList mList; QHash mRowForKey; QString mLabel; }; struct ImageMetaInfoModelPrivate { QVector mMetaInfoGroupVector; ImageMetaInfoModel* q; void clearGroup(MetaInfoGroup* group, const QModelIndex& parent) { if (group->size() > 0) { q->beginRemoveRows(parent, 0, group->size() - 1); group->clear(); q->endRemoveRows(); } } void setGroupEntryValue(GroupRow groupRow, const QString& key, const QString& value) { MetaInfoGroup* group = mMetaInfoGroupVector[groupRow]; int entryRow = group->getRowForKey(key); if (entryRow == MetaInfoGroup::InvalidRow) { qWarning() << "No row for key" << key; return; } group->setValueForKeyAt(entryRow, value); QModelIndex groupIndex = q->index(groupRow, 0); QModelIndex entryIndex = q->index(entryRow, 1, groupIndex); emit q->dataChanged(entryIndex, entryIndex); } QVariant displayData(const QModelIndex& index) const { if (index.internalId() == NoGroup) { if (index.column() != 0) { return QVariant(); } QString label = mMetaInfoGroupVector[index.row()]->label(); return QVariant(label); } if (index.internalId() == NoGroupSpace) { return QString(); } MetaInfoGroup* group = mMetaInfoGroupVector[index.internalId()]; if (index.column() == 0) { return group->getLabelForKeyAt(index.row()); } else { return group->getValueForKeyAt(index.row()); } } void initGeneralGroup() { MetaInfoGroup* group = mMetaInfoGroupVector[GeneralGroup]; group->addEntry(QStringLiteral("General.Name"), i18nc("@item:intable Image file name", "Name"), QString()); group->addEntry(QStringLiteral("General.Size"), i18nc("@item:intable", "File Size"), QString()); group->addEntry(QStringLiteral("General.Time"), i18nc("@item:intable", "File Time"), QString()); group->addEntry(QStringLiteral("General.ImageSize"), i18nc("@item:intable", "Image Size"), QString()); group->addEntry(QStringLiteral("General.Comment"), i18nc("@item:intable", "Comment"), QString()); } template void fillExivGroup(const QModelIndex& parent, MetaInfoGroup* group, const Container& container) { // key aren't always unique (for example, "Iptc.Application2.Keywords" // may appear multiple times) so we can't know how many rows we will // insert before going through them. That's why we create a hash // before. typedef QHash EntryHash; EntryHash hash; Iterator it = container.begin(), end = container.end(); for (; it != end; ++it) { try { // Skip metadatum if its tag is an hex number if (it->tagName().substr(0, 2) == "0x") { continue; } QString key = QString::fromUtf8(it->key().c_str()); QString label = QString::fromLocal8Bit(it->tagLabel().c_str()); std::ostringstream stream; stream << *it; QString value = QString::fromLocal8Bit(stream.str().c_str()); EntryHash::iterator hashIt = hash.find(key); if (hashIt != hash.end()) { hashIt.value()->appendValue(value); } else { hash.insert(key, new MetaInfoGroup::Entry(key, label, value)); } } catch (const Exiv2::Error& error) { qWarning() << "Failed to read some meta info:" << error.what(); } } if (hash.isEmpty()) { return; } q->beginInsertRows(parent, 0, hash.size() - 1); for (MetaInfoGroup::Entry * entry : qAsConst(hash)) { group->addEntry(entry); } q->endInsertRows(); } }; ImageMetaInfoModel::ImageMetaInfoModel() : d(new ImageMetaInfoModelPrivate) { d->q = this; #ifdef HAVE_FITS d->mMetaInfoGroupVector.resize(5); #else d->mMetaInfoGroupVector.resize(4); #endif d->mMetaInfoGroupVector[GeneralGroup] = new MetaInfoGroup(i18nc("@title:group General info about the image", "General")); d->mMetaInfoGroupVector[ExifGroup] = new MetaInfoGroup(QStringLiteral("EXIF")); #ifdef HAVE_FITS d->mMetaInfoGroupVector[FitsGroup] = new MetaInfoGroup(QStringLiteral("FITS")); #endif d->mMetaInfoGroupVector[IptcGroup] = new MetaInfoGroup(QStringLiteral("IPTC")); d->mMetaInfoGroupVector[XmpGroup] = new MetaInfoGroup(QStringLiteral("XMP")); d->initGeneralGroup(); } ImageMetaInfoModel::~ImageMetaInfoModel() { qDeleteAll(d->mMetaInfoGroupVector); delete d; } void ImageMetaInfoModel::setUrl(const QUrl &url) { KFileItem item(url); const QString sizeString = KFormat().formatByteSize(item.size()); const QString timeString = QLocale().toString(item.time(KFileItem::ModificationTime), QLocale::LongFormat); d->setGroupEntryValue(GeneralGroup, QStringLiteral("General.Name"), item.name()); d->setGroupEntryValue(GeneralGroup, QStringLiteral("General.Size"), sizeString); d->setGroupEntryValue(GeneralGroup, QStringLiteral("General.Time"), timeString); #ifdef HAVE_FITS - if (UrlUtils::urlIsFastLocalFile(url) && (url.fileName().endsWith(QStringLiteral(".fit"), Qt::CaseInsensitive) || - url.fileName().endsWith(QStringLiteral(".fits"), Qt::CaseInsensitive))) { + if (UrlUtils::urlIsFastLocalFile(url) && (url.fileName().endsWith(QLatin1String(".fit"), Qt::CaseInsensitive) || + url.fileName().endsWith(QLatin1String(".fits"), Qt::CaseInsensitive))) { FITSData fitsLoader; MetaInfoGroup* group = d->mMetaInfoGroupVector[FitsGroup]; QFile file(url.toLocalFile()); if (!file.open(QIODevice::ReadOnly)) { return; } if (fitsLoader.loadFITS(file)) { QString recordList; int nkeys = 0; fitsLoader.getFITSRecord(recordList, nkeys); for (int i = 0; i < nkeys; i++) { QString record = recordList.mid(i * 80, 80); QString key; QString keyStr; QString value; - if (!record.contains(QStringLiteral("="))) { + if (!record.contains(QLatin1String("="))) { key = record.section(QLatin1Char(' '), 0, 0).simplified(); keyStr = key; value = record.section(QLatin1Char(' '), 1, -1).simplified(); } else { key = record.section(QLatin1Char('='), 0, 0).simplified(); if (record.contains(QLatin1Char('/'))) { keyStr = record.section(QLatin1Char('/'), -1, -1).simplified(); value = record.section(QLatin1Char('='), 1, -1).section(QLatin1Char('/'), 0, 0); } else { keyStr = key; value = record.section(QLatin1Char('='), 1, -1); } value.remove(QStringLiteral("\'")); value = value.simplified(); } if (value.isEmpty()) { continue; } // Check if the value is a number and make it more readable bool ok = false; float number = value.toFloat(&ok); if (ok) { value = QString::number(number); } group->addEntry(QStringLiteral("Fits.")+key, keyStr, value); } } } #endif } void ImageMetaInfoModel::setImageSize(const QSize& size) { QString imageSize; if (size.isValid()) { imageSize = i18nc( "@item:intable %1 is image width, %2 is image height", "%1x%2", size.width(), size.height()); double megaPixels = size.width() * size.height() / 1000000.; if (megaPixels > 0.1) { QString megaPixelsString = QString::number(megaPixels, 'f', 1); imageSize += QLatin1Char(' '); imageSize += i18nc( "@item:intable %1 is number of millions of pixels in image", "(%1MP)", megaPixelsString); } } else { imageSize = QLatin1Char('-'); } d->setGroupEntryValue(GeneralGroup, QStringLiteral("General.ImageSize"), imageSize); } void ImageMetaInfoModel::setExiv2Image(const Exiv2::Image* image) { MetaInfoGroup* exifGroup = d->mMetaInfoGroupVector[ExifGroup]; MetaInfoGroup* iptcGroup = d->mMetaInfoGroupVector[IptcGroup]; MetaInfoGroup* xmpGroup = d->mMetaInfoGroupVector[XmpGroup]; QModelIndex exifIndex = index(ExifGroup, 0); QModelIndex iptcIndex = index(IptcGroup, 0); QModelIndex xmpIndex = index(XmpGroup, 0); d->clearGroup(exifGroup, exifIndex); d->clearGroup(iptcGroup, iptcIndex); d->clearGroup(xmpGroup, xmpIndex); if (!image) { return; } d->setGroupEntryValue(GeneralGroup, QStringLiteral("General.Comment"), QString::fromUtf8(image->comment().c_str())); if (image->checkMode(Exiv2::mdExif) & Exiv2::amRead) { const Exiv2::ExifData& exifData = image->exifData(); d->fillExivGroup(exifIndex, exifGroup, exifData); } if (image->checkMode(Exiv2::mdIptc) & Exiv2::amRead) { const Exiv2::IptcData& iptcData = image->iptcData(); d->fillExivGroup(iptcIndex, iptcGroup, iptcData); } if (image->checkMode(Exiv2::mdXmp) & Exiv2::amRead) { const Exiv2::XmpData& xmpData = image->xmpData(); d->fillExivGroup(xmpIndex, xmpGroup, xmpData); } } void ImageMetaInfoModel::getInfoForKey(const QString& key, QString* label, QString* value) const { MetaInfoGroup* group; if (key.startsWith(QLatin1String("General"))) { group = d->mMetaInfoGroupVector[GeneralGroup]; } else if (key.startsWith(QLatin1String("Exif"))) { group = d->mMetaInfoGroupVector[ExifGroup]; #ifdef HAVE_FITS } else if (key.startsWith(QLatin1String("Fits"))) { group = d->mMetaInfoGroupVector[FitsGroup]; #endif } else if (key.startsWith(QLatin1String("Iptc"))) { group = d->mMetaInfoGroupVector[IptcGroup]; } else if (key.startsWith(QLatin1String("Xmp"))) { group = d->mMetaInfoGroupVector[XmpGroup]; } else { qWarning() << "Unknown metainfo key" << key; return; } group->getInfoForKey(key, label, value); } QString ImageMetaInfoModel::getValueForKey(const QString& key) const { QString label, value; getInfoForKey(key, &label, &value); return value; } QString ImageMetaInfoModel::keyForIndex(const QModelIndex& index) const { if (index.internalId() == NoGroup) { return QString(); } MetaInfoGroup* group = d->mMetaInfoGroupVector[index.internalId()]; return group->getKeyAt(index.row()); } QModelIndex ImageMetaInfoModel::index(int row, int col, const QModelIndex& parent) const { if (col < 0 || col > 1) { return QModelIndex(); } if (!parent.isValid()) { // This is a group if (row < 0 || row >= d->mMetaInfoGroupVector.size()) { return QModelIndex(); } return createIndex(row, col, col == 0 ? NoGroup : NoGroupSpace); } else { // This is an entry int group = parent.row(); if (row < 0 || row >= d->mMetaInfoGroupVector[group]->size()) { return QModelIndex(); } return createIndex(row, col, group); } } QModelIndex ImageMetaInfoModel::parent(const QModelIndex& index) const { if (!index.isValid()) { return QModelIndex(); } if (index.internalId() == NoGroup || index.internalId() == NoGroupSpace) { return QModelIndex(); } else { return createIndex(index.internalId(), 0, NoGroup); } } int ImageMetaInfoModel::rowCount(const QModelIndex& parent) const { if (!parent.isValid()) { return d->mMetaInfoGroupVector.size(); } else if (parent.internalId() == NoGroup) { return d->mMetaInfoGroupVector[parent.row()]->size(); } else { return 0; } } int ImageMetaInfoModel::columnCount(const QModelIndex& /*parent*/) const { return 2; } QVariant ImageMetaInfoModel::data(const QModelIndex& index, int role) const { if (!index.isValid()) { return QVariant(); } switch (role) { case Qt::DisplayRole: return d->displayData(index); default: return QVariant(); } } QVariant ImageMetaInfoModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Vertical || role != Qt::DisplayRole) { return QVariant(); } QString caption; if (section == 0) { caption = i18nc("@title:column", "Property"); } else if (section == 1) { caption = i18nc("@title:column", "Value"); } else { qWarning() << "Unknown section" << section; } return QVariant(caption); } } // namespace diff --git a/lib/memoryutils.cpp b/lib/memoryutils.cpp index df57f8ab..5066d5aa 100644 --- a/lib/memoryutils.cpp +++ b/lib/memoryutils.cpp @@ -1,171 +1,171 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2008 Aurélien Gâteau Copyright (C) 2004-2005 by Enrico Ros Copyright (C) 2004-2007 by Albert Astals Cid This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Cambridge, MA 02110-1301, USA. */ // Self #include "memoryutils.h" // Qt #include #include #include // System #ifdef Q_OS_WIN #define _WIN32_WINNT 0x0500 #include #elif defined(Q_OS_FREEBSD) #include #include #include #endif namespace Gwenview { namespace MemoryUtils { // This code has been copied from okular/core/document.cpp qulonglong getTotalMemory() { static qulonglong cachedValue = 0; if ( cachedValue ) return cachedValue; #if defined(Q_OS_LINUX) // if /proc/meminfo doesn't exist, return 128MB QFile memFile( QStringLiteral("/proc/meminfo") ); if ( !memFile.open( QIODevice::ReadOnly ) ) return (cachedValue = 134217728); QTextStream readStream( &memFile ); while ( true ) { QString entry = readStream.readLine(); if ( entry.isNull() ) break; - if ( entry.startsWith( QStringLiteral("MemTotal:") ) ) + if ( entry.startsWith(QLatin1String("MemTotal:") ) ) return (cachedValue = (Q_UINT64_C(1024) * entry.section( QLatin1Char(' '), -2, -2 ).toULongLong())); } #elif defined(Q_OS_FREEBSD) qulonglong physmem; int mib[] = {CTL_HW, HW_PHYSMEM}; size_t len = sizeof( physmem ); if ( sysctl( mib, 2, &physmem, &len, NULL, 0 ) == 0 ) return (cachedValue = physmem); #elif defined(Q_OS_WIN) MEMORYSTATUSEX stat; stat.dwLength = sizeof(stat); GlobalMemoryStatusEx (&stat); return ( cachedValue = stat.ullTotalPhys ); #endif return (cachedValue = 134217728); } qulonglong getFreeMemory() { static QTime lastUpdate = QTime::currentTime().addSecs(-3); static qulonglong cachedValue = 0; if ( qAbs( lastUpdate.secsTo( QTime::currentTime() ) ) <= 2 ) return cachedValue; #if defined(Q_OS_LINUX) // if /proc/meminfo doesn't exist, return MEMORY FULL QFile memFile( QStringLiteral("/proc/meminfo") ); if ( !memFile.open( QIODevice::ReadOnly ) ) return 0; // read /proc/meminfo and sum up the contents of 'MemFree', 'Buffers' // and 'Cached' fields. consider swapped memory as used memory. qulonglong memoryFree = 0; QString entry; QTextStream readStream( &memFile ); static const int nElems = 5; QString names[nElems] = { QStringLiteral("MemFree:"), QStringLiteral("Buffers:"), QStringLiteral("Cached:"), QStringLiteral("SwapFree:"), QStringLiteral("SwapTotal:") }; qulonglong values[nElems] = { 0, 0, 0, 0, 0 }; bool foundValues[nElems] = { false, false, false, false, false }; while ( true ) { entry = readStream.readLine(); if ( entry.isNull() ) break; for ( int i = 0; i < nElems; ++i ) { if ( entry.startsWith( names[i] ) ) { values[i] = entry.section( QLatin1Char(' '), -2, -2 ).toULongLong( &foundValues[i] ); } } } memFile.close(); bool found = true; for ( int i = 0; found && i < nElems; ++i ) found = found && foundValues[i]; if ( found ) { memoryFree = values[0] + values[1] + values[2] + values[3]; if ( values[4] > memoryFree ) memoryFree = 0; else memoryFree -= values[4]; } lastUpdate = QTime::currentTime(); return ( cachedValue = (Q_UINT64_C(1024) * memoryFree) ); #elif defined(Q_OS_FREEBSD) qulonglong cache, inact, free, psize; size_t cachelen, inactlen, freelen, psizelen; cachelen = sizeof( cache ); inactlen = sizeof( inact ); freelen = sizeof( free ); psizelen = sizeof( psize ); // sum up inactive, cached and free memory if ( sysctlbyname( "vm.stats.vm.v_cache_count", &cache, &cachelen, NULL, 0 ) == 0 && sysctlbyname( "vm.stats.vm.v_inactive_count", &inact, &inactlen, NULL, 0 ) == 0 && sysctlbyname( "vm.stats.vm.v_free_count", &free, &freelen, NULL, 0 ) == 0 && sysctlbyname( "vm.stats.vm.v_page_size", &psize, &psizelen, NULL, 0 ) == 0 ) { lastUpdate = QTime::currentTime(); return (cachedValue = (cache + inact + free) * psize); } else { return 0; } #elif defined(Q_OS_WIN) MEMORYSTATUSEX stat; stat.dwLength = sizeof(stat); GlobalMemoryStatusEx (&stat); lastUpdate = QTime::currentTime(); return ( cachedValue = stat.ullAvailPhys ); #else // tell the memory is full.. will act as in LOW profile return 0; #endif } } // MemoryUtils namespace } // Gwenview namespace